aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-01-20 18:09:42 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-02-03 12:38:07 -0500
commit45c7f75fd4b57035cd35954986a2faefb07dac9d (patch)
tree1cab7610cd45d2b38334f62c49406a1b74ecdabe /drivers
parentc4592ce4e8f69d78711d53f0a42ec1b4dbf00cde (diff)
MFD: mcp-sa11x0: convert mcp-sa11x0 to use platform resources
Patch taken from af9081ae64 (ARM: sa1100: Refactor mcp-sa11x0 to use platform resources.) by Jochen Friedrich <jochen@scram.de>, and fixes applied. We can safely do this now that we have sanitized host removal; the original patch had use-after-free bugs in the removal code. Not only that, but there was no checking of the ioremap() return. The final change over Jochen's patch is that we wrap the base pointer selection inside the various register indexes, which reduces the possibility of the wrong register index being used. Acked-by: Jochen Friedrich <jochen@scram.de> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/mcp-sa11x0.c141
1 files changed, 99 insertions, 42 deletions
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
index 703e76a77005..4d06db72bb9c 100644
--- a/drivers/mfd/mcp-sa11x0.c
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -13,6 +13,7 @@
13 */ 13 */
14#include <linux/module.h> 14#include <linux/module.h>
15#include <linux/init.h> 15#include <linux/init.h>
16#include <linux/io.h>
16#include <linux/errno.h> 17#include <linux/errno.h>
17#include <linux/kernel.h> 18#include <linux/kernel.h>
18#include <linux/delay.h> 19#include <linux/delay.h>
@@ -30,34 +31,44 @@
30#define DRIVER_NAME "sa11x0-mcp" 31#define DRIVER_NAME "sa11x0-mcp"
31 32
32struct mcp_sa11x0 { 33struct mcp_sa11x0 {
33 u32 mccr0; 34 void __iomem *base0;
34 u32 mccr1; 35 void __iomem *base1;
36 u32 mccr0;
37 u32 mccr1;
35}; 38};
36 39
40/* Register offsets */
41#define MCCR0(m) ((m)->base0 + 0x00)
42#define MCDR0(m) ((m)->base0 + 0x08)
43#define MCDR1(m) ((m)->base0 + 0x0c)
44#define MCDR2(m) ((m)->base0 + 0x10)
45#define MCSR(m) ((m)->base0 + 0x18)
46#define MCCR1(m) ((m)->base1 + 0x00)
47
37#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) 48#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))
38 49
39static void 50static void
40mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) 51mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
41{ 52{
42 unsigned int mccr0; 53 struct mcp_sa11x0 *m = priv(mcp);
43 54
44 divisor /= 32; 55 divisor /= 32;
45 56
46 mccr0 = Ser4MCCR0 & ~0x00007f00; 57 m->mccr0 &= ~0x00007f00;
47 mccr0 |= divisor << 8; 58 m->mccr0 |= divisor << 8;
48 Ser4MCCR0 = mccr0; 59 writel_relaxed(m->mccr0, MCCR0(m));
49} 60}
50 61
51static void 62static void
52mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) 63mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
53{ 64{
54 unsigned int mccr0; 65 struct mcp_sa11x0 *m = priv(mcp);
55 66
56 divisor /= 32; 67 divisor /= 32;
57 68
58 mccr0 = Ser4MCCR0 & ~0x0000007f; 69 m->mccr0 &= ~0x0000007f;
59 mccr0 |= divisor; 70 m->mccr0 |= divisor;
60 Ser4MCCR0 = mccr0; 71 writel_relaxed(m->mccr0, MCCR0(m));
61} 72}
62 73
63/* 74/*
@@ -69,14 +80,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
69static void 80static void
70mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) 81mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
71{ 82{
83 struct mcp_sa11x0 *m = priv(mcp);
72 int ret = -ETIME; 84 int ret = -ETIME;
73 int i; 85 int i;
74 86
75 Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); 87 writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m));
76 88
77 for (i = 0; i < 2; i++) { 89 for (i = 0; i < 2; i++) {
78 udelay(mcp->rw_timeout); 90 udelay(mcp->rw_timeout);
79 if (Ser4MCSR & MCSR_CWC) { 91 if (readl_relaxed(MCSR(m)) & MCSR_CWC) {
80 ret = 0; 92 ret = 0;
81 break; 93 break;
82 } 94 }
@@ -95,15 +107,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
95static unsigned int 107static unsigned int
96mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) 108mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
97{ 109{
110 struct mcp_sa11x0 *m = priv(mcp);
98 int ret = -ETIME; 111 int ret = -ETIME;
99 int i; 112 int i;
100 113
101 Ser4MCDR2 = reg << 17 | MCDR2_Rd; 114 writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m));
102 115
103 for (i = 0; i < 2; i++) { 116 for (i = 0; i < 2; i++) {
104 udelay(mcp->rw_timeout); 117 udelay(mcp->rw_timeout);
105 if (Ser4MCSR & MCSR_CRC) { 118 if (readl_relaxed(MCSR(m)) & MCSR_CRC) {
106 ret = Ser4MCDR2 & 0xffff; 119 ret = readl_relaxed(MCDR2(m)) & 0xffff;
107 break; 120 break;
108 } 121 }
109 } 122 }
@@ -116,13 +129,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
116 129
117static void mcp_sa11x0_enable(struct mcp *mcp) 130static void mcp_sa11x0_enable(struct mcp *mcp)
118{ 131{
119 Ser4MCSR = -1; 132 struct mcp_sa11x0 *m = priv(mcp);
120 Ser4MCCR0 |= MCCR0_MCE; 133
134 writel(-1, MCSR(m));
135 m->mccr0 |= MCCR0_MCE;
136 writel_relaxed(m->mccr0, MCCR0(m));
121} 137}
122 138
123static void mcp_sa11x0_disable(struct mcp *mcp) 139static void mcp_sa11x0_disable(struct mcp *mcp)
124{ 140{
125 Ser4MCCR0 &= ~MCCR0_MCE; 141 struct mcp_sa11x0 *m = priv(mcp);
142
143 m->mccr0 &= ~MCCR0_MCE;
144 writel_relaxed(m->mccr0, MCCR0(m));
126} 145}
127 146
128/* 147/*
@@ -137,22 +156,38 @@ static struct mcp_ops mcp_sa11x0 = {
137 .disable = mcp_sa11x0_disable, 156 .disable = mcp_sa11x0_disable,
138}; 157};
139 158
140static int mcp_sa11x0_probe(struct platform_device *pdev) 159static int mcp_sa11x0_probe(struct platform_device *dev)
141{ 160{
142 struct mcp_plat_data *data = pdev->dev.platform_data; 161 struct mcp_plat_data *data = dev->dev.platform_data;
162 struct resource *mem0, *mem1;
163 struct mcp_sa11x0 *m;
143 struct mcp *mcp; 164 struct mcp *mcp;
144 int ret; 165 int ret;
145 166
146 if (!data) 167 if (!data)
147 return -ENODEV; 168 return -ENODEV;
148 169
149 if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) 170 mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
150 return -EBUSY; 171 mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
172 if (!mem0 || !mem1)
173 return -ENXIO;
174
175 if (!request_mem_region(mem0->start, resource_size(mem0),
176 DRIVER_NAME)) {
177 ret = -EBUSY;
178 goto err_mem0;
179 }
151 180
152 mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); 181 if (!request_mem_region(mem1->start, resource_size(mem1),
182 DRIVER_NAME)) {
183 ret = -EBUSY;
184 goto err_mem1;
185 }
186
187 mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0));
153 if (!mcp) { 188 if (!mcp) {
154 ret = -ENOMEM; 189 ret = -ENOMEM;
155 goto release; 190 goto err_alloc;
156 } 191 }
157 192
158 mcp->owner = THIS_MODULE; 193 mcp->owner = THIS_MODULE;
@@ -160,7 +195,18 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
160 mcp->sclk_rate = data->sclk_rate; 195 mcp->sclk_rate = data->sclk_rate;
161 mcp->gpio_base = data->gpio_base; 196 mcp->gpio_base = data->gpio_base;
162 197
163 platform_set_drvdata(pdev, mcp); 198 m = priv(mcp);
199 m->mccr0 = data->mccr0 | 0x7f7f;
200 m->mccr1 = data->mccr1;
201
202 m->base0 = ioremap(mem0->start, resource_size(mem0));
203 m->base1 = ioremap(mem1->start, resource_size(mem1));
204 if (!m->base0 || !m->base1) {
205 ret = -ENOMEM;
206 goto err_ioremap;
207 }
208
209 platform_set_drvdata(dev, mcp);
164 210
165 if (machine_is_assabet()) { 211 if (machine_is_assabet()) {
166 ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); 212 ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
@@ -170,9 +216,9 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
170 * Initialise device. Note that we initially 216 * Initialise device. Note that we initially
171 * set the sampling rate to minimum. 217 * set the sampling rate to minimum.
172 */ 218 */
173 Ser4MCSR = -1; 219 writel_relaxed(-1, MCSR(m));
174 Ser4MCCR1 = data->mccr1; 220 writel_relaxed(m->mccr1, MCCR1(m));
175 Ser4MCCR0 = data->mccr0 | 0x7f7f; 221 writel_relaxed(m->mccr0, MCCR0(m));
176 222
177 /* 223 /*
178 * Calculate the read/write timeout (us) from the bit clock 224 * Calculate the read/write timeout (us) from the bit clock
@@ -184,46 +230,57 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
184 230
185 ret = mcp_host_add(mcp); 231 ret = mcp_host_add(mcp);
186 if (ret == 0) 232 if (ret == 0)
187 goto out; 233 return 0;
188 234
189 mcp_host_free(mcp); 235 platform_set_drvdata(dev, NULL);
190 release:
191 release_mem_region(0x80060000, 0x60);
192 platform_set_drvdata(pdev, NULL);
193 236
194 out: 237 err_ioremap:
238 iounmap(m->base1);
239 iounmap(m->base0);
240 mcp_host_free(mcp);
241 err_alloc:
242 release_mem_region(mem1->start, resource_size(mem1));
243 err_mem1:
244 release_mem_region(mem0->start, resource_size(mem0));
245 err_mem0:
195 return ret; 246 return ret;
196} 247}
197 248
198static int mcp_sa11x0_remove(struct platform_device *dev) 249static int mcp_sa11x0_remove(struct platform_device *dev)
199{ 250{
200 struct mcp *mcp = platform_get_drvdata(dev); 251 struct mcp *mcp = platform_get_drvdata(dev);
252 struct mcp_sa11x0 *m = priv(mcp);
253 struct resource *mem0, *mem1;
254
255 mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0);
256 mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1);
201 257
202 platform_set_drvdata(dev, NULL); 258 platform_set_drvdata(dev, NULL);
203 mcp_host_del(mcp); 259 mcp_host_del(mcp);
260 iounmap(m->base1);
261 iounmap(m->base0);
204 mcp_host_free(mcp); 262 mcp_host_free(mcp);
205 release_mem_region(0x80060000, 0x60); 263 release_mem_region(mem1->start, resource_size(mem1));
264 release_mem_region(mem0->start, resource_size(mem0));
206 265
207 return 0; 266 return 0;
208} 267}
209 268
210static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) 269static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
211{ 270{
212 struct mcp *mcp = platform_get_drvdata(dev); 271 struct mcp_sa11x0 *m = priv(platform_get_drvdata(dev));
213 272
214 priv(mcp)->mccr0 = Ser4MCCR0; 273 writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m));
215 priv(mcp)->mccr1 = Ser4MCCR1;
216 Ser4MCCR0 &= ~MCCR0_MCE;
217 274
218 return 0; 275 return 0;
219} 276}
220 277
221static int mcp_sa11x0_resume(struct platform_device *dev) 278static int mcp_sa11x0_resume(struct platform_device *dev)
222{ 279{
223 struct mcp *mcp = platform_get_drvdata(dev); 280 struct mcp_sa11x0 *m = priv(platform_get_drvdata(dev));
224 281
225 Ser4MCCR1 = priv(mcp)->mccr1; 282 writel_relaxed(m->mccr1, MCCR1(m));
226 Ser4MCCR0 = priv(mcp)->mccr0; 283 writel_relaxed(m->mccr0, MCCR0(m));
227 284
228 return 0; 285 return 0;
229} 286}