aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2014-09-09 11:03:17 -0400
committerArnd Bergmann <arnd@arndb.de>2014-09-09 11:03:17 -0400
commitc40c4028f048a077a8950fd95e5d8901f773a63f (patch)
tree4d1abd711def0811b3c2af8e3c89322235bb9e10
parent32dc5ca0c10c859e0e4fcc457e7c0a0c2c4b15ae (diff)
parentd07a1ecdfb96b26dd665b54fee22fc7417b1cb08 (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.c15
-rw-r--r--drivers/misc/atmel_tclib.c101
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c2
-rw-r--r--include/linux/atmel_tc.h13
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
181static struct irqaction tc_irqaction = {
182 .name = "tc_clkevt",
183 .flags = IRQF_TIMER,
184 .handler = ch2_irq,
185};
186
187static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) 181static 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 */
44struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) 43struct 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
83out:
84 spin_unlock(&tc_list_lock); 60 spin_unlock(&tc_list_lock);
85 return tc;
86 61
87fail_ioremap: 62 return pdev ? tc : NULL;
88 release_mem_region(r->start, size);
89fail:
90 tc = NULL;
91 goto out;
92} 63}
93EXPORT_SYMBOL_GPL(atmel_tc_alloc); 64EXPORT_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 */
103void atmel_tc_free(struct atmel_tc *tc) 73void 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}
114EXPORT_SYMBOL_GPL(atmel_tc_free); 80EXPORT_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
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
196static struct platform_driver tc_driver = { 182static 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
203static int __init tc_init(void) 190static 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 */
62struct atmel_tc { 63struct 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
72extern struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name); 74extern struct atmel_tc *atmel_tc_alloc(unsigned block);
73extern void atmel_tc_free(struct atmel_tc *tc); 75extern 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