Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.7-rc5 207 lines 4.6 kB view raw
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 * @name: name to be associated with the iomem resource 39 * 40 * Caller allocates a block. If it is available, a pointer to a 41 * pre-initialized struct atmel_tc is returned. The caller can access 42 * the registers directly through the "regs" field. 43 */ 44struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) 45{ 46 struct atmel_tc *tc; 47 struct platform_device *pdev = NULL; 48 struct resource *r; 49 size_t size; 50 51 spin_lock(&tc_list_lock); 52 list_for_each_entry(tc, &tc_list, node) { 53 if (tc->pdev->dev.of_node) { 54 if (of_alias_get_id(tc->pdev->dev.of_node, "tcb") 55 == block) { 56 pdev = tc->pdev; 57 break; 58 } 59 } else if (tc->pdev->id == block) { 60 pdev = tc->pdev; 61 break; 62 } 63 } 64 65 if (!pdev || tc->iomem) 66 goto fail; 67 68 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 69 if (!r) 70 goto fail; 71 72 size = resource_size(r); 73 r = request_mem_region(r->start, size, name); 74 if (!r) 75 goto fail; 76 77 tc->regs = ioremap(r->start, size); 78 if (!tc->regs) 79 goto fail_ioremap; 80 81 tc->iomem = r; 82 83out: 84 spin_unlock(&tc_list_lock); 85 return tc; 86 87fail_ioremap: 88 release_mem_region(r->start, size); 89fail: 90 tc = NULL; 91 goto out; 92} 93EXPORT_SYMBOL_GPL(atmel_tc_alloc); 94 95/** 96 * atmel_tc_free - release a specified TC block 97 * @tc: Timer/counter block that was returned by atmel_tc_alloc() 98 * 99 * This reverses the effect of atmel_tc_alloc(), unmapping the I/O 100 * registers, invalidating the resource returned by that routine and 101 * making the TC available to other drivers. 102 */ 103void atmel_tc_free(struct atmel_tc *tc) 104{ 105 spin_lock(&tc_list_lock); 106 if (tc->regs) { 107 iounmap(tc->regs); 108 release_mem_region(tc->iomem->start, resource_size(tc->iomem)); 109 tc->regs = NULL; 110 tc->iomem = NULL; 111 } 112 spin_unlock(&tc_list_lock); 113} 114EXPORT_SYMBOL_GPL(atmel_tc_free); 115 116#if defined(CONFIG_OF) 117static struct atmel_tcb_config tcb_rm9200_config = { 118 .counter_width = 16, 119}; 120 121static struct atmel_tcb_config tcb_sam9x5_config = { 122 .counter_width = 32, 123}; 124 125static const struct of_device_id atmel_tcb_dt_ids[] = { 126 { 127 .compatible = "atmel,at91rm9200-tcb", 128 .data = &tcb_rm9200_config, 129 }, { 130 .compatible = "atmel,at91sam9x5-tcb", 131 .data = &tcb_sam9x5_config, 132 }, { 133 /* sentinel */ 134 } 135}; 136 137MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids); 138#endif 139 140static int __init tc_probe(struct platform_device *pdev) 141{ 142 struct atmel_tc *tc; 143 struct clk *clk; 144 int irq; 145 146 if (!platform_get_resource(pdev, IORESOURCE_MEM, 0)) 147 return -EINVAL; 148 149 irq = platform_get_irq(pdev, 0); 150 if (irq < 0) 151 return -EINVAL; 152 153 tc = kzalloc(sizeof(struct atmel_tc), GFP_KERNEL); 154 if (!tc) 155 return -ENOMEM; 156 157 tc->pdev = pdev; 158 159 clk = clk_get(&pdev->dev, "t0_clk"); 160 if (IS_ERR(clk)) { 161 kfree(tc); 162 return -EINVAL; 163 } 164 165 /* Now take SoC information if available */ 166 if (pdev->dev.of_node) { 167 const struct of_device_id *match; 168 match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); 169 if (match) 170 tc->tcb_config = match->data; 171 } 172 173 tc->clk[0] = clk; 174 tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); 175 if (IS_ERR(tc->clk[1])) 176 tc->clk[1] = clk; 177 tc->clk[2] = clk_get(&pdev->dev, "t2_clk"); 178 if (IS_ERR(tc->clk[2])) 179 tc->clk[2] = clk; 180 181 tc->irq[0] = irq; 182 tc->irq[1] = platform_get_irq(pdev, 1); 183 if (tc->irq[1] < 0) 184 tc->irq[1] = irq; 185 tc->irq[2] = platform_get_irq(pdev, 2); 186 if (tc->irq[2] < 0) 187 tc->irq[2] = irq; 188 189 spin_lock(&tc_list_lock); 190 list_add_tail(&tc->node, &tc_list); 191 spin_unlock(&tc_list_lock); 192 193 return 0; 194} 195 196static struct platform_driver tc_driver = { 197 .driver = { 198 .name = "atmel_tcb", 199 .of_match_table = of_match_ptr(atmel_tcb_dt_ids), 200 }, 201}; 202 203static int __init tc_init(void) 204{ 205 return platform_driver_probe(&tc_driver, tc_probe); 206} 207arch_initcall(tc_init);