1#include <linux/atmel_tc.h> 2#include <linux/clk.h> 3#include <linux/err.h> 4#include <linux/init.h> 5#include <linux/io.h> 6#include <linux/ioport.h> 7#include <linux/kernel.h> 8#include <linux/platform_device.h> 9#include <linux/module.h> 10#include <linux/slab.h> 11#include <linux/export.h> 12#include <linux/of.h> 13 14/* 15 * This is a thin library to solve the problem of how to portably allocate 16 * one of the TC blocks. For simplicity, it doesn't currently expect to 17 * share individual timers between different drivers. 18 */ 19 20#if defined(CONFIG_AVR32) 21/* AVR32 has these divide PBB */ 22const u8 atmel_tc_divisors[5] = { 0, 4, 8, 16, 32, }; 23EXPORT_SYMBOL(atmel_tc_divisors); 24 25#elif defined(CONFIG_ARCH_AT91) 26/* AT91 has these divide MCK */ 27const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, }; 28EXPORT_SYMBOL(atmel_tc_divisors); 29 30#endif 31 32static DEFINE_SPINLOCK(tc_list_lock); 33static LIST_HEAD(tc_list); 34 35/** 36 * atmel_tc_alloc - allocate a specified TC block 37 * @block: which block to allocate 38 * 39 * Caller allocates a block. If it is available, a pointer to a 40 * pre-initialized struct atmel_tc is returned. The caller can access 41 * the registers directly through the "regs" field. 42 */ 43struct atmel_tc *atmel_tc_alloc(unsigned block) 44{ 45 struct atmel_tc *tc; 46 struct platform_device *pdev = NULL; 47 48 spin_lock(&tc_list_lock); 49 list_for_each_entry(tc, &tc_list, node) { 50 if (tc->allocated) 51 continue; 52 53 if ((tc->pdev->dev.of_node && tc->id == block) || 54 (tc->pdev->id == block)) { 55 pdev = tc->pdev; 56 tc->allocated = true; 57 break; 58 } 59 } 60 spin_unlock(&tc_list_lock); 61 62 return pdev ? tc : NULL; 63} 64EXPORT_SYMBOL_GPL(atmel_tc_alloc); 65 66/** 67 * atmel_tc_free - release a specified TC block 68 * @tc: Timer/counter block that was returned by atmel_tc_alloc() 69 * 70 * This reverses the effect of atmel_tc_alloc(), invalidating the resource 71 * returned by that routine and making the TC available to other drivers. 72 */ 73void atmel_tc_free(struct atmel_tc *tc) 74{ 75 spin_lock(&tc_list_lock); 76 if (tc->allocated) 77 tc->allocated = false; 78 spin_unlock(&tc_list_lock); 79} 80EXPORT_SYMBOL_GPL(atmel_tc_free); 81 82#if defined(CONFIG_OF) 83static struct atmel_tcb_config tcb_rm9200_config = { 84 .counter_width = 16, 85}; 86 87static struct atmel_tcb_config tcb_sam9x5_config = { 88 .counter_width = 32, 89}; 90 91static const struct of_device_id atmel_tcb_dt_ids[] = { 92 { 93 .compatible = "atmel,at91rm9200-tcb", 94 .data = &tcb_rm9200_config, 95 }, { 96 .compatible = "atmel,at91sam9x5-tcb", 97 .data = &tcb_sam9x5_config, 98 }, { 99 /* sentinel */ 100 } 101}; 102 103MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids); 104#endif 105 106static int __init tc_probe(struct platform_device *pdev) 107{ 108 struct atmel_tc *tc; 109 struct clk *clk; 110 int irq; 111 struct resource *r; 112 unsigned int i; 113 114 irq = platform_get_irq(pdev, 0); 115 if (irq < 0) 116 return -EINVAL; 117 118 tc = devm_kzalloc(&pdev->dev, sizeof(struct atmel_tc), GFP_KERNEL); 119 if (!tc) 120 return -ENOMEM; 121 122 tc->pdev = pdev; 123 124 clk = devm_clk_get(&pdev->dev, "t0_clk"); 125 if (IS_ERR(clk)) 126 return PTR_ERR(clk); 127 128 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 129 tc->regs = devm_ioremap_resource(&pdev->dev, r); 130 if (IS_ERR(tc->regs)) 131 return PTR_ERR(tc->regs); 132 133 /* Now take SoC information if available */ 134 if (pdev->dev.of_node) { 135 const struct of_device_id *match; 136 match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); 137 if (match) 138 tc->tcb_config = match->data; 139 140 tc->id = of_alias_get_id(tc->pdev->dev.of_node, "tcb"); 141 } else { 142 tc->id = pdev->id; 143 } 144 145 tc->clk[0] = clk; 146 tc->clk[1] = devm_clk_get(&pdev->dev, "t1_clk"); 147 if (IS_ERR(tc->clk[1])) 148 tc->clk[1] = clk; 149 tc->clk[2] = devm_clk_get(&pdev->dev, "t2_clk"); 150 if (IS_ERR(tc->clk[2])) 151 tc->clk[2] = clk; 152 153 tc->irq[0] = irq; 154 tc->irq[1] = platform_get_irq(pdev, 1); 155 if (tc->irq[1] < 0) 156 tc->irq[1] = irq; 157 tc->irq[2] = platform_get_irq(pdev, 2); 158 if (tc->irq[2] < 0) 159 tc->irq[2] = irq; 160 161 for (i = 0; i < 3; i++) 162 writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); 163 164 spin_lock(&tc_list_lock); 165 list_add_tail(&tc->node, &tc_list); 166 spin_unlock(&tc_list_lock); 167 168 platform_set_drvdata(pdev, tc); 169 170 return 0; 171} 172 173static void tc_shutdown(struct platform_device *pdev) 174{ 175 int i; 176 struct atmel_tc *tc = platform_get_drvdata(pdev); 177 178 for (i = 0; i < 3; i++) 179 writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); 180} 181 182static struct platform_driver tc_driver = { 183 .driver = { 184 .name = "atmel_tcb", 185 .of_match_table = of_match_ptr(atmel_tcb_dt_ids), 186 }, 187 .shutdown = tc_shutdown, 188}; 189 190static int __init tc_init(void) 191{ 192 return platform_driver_probe(&tc_driver, tc_probe); 193} 194arch_initcall(tc_init); 195