diff options
author | Arnd Bergmann <arnd@arndb.de> | 2014-09-09 11:03:17 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2014-09-09 11:03:17 -0400 |
commit | c40c4028f048a077a8950fd95e5d8901f773a63f (patch) | |
tree | 4d1abd711def0811b3c2af8e3c89322235bb9e10 | |
parent | 32dc5ca0c10c859e0e4fcc457e7c0a0c2c4b15ae (diff) | |
parent | d07a1ecdfb96b26dd665b54fee22fc7417b1cb08 (diff) |
Merge tag 'at91-cleanup2' of git://github.com/at91linux/linux-at91 into next/cleanup
Pull "Second batch of AT91 cleanup for 3.18" from Nicolas Ferre:
- Timer Counter (TC) fixup and cleanup:
- fix segmentation fault when kexec-ing a kernel by masking
TC interrupts at shutdown and probe time
- use modern driver model: devm_*, probe function, sanitize IRQ request
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
* tag 'at91-cleanup2' of git://github.com/at91linux/linux-at91:
clocksource: tcb_clksrc: sanitize IRQ request
ARM: at91/tclib: mask interruptions at shutdown and probe
ARM: at91/tclib: move initialization from alloc to probe
ARM: at91/tclib: prefer using of devm_* functions
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 15 | ||||
-rw-r--r-- | drivers/misc/atmel_tclib.c | 101 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 2 | ||||
-rw-r--r-- | include/linux/atmel_tc.h | 13 |
4 files changed, 60 insertions, 71 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index a8d7ea14f183..8bdbc45c6dad 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c | |||
@@ -178,12 +178,6 @@ static irqreturn_t ch2_irq(int irq, void *handle) | |||
178 | return IRQ_NONE; | 178 | return IRQ_NONE; |
179 | } | 179 | } |
180 | 180 | ||
181 | static struct irqaction tc_irqaction = { | ||
182 | .name = "tc_clkevt", | ||
183 | .flags = IRQF_TIMER, | ||
184 | .handler = ch2_irq, | ||
185 | }; | ||
186 | |||
187 | static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) | 181 | static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) |
188 | { | 182 | { |
189 | int ret; | 183 | int ret; |
@@ -198,15 +192,16 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) | |||
198 | 192 | ||
199 | clkevt.regs = tc->regs; | 193 | clkevt.regs = tc->regs; |
200 | clkevt.clk = t2_clk; | 194 | clkevt.clk = t2_clk; |
201 | tc_irqaction.dev_id = &clkevt; | ||
202 | 195 | ||
203 | timer_clock = clk32k_divisor_idx; | 196 | timer_clock = clk32k_divisor_idx; |
204 | 197 | ||
205 | clkevt.clkevt.cpumask = cpumask_of(0); | 198 | clkevt.clkevt.cpumask = cpumask_of(0); |
206 | 199 | ||
207 | ret = setup_irq(irq, &tc_irqaction); | 200 | ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); |
208 | if (ret) | 201 | if (ret) { |
202 | clk_disable_unprepare(t2_clk); | ||
209 | return ret; | 203 | return ret; |
204 | } | ||
210 | 205 | ||
211 | clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); | 206 | clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); |
212 | 207 | ||
@@ -279,7 +274,7 @@ static int __init tcb_clksrc_init(void) | |||
279 | int i; | 274 | int i; |
280 | int ret; | 275 | int ret; |
281 | 276 | ||
282 | tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK, clksrc.name); | 277 | tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK); |
283 | if (!tc) { | 278 | if (!tc) { |
284 | pr_debug("can't alloc TC for clocksource\n"); | 279 | pr_debug("can't alloc TC for clocksource\n"); |
285 | return -ENODEV; | 280 | return -ENODEV; |
diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index c8d8e38d0d8a..0ca05c3ec8d6 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c | |||
@@ -35,60 +35,31 @@ static LIST_HEAD(tc_list); | |||
35 | /** | 35 | /** |
36 | * atmel_tc_alloc - allocate a specified TC block | 36 | * atmel_tc_alloc - allocate a specified TC block |
37 | * @block: which block to allocate | 37 | * @block: which block to allocate |
38 | * @name: name to be associated with the iomem resource | ||
39 | * | 38 | * |
40 | * Caller allocates a block. If it is available, a pointer to a | 39 | * Caller allocates a block. If it is available, a pointer to a |
41 | * pre-initialized struct atmel_tc is returned. The caller can access | 40 | * pre-initialized struct atmel_tc is returned. The caller can access |
42 | * the registers directly through the "regs" field. | 41 | * the registers directly through the "regs" field. |
43 | */ | 42 | */ |
44 | struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) | 43 | struct atmel_tc *atmel_tc_alloc(unsigned block) |
45 | { | 44 | { |
46 | struct atmel_tc *tc; | 45 | struct atmel_tc *tc; |
47 | struct platform_device *pdev = NULL; | 46 | struct platform_device *pdev = NULL; |
48 | struct resource *r; | ||
49 | size_t size; | ||
50 | 47 | ||
51 | spin_lock(&tc_list_lock); | 48 | spin_lock(&tc_list_lock); |
52 | list_for_each_entry(tc, &tc_list, node) { | 49 | list_for_each_entry(tc, &tc_list, node) { |
53 | if (tc->pdev->dev.of_node) { | 50 | if (tc->allocated) |
54 | if (of_alias_get_id(tc->pdev->dev.of_node, "tcb") | 51 | continue; |
55 | == block) { | 52 | |
56 | pdev = tc->pdev; | 53 | if ((tc->pdev->dev.of_node && tc->id == block) || |
57 | break; | 54 | (tc->pdev->id == block)) { |
58 | } | ||
59 | } else if (tc->pdev->id == block) { | ||
60 | pdev = tc->pdev; | 55 | pdev = tc->pdev; |
56 | tc->allocated = true; | ||
61 | break; | 57 | break; |
62 | } | 58 | } |
63 | } | 59 | } |
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 | |||
83 | out: | ||
84 | spin_unlock(&tc_list_lock); | 60 | spin_unlock(&tc_list_lock); |
85 | return tc; | ||
86 | 61 | ||
87 | fail_ioremap: | 62 | return pdev ? tc : NULL; |
88 | release_mem_region(r->start, size); | ||
89 | fail: | ||
90 | tc = NULL; | ||
91 | goto out; | ||
92 | } | 63 | } |
93 | EXPORT_SYMBOL_GPL(atmel_tc_alloc); | 64 | EXPORT_SYMBOL_GPL(atmel_tc_alloc); |
94 | 65 | ||
@@ -96,19 +67,14 @@ EXPORT_SYMBOL_GPL(atmel_tc_alloc); | |||
96 | * atmel_tc_free - release a specified TC block | 67 | * atmel_tc_free - release a specified TC block |
97 | * @tc: Timer/counter block that was returned by atmel_tc_alloc() | 68 | * @tc: Timer/counter block that was returned by atmel_tc_alloc() |
98 | * | 69 | * |
99 | * This reverses the effect of atmel_tc_alloc(), unmapping the I/O | 70 | * This reverses the effect of atmel_tc_alloc(), invalidating the resource |
100 | * registers, invalidating the resource returned by that routine and | 71 | * returned by that routine and making the TC available to other drivers. |
101 | * making the TC available to other drivers. | ||
102 | */ | 72 | */ |
103 | void atmel_tc_free(struct atmel_tc *tc) | 73 | void atmel_tc_free(struct atmel_tc *tc) |
104 | { | 74 | { |
105 | spin_lock(&tc_list_lock); | 75 | spin_lock(&tc_list_lock); |
106 | if (tc->regs) { | 76 | if (tc->allocated) |
107 | iounmap(tc->regs); | 77 | tc->allocated = false; |
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); | 78 | spin_unlock(&tc_list_lock); |
113 | } | 79 | } |
114 | EXPORT_SYMBOL_GPL(atmel_tc_free); | 80 | EXPORT_SYMBOL_GPL(atmel_tc_free); |
@@ -142,25 +108,27 @@ static int __init tc_probe(struct platform_device *pdev) | |||
142 | struct atmel_tc *tc; | 108 | struct atmel_tc *tc; |
143 | struct clk *clk; | 109 | struct clk *clk; |
144 | int irq; | 110 | int irq; |
145 | 111 | struct resource *r; | |
146 | if (!platform_get_resource(pdev, IORESOURCE_MEM, 0)) | 112 | unsigned int i; |
147 | return -EINVAL; | ||
148 | 113 | ||
149 | irq = platform_get_irq(pdev, 0); | 114 | irq = platform_get_irq(pdev, 0); |
150 | if (irq < 0) | 115 | if (irq < 0) |
151 | return -EINVAL; | 116 | return -EINVAL; |
152 | 117 | ||
153 | tc = kzalloc(sizeof(struct atmel_tc), GFP_KERNEL); | 118 | tc = devm_kzalloc(&pdev->dev, sizeof(struct atmel_tc), GFP_KERNEL); |
154 | if (!tc) | 119 | if (!tc) |
155 | return -ENOMEM; | 120 | return -ENOMEM; |
156 | 121 | ||
157 | tc->pdev = pdev; | 122 | tc->pdev = pdev; |
158 | 123 | ||
159 | clk = clk_get(&pdev->dev, "t0_clk"); | 124 | clk = devm_clk_get(&pdev->dev, "t0_clk"); |
160 | if (IS_ERR(clk)) { | 125 | if (IS_ERR(clk)) |
161 | kfree(tc); | 126 | return PTR_ERR(clk); |
162 | return -EINVAL; | 127 | |
163 | } | 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); | ||
164 | 132 | ||
165 | /* Now take SoC information if available */ | 133 | /* Now take SoC information if available */ |
166 | if (pdev->dev.of_node) { | 134 | if (pdev->dev.of_node) { |
@@ -168,13 +136,17 @@ static int __init tc_probe(struct platform_device *pdev) | |||
168 | match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); | 136 | match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); |
169 | if (match) | 137 | if (match) |
170 | tc->tcb_config = match->data; | 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; | ||
171 | } | 143 | } |
172 | 144 | ||
173 | tc->clk[0] = clk; | 145 | tc->clk[0] = clk; |
174 | tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); | 146 | tc->clk[1] = devm_clk_get(&pdev->dev, "t1_clk"); |
175 | if (IS_ERR(tc->clk[1])) | 147 | if (IS_ERR(tc->clk[1])) |
176 | tc->clk[1] = clk; | 148 | tc->clk[1] = clk; |
177 | tc->clk[2] = clk_get(&pdev->dev, "t2_clk"); | 149 | tc->clk[2] = devm_clk_get(&pdev->dev, "t2_clk"); |
178 | if (IS_ERR(tc->clk[2])) | 150 | if (IS_ERR(tc->clk[2])) |
179 | tc->clk[2] = clk; | 151 | tc->clk[2] = clk; |
180 | 152 | ||
@@ -186,18 +158,33 @@ static int __init tc_probe(struct platform_device *pdev) | |||
186 | if (tc->irq[2] < 0) | 158 | if (tc->irq[2] < 0) |
187 | tc->irq[2] = irq; | 159 | tc->irq[2] = irq; |
188 | 160 | ||
161 | for (i = 0; i < 3; i++) | ||
162 | writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); | ||
163 | |||
189 | spin_lock(&tc_list_lock); | 164 | spin_lock(&tc_list_lock); |
190 | list_add_tail(&tc->node, &tc_list); | 165 | list_add_tail(&tc->node, &tc_list); |
191 | spin_unlock(&tc_list_lock); | 166 | spin_unlock(&tc_list_lock); |
192 | 167 | ||
168 | platform_set_drvdata(pdev, tc); | ||
169 | |||
193 | return 0; | 170 | return 0; |
194 | } | 171 | } |
195 | 172 | ||
173 | static 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 | |||
196 | static struct platform_driver tc_driver = { | 182 | static struct platform_driver tc_driver = { |
197 | .driver = { | 183 | .driver = { |
198 | .name = "atmel_tcb", | 184 | .name = "atmel_tcb", |
199 | .of_match_table = of_match_ptr(atmel_tcb_dt_ids), | 185 | .of_match_table = of_match_ptr(atmel_tcb_dt_ids), |
200 | }, | 186 | }, |
187 | .shutdown = tc_shutdown, | ||
201 | }; | 188 | }; |
202 | 189 | ||
203 | static int __init tc_init(void) | 190 | static int __init tc_init(void) |
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index f3dcd02390f1..d56e5b717431 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c | |||
@@ -379,7 +379,7 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) | |||
379 | return err; | 379 | return err; |
380 | } | 380 | } |
381 | 381 | ||
382 | tc = atmel_tc_alloc(tcblock, "tcb-pwm"); | 382 | tc = atmel_tc_alloc(tcblock); |
383 | if (tc == NULL) { | 383 | if (tc == NULL) { |
384 | dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); | 384 | dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); |
385 | return -ENOMEM; | 385 | return -ENOMEM; |
diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h index 89a931babecf..b87c1c7c242a 100644 --- a/include/linux/atmel_tc.h +++ b/include/linux/atmel_tc.h | |||
@@ -44,12 +44,13 @@ struct atmel_tcb_config { | |||
44 | /** | 44 | /** |
45 | * struct atmel_tc - information about a Timer/Counter Block | 45 | * struct atmel_tc - information about a Timer/Counter Block |
46 | * @pdev: physical device | 46 | * @pdev: physical device |
47 | * @iomem: resource associated with the I/O register | ||
48 | * @regs: mapping through which the I/O registers can be accessed | 47 | * @regs: mapping through which the I/O registers can be accessed |
48 | * @id: block id | ||
49 | * @tcb_config: configuration data from SoC | 49 | * @tcb_config: configuration data from SoC |
50 | * @irq: irq for each of the three channels | 50 | * @irq: irq for each of the three channels |
51 | * @clk: internal clock source for each of the three channels | 51 | * @clk: internal clock source for each of the three channels |
52 | * @node: list node, for tclib internal use | 52 | * @node: list node, for tclib internal use |
53 | * @allocated: if already used, for tclib internal use | ||
53 | * | 54 | * |
54 | * On some platforms, each TC channel has its own clocks and IRQs, | 55 | * On some platforms, each TC channel has its own clocks and IRQs, |
55 | * while on others, all TC channels share the same clock and IRQ. | 56 | * while on others, all TC channels share the same clock and IRQ. |
@@ -61,15 +62,16 @@ struct atmel_tcb_config { | |||
61 | */ | 62 | */ |
62 | struct atmel_tc { | 63 | struct atmel_tc { |
63 | struct platform_device *pdev; | 64 | struct platform_device *pdev; |
64 | struct resource *iomem; | ||
65 | void __iomem *regs; | 65 | void __iomem *regs; |
66 | int id; | ||
66 | const struct atmel_tcb_config *tcb_config; | 67 | const struct atmel_tcb_config *tcb_config; |
67 | int irq[3]; | 68 | int irq[3]; |
68 | struct clk *clk[3]; | 69 | struct clk *clk[3]; |
69 | struct list_head node; | 70 | struct list_head node; |
71 | bool allocated; | ||
70 | }; | 72 | }; |
71 | 73 | ||
72 | extern struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name); | 74 | extern struct atmel_tc *atmel_tc_alloc(unsigned block); |
73 | extern void atmel_tc_free(struct atmel_tc *tc); | 75 | extern void atmel_tc_free(struct atmel_tc *tc); |
74 | 76 | ||
75 | /* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ | 77 | /* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */ |
@@ -258,5 +260,10 @@ extern const u8 atmel_tc_divisors[5]; | |||
258 | #define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ | 260 | #define ATMEL_TC_LDRAS (1 << 5) /* RA loading */ |
259 | #define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ | 261 | #define ATMEL_TC_LDRBS (1 << 6) /* RB loading */ |
260 | #define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ | 262 | #define ATMEL_TC_ETRGS (1 << 7) /* external trigger */ |
263 | #define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \ | ||
264 | ATMEL_TC_CPAS | ATMEL_TC_CPBS | \ | ||
265 | ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \ | ||
266 | ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \ | ||
267 | /* all IRQs */ | ||
261 | 268 | ||
262 | #endif | 269 | #endif |