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

atmel_tc library

Create <linux/atmel_tc.h> based on <asm-arm/arch-at91/at91-tc.h> and the
at91sam9263 and at32ap7000 datasheets. Most AT91 and AT32 SOCs have one
or two of these TC blocks, which include three 16-bit timers that can be
interconnected in various ways.

These TC blocks can be used for external interfacing (such as PWM and
measurement), or used as somewhat quirky sixteen-bit timers.

Changes relative to the original version:
* Drop unneeded inclusion of <linux/mutex.h>
* Support an arbitrary number of TC blocks
* Return a struct with information about a TC block from
atmel_tc_alloc() instead of using a combination of return values
and "out" parameters.
* ioremap() the I/O registers on allocation
* Look up clocks and irqs for all channels
* Add "name" parameter to atmel_tc_alloc() and use this when
requesting the iomem resource.
* Check if the platform provided the necessary resources at probe()
time instead of when the TCB is allocated.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>

authored by

David Brownell and committed by
Haavard Skinnemoen
2a341f5c 976dde01

+422
+8
drivers/misc/Kconfig
··· 22 22 purposes including software controlled power-efficent backlights 23 23 on LCD displays, motor control, and waveform generation. 24 24 25 + config ATMEL_TCLIB 26 + bool "Atmel AT32/AT91 Timer/Counter Library" 27 + depends on (AVR32 || ARCH_AT91) 28 + help 29 + Select this if you want a library to allocate the Timer/Counter 30 + blocks found on many Atmel processors. This facilitates using 31 + these blocks by different drivers despite processor differences. 32 + 25 33 config IBM_ASM 26 34 tristate "Device driver for IBM RSA service processor" 27 35 depends on X86 && PCI && INPUT && EXPERIMENTAL
+1
drivers/misc/Makefile
··· 10 10 obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o 11 11 obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o 12 12 obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 13 + obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o 13 14 obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 14 15 obj-$(CONFIG_LKDTM) += lkdtm.o 15 16 obj-$(CONFIG_TIFM_CORE) += tifm_core.o
+161
drivers/misc/atmel_tclib.c
··· 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 + 10 + /* Number of bytes to reserve for the iomem resource */ 11 + #define ATMEL_TC_IOMEM_SIZE 256 12 + 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 */ 22 + const u8 atmel_tc_divisors[5] = { 0, 4, 8, 16, 32, }; 23 + EXPORT_SYMBOL(atmel_tc_divisors); 24 + 25 + #elif defined(CONFIG_ARCH_AT91) 26 + /* AT91 has these divide MCK */ 27 + const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, }; 28 + EXPORT_SYMBOL(atmel_tc_divisors); 29 + 30 + #endif 31 + 32 + static DEFINE_SPINLOCK(tc_list_lock); 33 + static 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 + */ 44 + struct 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 + 50 + spin_lock(&tc_list_lock); 51 + list_for_each_entry(tc, &tc_list, node) { 52 + if (tc->pdev->id == block) { 53 + pdev = tc->pdev; 54 + break; 55 + } 56 + } 57 + 58 + if (!pdev || tc->iomem) 59 + goto fail; 60 + 61 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 62 + r = request_mem_region(r->start, ATMEL_TC_IOMEM_SIZE, name); 63 + if (!r) 64 + goto fail; 65 + 66 + tc->regs = ioremap(r->start, ATMEL_TC_IOMEM_SIZE); 67 + if (!tc->regs) 68 + goto fail_ioremap; 69 + 70 + tc->iomem = r; 71 + 72 + out: 73 + spin_unlock(&tc_list_lock); 74 + return tc; 75 + 76 + fail_ioremap: 77 + release_resource(r); 78 + fail: 79 + tc = NULL; 80 + goto out; 81 + } 82 + EXPORT_SYMBOL_GPL(atmel_tc_alloc); 83 + 84 + /** 85 + * atmel_tc_free - release a specified TC block 86 + * @tc: Timer/counter block that was returned by atmel_tc_alloc() 87 + * 88 + * This reverses the effect of atmel_tc_alloc(), unmapping the I/O 89 + * registers, invalidating the resource returned by that routine and 90 + * making the TC available to other drivers. 91 + */ 92 + void atmel_tc_free(struct atmel_tc *tc) 93 + { 94 + spin_lock(&tc_list_lock); 95 + if (tc->regs) { 96 + iounmap(tc->regs); 97 + release_resource(tc->iomem); 98 + tc->regs = NULL; 99 + tc->iomem = NULL; 100 + } 101 + spin_unlock(&tc_list_lock); 102 + } 103 + EXPORT_SYMBOL_GPL(atmel_tc_free); 104 + 105 + static int __init tc_probe(struct platform_device *pdev) 106 + { 107 + struct atmel_tc *tc; 108 + struct clk *clk; 109 + int irq; 110 + 111 + if (!platform_get_resource(pdev, IORESOURCE_MEM, 0)) 112 + return -EINVAL; 113 + 114 + irq = platform_get_irq(pdev, 0); 115 + if (irq < 0) 116 + return -EINVAL; 117 + 118 + tc = kzalloc(sizeof(struct atmel_tc), GFP_KERNEL); 119 + if (!tc) 120 + return -ENOMEM; 121 + 122 + tc->pdev = pdev; 123 + 124 + clk = clk_get(&pdev->dev, "t0_clk"); 125 + if (IS_ERR(clk)) { 126 + kfree(tc); 127 + return -EINVAL; 128 + } 129 + 130 + tc->clk[0] = clk; 131 + tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); 132 + if (IS_ERR(tc->clk[1])) 133 + tc->clk[1] = clk; 134 + tc->clk[2] = clk_get(&pdev->dev, "t2_clk"); 135 + if (IS_ERR(tc->clk[2])) 136 + tc->clk[2] = clk; 137 + 138 + tc->irq[0] = irq; 139 + tc->irq[1] = platform_get_irq(pdev, 1); 140 + if (tc->irq[1] < 0) 141 + tc->irq[1] = irq; 142 + tc->irq[2] = platform_get_irq(pdev, 2); 143 + if (tc->irq[2] < 0) 144 + tc->irq[2] = irq; 145 + 146 + spin_lock(&tc_list_lock); 147 + list_add_tail(&tc->node, &tc_list); 148 + spin_unlock(&tc_list_lock); 149 + 150 + return 0; 151 + } 152 + 153 + static struct platform_driver tc_driver = { 154 + .driver.name = "atmel_tcb", 155 + }; 156 + 157 + static int __init tc_init(void) 158 + { 159 + return platform_driver_probe(&tc_driver, tc_probe); 160 + } 161 + arch_initcall(tc_init);
+252
include/linux/atmel_tc.h
··· 1 + /* 2 + * Timer/Counter Unit (TC) registers. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #ifndef ATMEL_TC_H 11 + #define ATMEL_TC_H 12 + 13 + #include <linux/compiler.h> 14 + #include <linux/list.h> 15 + 16 + /* 17 + * Many 32-bit Atmel SOCs include one or more TC blocks, each of which holds 18 + * three general-purpose 16-bit timers. These timers share one register bank. 19 + * Depending on the SOC, each timer may have its own clock and IRQ, or those 20 + * may be shared by the whole TC block. 21 + * 22 + * These TC blocks may have up to nine external pins: TCLK0..2 signals for 23 + * clocks or clock gates, and per-timer TIOA and TIOB signals used for PWM 24 + * or triggering. Those pins need to be set up for use with the TC block, 25 + * else they will be used as GPIOs or for a different controller. 26 + * 27 + * Although we expect each TC block to have a platform_device node, those 28 + * nodes are not what drivers bind to. Instead, they ask for a specific 29 + * TC block, by number ... which is a common approach on systems with many 30 + * timers. Then they use clk_get() and platform_get_irq() to get clock and 31 + * IRQ resources. 32 + */ 33 + 34 + struct clk; 35 + 36 + /** 37 + * struct atmel_tc - information about a Timer/Counter Block 38 + * @pdev: physical device 39 + * @iomem: resource associated with the I/O register 40 + * @regs: mapping through which the I/O registers can be accessed 41 + * @irq: irq for each of the three channels 42 + * @clk: internal clock source for each of the three channels 43 + * @node: list node, for tclib internal use 44 + * 45 + * On some platforms, each TC channel has its own clocks and IRQs, 46 + * while on others, all TC channels share the same clock and IRQ. 47 + * Drivers should clk_enable() all the clocks they need even though 48 + * all the entries in @clk may point to the same physical clock. 49 + * Likewise, drivers should request irqs independently for each 50 + * channel, but they must use IRQF_SHARED in case some of the entries 51 + * in @irq are actually the same IRQ. 52 + */ 53 + struct atmel_tc { 54 + struct platform_device *pdev; 55 + struct resource *iomem; 56 + void __iomem *regs; 57 + int irq[3]; 58 + struct clk *clk[3]; 59 + struct list_head node; 60 + }; 61 + 62 + extern struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name); 63 + extern void atmel_tc_free(struct atmel_tc *tc); 64 + 65 + /* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ 66 + extern const u8 atmel_tc_divisors[5]; 67 + 68 + 69 + /* 70 + * Two registers have block-wide controls. These are: configuring the three 71 + * "external" clocks (or event sources) used by the timer channels; and 72 + * synchronizing the timers by resetting them all at once. 73 + * 74 + * "External" can mean "external to chip" using the TCLK0, TCLK1, or TCLK2 75 + * signals. Or, it can mean "external to timer", using the TIOA output from 76 + * one of the other two timers that's being run in waveform mode. 77 + */ 78 + 79 + #define ATMEL_TC_BCR 0xc0 /* TC Block Control Register */ 80 + #define ATMEL_TC_SYNC (1 << 0) /* synchronize timers */ 81 + 82 + #define ATMEL_TC_BMR 0xc4 /* TC Block Mode Register */ 83 + #define ATMEL_TC_TC0XC0S (3 << 0) /* external clock 0 source */ 84 + #define ATMEL_TC_TC0XC0S_TCLK0 (0 << 0) 85 + #define ATMEL_TC_TC0XC0S_NONE (1 << 0) 86 + #define ATMEL_TC_TC0XC0S_TIOA1 (2 << 0) 87 + #define ATMEL_TC_TC0XC0S_TIOA2 (3 << 0) 88 + #define ATMEL_TC_TC1XC1S (3 << 2) /* external clock 1 source */ 89 + #define ATMEL_TC_TC1XC1S_TCLK1 (0 << 2) 90 + #define ATMEL_TC_TC1XC1S_NONE (1 << 2) 91 + #define ATMEL_TC_TC1XC1S_TIOA0 (2 << 2) 92 + #define ATMEL_TC_TC1XC1S_TIOA2 (3 << 2) 93 + #define ATMEL_TC_TC2XC2S (3 << 4) /* external clock 2 source */ 94 + #define ATMEL_TC_TC2XC2S_TCLK2 (0 << 4) 95 + #define ATMEL_TC_TC2XC2S_NONE (1 << 4) 96 + #define ATMEL_TC_TC2XC2S_TIOA0 (2 << 4) 97 + #define ATMEL_TC_TC2XC2S_TIOA1 (3 << 4) 98 + 99 + 100 + /* 101 + * Each TC block has three "channels", each with one counter and controls. 102 + * 103 + * Note that the semantics of ATMEL_TC_TIMER_CLOCKx (input clock selection 104 + * when it's not "external") is silicon-specific. AT91 platforms use one 105 + * set of definitions; AVR32 platforms use a different set. Don't hard-wire 106 + * such knowledge into your code, use the global "atmel_tc_divisors" ... 107 + * where index N is the divisor for clock N+1, else zero to indicate it uses 108 + * the 32 KiHz clock. 109 + * 110 + * The timers can be chained in various ways, and operated in "waveform" 111 + * generation mode (including PWM) or "capture" mode (to time events). In 112 + * both modes, behavior can be configured in many ways. 113 + * 114 + * Each timer has two I/O pins, TIOA and TIOB. Waveform mode uses TIOA as a 115 + * PWM output, and TIOB as either another PWM or as a trigger. Capture mode 116 + * uses them only as inputs. 117 + */ 118 + #define ATMEL_TC_CHAN(idx) ((idx)*0x40) 119 + #define ATMEL_TC_REG(idx, reg) (ATMEL_TC_CHAN(idx) + ATMEL_TC_ ## reg) 120 + 121 + #define ATMEL_TC_CCR 0x00 /* Channel Control Register */ 122 + #define ATMEL_TC_CLKEN (1 << 0) /* clock enable */ 123 + #define ATMEL_TC_CLKDIS (1 << 1) /* clock disable */ 124 + #define ATMEL_TC_SWTRG (1 << 2) /* software trigger */ 125 + 126 + #define ATMEL_TC_CMR 0x04 /* Channel Mode Register */ 127 + 128 + /* Both modes share some CMR bits */ 129 + #define ATMEL_TC_TCCLKS (7 << 0) /* clock source */ 130 + #define ATMEL_TC_TIMER_CLOCK1 (0 << 0) 131 + #define ATMEL_TC_TIMER_CLOCK2 (1 << 0) 132 + #define ATMEL_TC_TIMER_CLOCK3 (2 << 0) 133 + #define ATMEL_TC_TIMER_CLOCK4 (3 << 0) 134 + #define ATMEL_TC_TIMER_CLOCK5 (4 << 0) 135 + #define ATMEL_TC_XC0 (5 << 0) 136 + #define ATMEL_TC_XC1 (6 << 0) 137 + #define ATMEL_TC_XC2 (7 << 0) 138 + #define ATMEL_TC_CLKI (1 << 3) /* clock invert */ 139 + #define ATMEL_TC_BURST (3 << 4) /* clock gating */ 140 + #define ATMEL_TC_GATE_NONE (0 << 4) 141 + #define ATMEL_TC_GATE_XC0 (1 << 4) 142 + #define ATMEL_TC_GATE_XC1 (2 << 4) 143 + #define ATMEL_TC_GATE_XC2 (3 << 4) 144 + #define ATMEL_TC_WAVE (1 << 15) /* true = Waveform mode */ 145 + 146 + /* CAPTURE mode CMR bits */ 147 + #define ATMEL_TC_LDBSTOP (1 << 6) /* counter stops on RB load */ 148 + #define ATMEL_TC_LDBDIS (1 << 7) /* counter disable on RB load */ 149 + #define ATMEL_TC_ETRGEDG (3 << 8) /* external trigger edge */ 150 + #define ATMEL_TC_ETRGEDG_NONE (0 << 8) 151 + #define ATMEL_TC_ETRGEDG_RISING (1 << 8) 152 + #define ATMEL_TC_ETRGEDG_FALLING (2 << 8) 153 + #define ATMEL_TC_ETRGEDG_BOTH (3 << 8) 154 + #define ATMEL_TC_ABETRG (1 << 10) /* external trigger is TIOA? */ 155 + #define ATMEL_TC_CPCTRG (1 << 14) /* RC compare trigger enable */ 156 + #define ATMEL_TC_LDRA (3 << 16) /* RA loading edge (of TIOA) */ 157 + #define ATMEL_TC_LDRA_NONE (0 << 16) 158 + #define ATMEL_TC_LDRA_RISING (1 << 16) 159 + #define ATMEL_TC_LDRA_FALLING (2 << 16) 160 + #define ATMEL_TC_LDRA_BOTH (3 << 16) 161 + #define ATMEL_TC_LDRB (3 << 18) /* RB loading edge (of TIOA) */ 162 + #define ATMEL_TC_LDRB_NONE (0 << 18) 163 + #define ATMEL_TC_LDRB_RISING (1 << 18) 164 + #define ATMEL_TC_LDRB_FALLING (2 << 18) 165 + #define ATMEL_TC_LDRB_BOTH (3 << 18) 166 + 167 + /* WAVEFORM mode CMR bits */ 168 + #define ATMEL_TC_CPCSTOP (1 << 6) /* RC compare stops counter */ 169 + #define ATMEL_TC_CPCDIS (1 << 7) /* RC compare disables counter */ 170 + #define ATMEL_TC_EEVTEDG (3 << 8) /* external event edge */ 171 + #define ATMEL_TC_EEVTEDG_NONE (0 << 8) 172 + #define ATMEL_TC_EEVTEDG_RISING (1 << 8) 173 + #define ATMEL_TC_EEVTEDG_FALLING (2 << 8) 174 + #define ATMEL_TC_EEVTEDG_BOTH (3 << 8) 175 + #define ATMEL_TC_EEVT (3 << 10) /* external event source */ 176 + #define ATMEL_TC_EEVT_TIOB (0 << 10) 177 + #define ATMEL_TC_EEVT_XC0 (1 << 10) 178 + #define ATMEL_TC_EEVT_XC1 (2 << 10) 179 + #define ATMEL_TC_EEVT_XC2 (3 << 10) 180 + #define ATMEL_TC_ENETRG (1 << 12) /* external event is trigger */ 181 + #define ATMEL_TC_WAVESEL (3 << 13) /* waveform type */ 182 + #define ATMEL_TC_WAVESEL_UP (0 << 13) 183 + #define ATMEL_TC_WAVESEL_UPDOWN (1 << 13) 184 + #define ATMEL_TC_WAVESEL_UP_AUTO (2 << 13) 185 + #define ATMEL_TC_WAVESEL_UPDOWN_AUTO (3 << 13) 186 + #define ATMEL_TC_ACPA (3 << 16) /* RA compare changes TIOA */ 187 + #define ATMEL_TC_ACPA_NONE (0 << 16) 188 + #define ATMEL_TC_ACPA_SET (1 << 16) 189 + #define ATMEL_TC_ACPA_CLEAR (2 << 16) 190 + #define ATMEL_TC_ACPA_TOGGLE (3 << 16) 191 + #define ATMEL_TC_ACPC (3 << 18) /* RC compare changes TIOA */ 192 + #define ATMEL_TC_ACPC_NONE (0 << 18) 193 + #define ATMEL_TC_ACPC_SET (1 << 18) 194 + #define ATMEL_TC_ACPC_CLEAR (2 << 18) 195 + #define ATMEL_TC_ACPC_TOGGLE (3 << 18) 196 + #define ATMEL_TC_AEEVT (3 << 20) /* external event changes TIOA */ 197 + #define ATMEL_TC_AEEVT_NONE (0 << 20) 198 + #define ATMEL_TC_AEEVT_SET (1 << 20) 199 + #define ATMEL_TC_AEEVT_CLEAR (2 << 20) 200 + #define ATMEL_TC_AEEVT_TOGGLE (3 << 20) 201 + #define ATMEL_TC_ASWTRG (3 << 22) /* software trigger changes TIOA */ 202 + #define ATMEL_TC_ASWTRG_NONE (0 << 22) 203 + #define ATMEL_TC_ASWTRG_SET (1 << 22) 204 + #define ATMEL_TC_ASWTRG_CLEAR (2 << 22) 205 + #define ATMEL_TC_ASWTRG_TOGGLE (3 << 22) 206 + #define ATMEL_TC_BCPB (3 << 24) /* RB compare changes TIOB */ 207 + #define ATMEL_TC_BCPB_NONE (0 << 24) 208 + #define ATMEL_TC_BCPB_SET (1 << 24) 209 + #define ATMEL_TC_BCPB_CLEAR (2 << 24) 210 + #define ATMEL_TC_BCPB_TOGGLE (3 << 24) 211 + #define ATMEL_TC_BCPC (3 << 26) /* RC compare changes TIOB */ 212 + #define ATMEL_TC_BCPC_NONE (0 << 26) 213 + #define ATMEL_TC_BCPC_SET (1 << 26) 214 + #define ATMEL_TC_BCPC_CLEAR (2 << 26) 215 + #define ATMEL_TC_BCPC_TOGGLE (3 << 26) 216 + #define ATMEL_TC_BEEVT (3 << 28) /* external event changes TIOB */ 217 + #define ATMEL_TC_BEEVT_NONE (0 << 28) 218 + #define ATMEL_TC_BEEVT_SET (1 << 28) 219 + #define ATMEL_TC_BEEVT_CLEAR (2 << 28) 220 + #define ATMEL_TC_BEEVT_TOGGLE (3 << 28) 221 + #define ATMEL_TC_BSWTRG (3 << 30) /* software trigger changes TIOB */ 222 + #define ATMEL_TC_BSWTRG_NONE (0 << 30) 223 + #define ATMEL_TC_BSWTRG_SET (1 << 30) 224 + #define ATMEL_TC_BSWTRG_CLEAR (2 << 30) 225 + #define ATMEL_TC_BSWTRG_TOGGLE (3 << 30) 226 + 227 + #define ATMEL_TC_CV 0x10 /* counter Value */ 228 + #define ATMEL_TC_RA 0x14 /* register A */ 229 + #define ATMEL_TC_RB 0x18 /* register B */ 230 + #define ATMEL_TC_RC 0x1c /* register C */ 231 + 232 + #define ATMEL_TC_SR 0x20 /* status (read-only) */ 233 + /* Status-only flags */ 234 + #define ATMEL_TC_CLKSTA (1 << 16) /* clock enabled */ 235 + #define ATMEL_TC_MTIOA (1 << 17) /* TIOA mirror */ 236 + #define ATMEL_TC_MTIOB (1 << 18) /* TIOB mirror */ 237 + 238 + #define ATMEL_TC_IER 0x24 /* interrupt enable (write-only) */ 239 + #define ATMEL_TC_IDR 0x28 /* interrupt disable (write-only) */ 240 + #define ATMEL_TC_IMR 0x2c /* interrupt mask (read-only) */ 241 + 242 + /* Status and IRQ flags */ 243 + #define ATMEL_TC_COVFS (1 << 0) /* counter overflow */ 244 + #define ATMEL_TC_LOVRS (1 << 1) /* load overrun */ 245 + #define ATMEL_TC_CPAS (1 << 2) /* RA compare */ 246 + #define ATMEL_TC_CPBS (1 << 3) /* RB compare */ 247 + #define ATMEL_TC_CPCS (1 << 4) /* RC compare */ 248 + #define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ 249 + #define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ 250 + #define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ 251 + 252 + #endif