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 v5.0-rc7 198 lines 4.7 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 * 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 tc->slow_clk = devm_clk_get(&pdev->dev, "slow_clk"); 129 if (IS_ERR(tc->slow_clk)) 130 return PTR_ERR(tc->slow_clk); 131 132 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133 tc->regs = devm_ioremap_resource(&pdev->dev, r); 134 if (IS_ERR(tc->regs)) 135 return PTR_ERR(tc->regs); 136 137 /* Now take SoC information if available */ 138 if (pdev->dev.of_node) { 139 const struct of_device_id *match; 140 match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); 141 if (match) 142 tc->tcb_config = match->data; 143 144 tc->id = of_alias_get_id(tc->pdev->dev.of_node, "tcb"); 145 } else { 146 tc->id = pdev->id; 147 } 148 149 tc->clk[0] = clk; 150 tc->clk[1] = devm_clk_get(&pdev->dev, "t1_clk"); 151 if (IS_ERR(tc->clk[1])) 152 tc->clk[1] = clk; 153 tc->clk[2] = devm_clk_get(&pdev->dev, "t2_clk"); 154 if (IS_ERR(tc->clk[2])) 155 tc->clk[2] = clk; 156 157 tc->irq[0] = irq; 158 tc->irq[1] = platform_get_irq(pdev, 1); 159 if (tc->irq[1] < 0) 160 tc->irq[1] = irq; 161 tc->irq[2] = platform_get_irq(pdev, 2); 162 if (tc->irq[2] < 0) 163 tc->irq[2] = irq; 164 165 for (i = 0; i < 3; i++) 166 writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); 167 168 spin_lock(&tc_list_lock); 169 list_add_tail(&tc->node, &tc_list); 170 spin_unlock(&tc_list_lock); 171 172 platform_set_drvdata(pdev, tc); 173 174 return 0; 175} 176 177static void tc_shutdown(struct platform_device *pdev) 178{ 179 int i; 180 struct atmel_tc *tc = platform_get_drvdata(pdev); 181 182 for (i = 0; i < 3; i++) 183 writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); 184} 185 186static struct platform_driver tc_driver = { 187 .driver = { 188 .name = "atmel_tcb", 189 .of_match_table = of_match_ptr(atmel_tcb_dt_ids), 190 }, 191 .shutdown = tc_shutdown, 192}; 193 194static int __init tc_init(void) 195{ 196 return platform_driver_probe(&tc_driver, tc_probe); 197} 198arch_initcall(tc_init);