diff options
Diffstat (limited to 'drivers/mfd/mcp-sa11x0.c')
-rw-r--r-- | drivers/mfd/mcp-sa11x0.c | 198 |
1 files changed, 127 insertions, 71 deletions
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index 4a27c2b9a891..c54e244ca0cf 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c | |||
@@ -13,50 +13,60 @@ | |||
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> |
19 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
20 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/pm.h> | ||
21 | #include <linux/mfd/mcp.h> | 23 | #include <linux/mfd/mcp.h> |
22 | 24 | ||
23 | #include <mach/dma.h> | ||
24 | #include <mach/hardware.h> | 25 | #include <mach/hardware.h> |
25 | #include <asm/mach-types.h> | 26 | #include <asm/mach-types.h> |
26 | #include <mach/mcp.h> | 27 | #include <mach/mcp.h> |
27 | 28 | ||
28 | #include <mach/assabet.h> | 29 | #define DRIVER_NAME "sa11x0-mcp" |
29 | |||
30 | 30 | ||
31 | struct mcp_sa11x0 { | 31 | struct mcp_sa11x0 { |
32 | u32 mccr0; | 32 | void __iomem *base0; |
33 | u32 mccr1; | 33 | void __iomem *base1; |
34 | u32 mccr0; | ||
35 | u32 mccr1; | ||
34 | }; | 36 | }; |
35 | 37 | ||
38 | /* Register offsets */ | ||
39 | #define MCCR0(m) ((m)->base0 + 0x00) | ||
40 | #define MCDR0(m) ((m)->base0 + 0x08) | ||
41 | #define MCDR1(m) ((m)->base0 + 0x0c) | ||
42 | #define MCDR2(m) ((m)->base0 + 0x10) | ||
43 | #define MCSR(m) ((m)->base0 + 0x18) | ||
44 | #define MCCR1(m) ((m)->base1 + 0x00) | ||
45 | |||
36 | #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) | 46 | #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) |
37 | 47 | ||
38 | static void | 48 | static void |
39 | mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) | 49 | mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) |
40 | { | 50 | { |
41 | unsigned int mccr0; | 51 | struct mcp_sa11x0 *m = priv(mcp); |
42 | 52 | ||
43 | divisor /= 32; | 53 | divisor /= 32; |
44 | 54 | ||
45 | mccr0 = Ser4MCCR0 & ~0x00007f00; | 55 | m->mccr0 &= ~0x00007f00; |
46 | mccr0 |= divisor << 8; | 56 | m->mccr0 |= divisor << 8; |
47 | Ser4MCCR0 = mccr0; | 57 | writel_relaxed(m->mccr0, MCCR0(m)); |
48 | } | 58 | } |
49 | 59 | ||
50 | static void | 60 | static void |
51 | mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) | 61 | mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) |
52 | { | 62 | { |
53 | unsigned int mccr0; | 63 | struct mcp_sa11x0 *m = priv(mcp); |
54 | 64 | ||
55 | divisor /= 32; | 65 | divisor /= 32; |
56 | 66 | ||
57 | mccr0 = Ser4MCCR0 & ~0x0000007f; | 67 | m->mccr0 &= ~0x0000007f; |
58 | mccr0 |= divisor; | 68 | m->mccr0 |= divisor; |
59 | Ser4MCCR0 = mccr0; | 69 | writel_relaxed(m->mccr0, MCCR0(m)); |
60 | } | 70 | } |
61 | 71 | ||
62 | /* | 72 | /* |
@@ -68,14 +78,15 @@ mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) | |||
68 | static void | 78 | static void |
69 | mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) | 79 | mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) |
70 | { | 80 | { |
81 | struct mcp_sa11x0 *m = priv(mcp); | ||
71 | int ret = -ETIME; | 82 | int ret = -ETIME; |
72 | int i; | 83 | int i; |
73 | 84 | ||
74 | Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); | 85 | writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m)); |
75 | 86 | ||
76 | for (i = 0; i < 2; i++) { | 87 | for (i = 0; i < 2; i++) { |
77 | udelay(mcp->rw_timeout); | 88 | udelay(mcp->rw_timeout); |
78 | if (Ser4MCSR & MCSR_CWC) { | 89 | if (readl_relaxed(MCSR(m)) & MCSR_CWC) { |
79 | ret = 0; | 90 | ret = 0; |
80 | break; | 91 | break; |
81 | } | 92 | } |
@@ -94,15 +105,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) | |||
94 | static unsigned int | 105 | static unsigned int |
95 | mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) | 106 | mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) |
96 | { | 107 | { |
108 | struct mcp_sa11x0 *m = priv(mcp); | ||
97 | int ret = -ETIME; | 109 | int ret = -ETIME; |
98 | int i; | 110 | int i; |
99 | 111 | ||
100 | Ser4MCDR2 = reg << 17 | MCDR2_Rd; | 112 | writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m)); |
101 | 113 | ||
102 | for (i = 0; i < 2; i++) { | 114 | for (i = 0; i < 2; i++) { |
103 | udelay(mcp->rw_timeout); | 115 | udelay(mcp->rw_timeout); |
104 | if (Ser4MCSR & MCSR_CRC) { | 116 | if (readl_relaxed(MCSR(m)) & MCSR_CRC) { |
105 | ret = Ser4MCDR2 & 0xffff; | 117 | ret = readl_relaxed(MCDR2(m)) & 0xffff; |
106 | break; | 118 | break; |
107 | } | 119 | } |
108 | } | 120 | } |
@@ -115,13 +127,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) | |||
115 | 127 | ||
116 | static void mcp_sa11x0_enable(struct mcp *mcp) | 128 | static void mcp_sa11x0_enable(struct mcp *mcp) |
117 | { | 129 | { |
118 | Ser4MCSR = -1; | 130 | struct mcp_sa11x0 *m = priv(mcp); |
119 | Ser4MCCR0 |= MCCR0_MCE; | 131 | |
132 | writel(-1, MCSR(m)); | ||
133 | m->mccr0 |= MCCR0_MCE; | ||
134 | writel_relaxed(m->mccr0, MCCR0(m)); | ||
120 | } | 135 | } |
121 | 136 | ||
122 | static void mcp_sa11x0_disable(struct mcp *mcp) | 137 | static void mcp_sa11x0_disable(struct mcp *mcp) |
123 | { | 138 | { |
124 | Ser4MCCR0 &= ~MCCR0_MCE; | 139 | struct mcp_sa11x0 *m = priv(mcp); |
140 | |||
141 | m->mccr0 &= ~MCCR0_MCE; | ||
142 | writel_relaxed(m->mccr0, MCCR0(m)); | ||
125 | } | 143 | } |
126 | 144 | ||
127 | /* | 145 | /* |
@@ -136,55 +154,64 @@ static struct mcp_ops mcp_sa11x0 = { | |||
136 | .disable = mcp_sa11x0_disable, | 154 | .disable = mcp_sa11x0_disable, |
137 | }; | 155 | }; |
138 | 156 | ||
139 | static int mcp_sa11x0_probe(struct platform_device *pdev) | 157 | static int mcp_sa11x0_probe(struct platform_device *dev) |
140 | { | 158 | { |
141 | struct mcp_plat_data *data = pdev->dev.platform_data; | 159 | struct mcp_plat_data *data = dev->dev.platform_data; |
160 | struct resource *mem0, *mem1; | ||
161 | struct mcp_sa11x0 *m; | ||
142 | struct mcp *mcp; | 162 | struct mcp *mcp; |
143 | int ret; | 163 | int ret; |
144 | 164 | ||
145 | if (!data) | 165 | if (!data) |
146 | return -ENODEV; | 166 | return -ENODEV; |
147 | 167 | ||
148 | if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) | 168 | mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); |
149 | return -EBUSY; | 169 | mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); |
170 | if (!mem0 || !mem1) | ||
171 | return -ENXIO; | ||
172 | |||
173 | if (!request_mem_region(mem0->start, resource_size(mem0), | ||
174 | DRIVER_NAME)) { | ||
175 | ret = -EBUSY; | ||
176 | goto err_mem0; | ||
177 | } | ||
150 | 178 | ||
151 | mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); | 179 | if (!request_mem_region(mem1->start, resource_size(mem1), |
180 | DRIVER_NAME)) { | ||
181 | ret = -EBUSY; | ||
182 | goto err_mem1; | ||
183 | } | ||
184 | |||
185 | mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0)); | ||
152 | if (!mcp) { | 186 | if (!mcp) { |
153 | ret = -ENOMEM; | 187 | ret = -ENOMEM; |
154 | goto release; | 188 | goto err_alloc; |
155 | } | 189 | } |
156 | 190 | ||
157 | mcp->owner = THIS_MODULE; | 191 | mcp->owner = THIS_MODULE; |
158 | mcp->ops = &mcp_sa11x0; | 192 | mcp->ops = &mcp_sa11x0; |
159 | mcp->sclk_rate = data->sclk_rate; | 193 | mcp->sclk_rate = data->sclk_rate; |
160 | mcp->dma_audio_rd = DMA_Ser4MCP0Rd; | ||
161 | mcp->dma_audio_wr = DMA_Ser4MCP0Wr; | ||
162 | mcp->dma_telco_rd = DMA_Ser4MCP1Rd; | ||
163 | mcp->dma_telco_wr = DMA_Ser4MCP1Wr; | ||
164 | mcp->gpio_base = data->gpio_base; | ||
165 | 194 | ||
166 | platform_set_drvdata(pdev, mcp); | 195 | m = priv(mcp); |
196 | m->mccr0 = data->mccr0 | 0x7f7f; | ||
197 | m->mccr1 = data->mccr1; | ||
167 | 198 | ||
168 | if (machine_is_assabet()) { | 199 | m->base0 = ioremap(mem0->start, resource_size(mem0)); |
169 | ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); | 200 | m->base1 = ioremap(mem1->start, resource_size(mem1)); |
201 | if (!m->base0 || !m->base1) { | ||
202 | ret = -ENOMEM; | ||
203 | goto err_ioremap; | ||
170 | } | 204 | } |
171 | 205 | ||
172 | /* | 206 | platform_set_drvdata(dev, mcp); |
173 | * Setup the PPC unit correctly. | ||
174 | */ | ||
175 | PPDR &= ~PPC_RXD4; | ||
176 | PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; | ||
177 | PSDR |= PPC_RXD4; | ||
178 | PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | ||
179 | PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | ||
180 | 207 | ||
181 | /* | 208 | /* |
182 | * Initialise device. Note that we initially | 209 | * Initialise device. Note that we initially |
183 | * set the sampling rate to minimum. | 210 | * set the sampling rate to minimum. |
184 | */ | 211 | */ |
185 | Ser4MCSR = -1; | 212 | writel_relaxed(-1, MCSR(m)); |
186 | Ser4MCCR1 = data->mccr1; | 213 | writel_relaxed(m->mccr1, MCCR1(m)); |
187 | Ser4MCCR0 = data->mccr0 | 0x7f7f; | 214 | writel_relaxed(m->mccr0, MCCR0(m)); |
188 | 215 | ||
189 | /* | 216 | /* |
190 | * Calculate the read/write timeout (us) from the bit clock | 217 | * Calculate the read/write timeout (us) from the bit clock |
@@ -194,62 +221,90 @@ static int mcp_sa11x0_probe(struct platform_device *pdev) | |||
194 | mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / | 221 | mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / |
195 | mcp->sclk_rate; | 222 | mcp->sclk_rate; |
196 | 223 | ||
197 | ret = mcp_host_register(mcp); | 224 | ret = mcp_host_add(mcp, data->codec_pdata); |
198 | if (ret == 0) | 225 | if (ret == 0) |
199 | goto out; | 226 | return 0; |
200 | 227 | ||
201 | release: | 228 | platform_set_drvdata(dev, NULL); |
202 | release_mem_region(0x80060000, 0x60); | ||
203 | platform_set_drvdata(pdev, NULL); | ||
204 | 229 | ||
205 | out: | 230 | err_ioremap: |
231 | iounmap(m->base1); | ||
232 | iounmap(m->base0); | ||
233 | mcp_host_free(mcp); | ||
234 | err_alloc: | ||
235 | release_mem_region(mem1->start, resource_size(mem1)); | ||
236 | err_mem1: | ||
237 | release_mem_region(mem0->start, resource_size(mem0)); | ||
238 | err_mem0: | ||
206 | return ret; | 239 | return ret; |
207 | } | 240 | } |
208 | 241 | ||
209 | static int mcp_sa11x0_remove(struct platform_device *dev) | 242 | static int mcp_sa11x0_remove(struct platform_device *dev) |
210 | { | 243 | { |
211 | struct mcp *mcp = platform_get_drvdata(dev); | 244 | struct mcp *mcp = platform_get_drvdata(dev); |
245 | struct mcp_sa11x0 *m = priv(mcp); | ||
246 | struct resource *mem0, *mem1; | ||
247 | |||
248 | if (m->mccr0 & MCCR0_MCE) | ||
249 | dev_warn(&dev->dev, | ||
250 | "device left active (missing disable call?)\n"); | ||
251 | |||
252 | mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
253 | mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); | ||
212 | 254 | ||
213 | platform_set_drvdata(dev, NULL); | 255 | platform_set_drvdata(dev, NULL); |
214 | mcp_host_unregister(mcp); | 256 | mcp_host_del(mcp); |
215 | release_mem_region(0x80060000, 0x60); | 257 | iounmap(m->base1); |
258 | iounmap(m->base0); | ||
259 | mcp_host_free(mcp); | ||
260 | release_mem_region(mem1->start, resource_size(mem1)); | ||
261 | release_mem_region(mem0->start, resource_size(mem0)); | ||
216 | 262 | ||
217 | return 0; | 263 | return 0; |
218 | } | 264 | } |
219 | 265 | ||
220 | static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) | 266 | #ifdef CONFIG_PM_SLEEP |
267 | static int mcp_sa11x0_suspend(struct device *dev) | ||
221 | { | 268 | { |
222 | struct mcp *mcp = platform_get_drvdata(dev); | 269 | struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); |
270 | |||
271 | if (m->mccr0 & MCCR0_MCE) | ||
272 | dev_warn(dev, "device left active (missing disable call?)\n"); | ||
223 | 273 | ||
224 | priv(mcp)->mccr0 = Ser4MCCR0; | 274 | writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m)); |
225 | priv(mcp)->mccr1 = Ser4MCCR1; | ||
226 | Ser4MCCR0 &= ~MCCR0_MCE; | ||
227 | 275 | ||
228 | return 0; | 276 | return 0; |
229 | } | 277 | } |
230 | 278 | ||
231 | static int mcp_sa11x0_resume(struct platform_device *dev) | 279 | static int mcp_sa11x0_resume(struct device *dev) |
232 | { | 280 | { |
233 | struct mcp *mcp = platform_get_drvdata(dev); | 281 | struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); |
234 | 282 | ||
235 | Ser4MCCR1 = priv(mcp)->mccr1; | 283 | writel_relaxed(m->mccr1, MCCR1(m)); |
236 | Ser4MCCR0 = priv(mcp)->mccr0; | 284 | writel_relaxed(m->mccr0, MCCR0(m)); |
237 | 285 | ||
238 | return 0; | 286 | return 0; |
239 | } | 287 | } |
240 | 288 | #endif | |
241 | /* | 289 | |
242 | * The driver for the SA11x0 MCP port. | 290 | static const struct dev_pm_ops mcp_sa11x0_pm_ops = { |
243 | */ | 291 | #ifdef CONFIG_PM_SLEEP |
244 | MODULE_ALIAS("platform:sa11x0-mcp"); | 292 | .suspend = mcp_sa11x0_suspend, |
293 | .freeze = mcp_sa11x0_suspend, | ||
294 | .poweroff = mcp_sa11x0_suspend, | ||
295 | .resume_noirq = mcp_sa11x0_resume, | ||
296 | .thaw_noirq = mcp_sa11x0_resume, | ||
297 | .restore_noirq = mcp_sa11x0_resume, | ||
298 | #endif | ||
299 | }; | ||
245 | 300 | ||
246 | static struct platform_driver mcp_sa11x0_driver = { | 301 | static struct platform_driver mcp_sa11x0_driver = { |
247 | .probe = mcp_sa11x0_probe, | 302 | .probe = mcp_sa11x0_probe, |
248 | .remove = mcp_sa11x0_remove, | 303 | .remove = mcp_sa11x0_remove, |
249 | .suspend = mcp_sa11x0_suspend, | ||
250 | .resume = mcp_sa11x0_resume, | ||
251 | .driver = { | 304 | .driver = { |
252 | .name = "sa11x0-mcp", | 305 | .name = DRIVER_NAME, |
306 | .owner = THIS_MODULE, | ||
307 | .pm = &mcp_sa11x0_pm_ops, | ||
253 | }, | 308 | }, |
254 | }; | 309 | }; |
255 | 310 | ||
@@ -258,6 +313,7 @@ static struct platform_driver mcp_sa11x0_driver = { | |||
258 | */ | 313 | */ |
259 | module_platform_driver(mcp_sa11x0_driver); | 314 | module_platform_driver(mcp_sa11x0_driver); |
260 | 315 | ||
316 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
261 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | 317 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); |
262 | MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); | 318 | MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); |
263 | MODULE_LICENSE("GPL"); | 319 | MODULE_LICENSE("GPL"); |