Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[sfrench/cifs-2.6.git] / drivers / mfd / mcp-sa11x0.c
1 /*
2  *  linux/drivers/mfd/mcp-sa11x0.c
3  *
4  *  Copyright (C) 2001-2005 Russell King
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License.
9  *
10  *  SA11x0 MCP (Multimedia Communications Port) driver.
11  *
12  *  MCP read/write timeouts from Jordi Colomer, rehacked by rmk.
13  */
14 #include <linux/module.h>
15 #include <linux/init.h>
16 #include <linux/errno.h>
17 #include <linux/kernel.h>
18 #include <linux/delay.h>
19 #include <linux/spinlock.h>
20 #include <linux/platform_device.h>
21 #include <linux/mfd/mcp.h>
22 #include <linux/io.h>
23
24 #include <mach/dma.h>
25 #include <mach/hardware.h>
26 #include <asm/mach-types.h>
27 #include <asm/system.h>
28 #include <mach/mcp.h>
29
30 /* Register offsets */
31 #define MCCR0   0x00
32 #define MCDR0   0x08
33 #define MCDR1   0x0C
34 #define MCDR2   0x10
35 #define MCSR    0x18
36 #define MCCR1   0x00
37
38 struct mcp_sa11x0 {
39         u32             mccr0;
40         u32             mccr1;
41         unsigned char   *mccr0_base;
42         unsigned char   *mccr1_base;
43 };
44
45 #define priv(mcp)       ((struct mcp_sa11x0 *)mcp_priv(mcp))
46
47 static void
48 mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
49 {
50         struct mcp_sa11x0 *priv = priv(mcp);
51
52         divisor /= 32;
53
54         priv->mccr0 &= ~0x00007f00;
55         priv->mccr0 |= divisor << 8;
56         __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
57 }
58
59 static void
60 mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
61 {
62         struct mcp_sa11x0 *priv = priv(mcp);
63
64         divisor /= 32;
65
66         priv->mccr0 &= ~0x0000007f;
67         priv->mccr0 |= divisor;
68         __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
69 }
70
71 /*
72  * Write data to the device.  The bit should be set after 3 subframe
73  * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
74  * We really should try doing something more productive while we
75  * wait.
76  */
77 static void
78 mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
79 {
80         int ret = -ETIME;
81         int i;
82         u32 mcpreg;
83         struct mcp_sa11x0 *priv = priv(mcp);
84
85         mcpreg = reg << 17 | MCDR2_Wr | (val & 0xffff);
86         __raw_writel(mcpreg, priv->mccr0_base + MCDR2);
87
88         for (i = 0; i < 2; i++) {
89                 udelay(mcp->rw_timeout);
90                 mcpreg = __raw_readl(priv->mccr0_base + MCSR);
91                 if (mcpreg & MCSR_CWC) {
92                         ret = 0;
93                         break;
94                 }
95         }
96
97         if (ret < 0)
98                 printk(KERN_WARNING "mcp: write timed out\n");
99 }
100
101 /*
102  * Read data from the device.  The bit should be set after 3 subframe
103  * times (each frame is 64 clocks).  We wait a maximum of 6 subframes.
104  * We really should try doing something more productive while we
105  * wait.
106  */
107 static unsigned int
108 mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
109 {
110         int ret = -ETIME;
111         int i;
112         u32 mcpreg;
113         struct mcp_sa11x0 *priv = priv(mcp);
114
115         mcpreg = reg << 17 | MCDR2_Rd;
116         __raw_writel(mcpreg, priv->mccr0_base + MCDR2);
117
118         for (i = 0; i < 2; i++) {
119                 udelay(mcp->rw_timeout);
120                 mcpreg = __raw_readl(priv->mccr0_base + MCSR);
121                 if (mcpreg & MCSR_CRC) {
122                         ret = __raw_readl(priv->mccr0_base + MCDR2)
123                                 & 0xffff;
124                         break;
125                 }
126         }
127
128         if (ret < 0)
129                 printk(KERN_WARNING "mcp: read timed out\n");
130
131         return ret;
132 }
133
134 static void mcp_sa11x0_enable(struct mcp *mcp)
135 {
136         struct mcp_sa11x0 *priv = priv(mcp);
137
138         __raw_writel(-1, priv->mccr0_base + MCSR);
139         priv->mccr0 |= MCCR0_MCE;
140         __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
141 }
142
143 static void mcp_sa11x0_disable(struct mcp *mcp)
144 {
145         struct mcp_sa11x0 *priv = priv(mcp);
146
147         priv->mccr0 &= ~MCCR0_MCE;
148         __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
149 }
150
151 /*
152  * Our methods.
153  */
154 static struct mcp_ops mcp_sa11x0 = {
155         .set_telecom_divisor    = mcp_sa11x0_set_telecom_divisor,
156         .set_audio_divisor      = mcp_sa11x0_set_audio_divisor,
157         .reg_write              = mcp_sa11x0_write,
158         .reg_read               = mcp_sa11x0_read,
159         .enable                 = mcp_sa11x0_enable,
160         .disable                = mcp_sa11x0_disable,
161 };
162
163 static int mcp_sa11x0_probe(struct platform_device *pdev)
164 {
165         struct mcp_plat_data *data = pdev->dev.platform_data;
166         struct mcp *mcp;
167         int ret;
168         struct mcp_sa11x0 *priv;
169         struct resource *res_mem0, *res_mem1;
170         u32 size0, size1;
171
172         if (!data)
173                 return -ENODEV;
174
175         if (!data->codec)
176                 return -ENODEV;
177
178         res_mem0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
179         if (!res_mem0)
180                 return -ENODEV;
181         size0 = res_mem0->end - res_mem0->start + 1;
182
183         res_mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
184         if (!res_mem1)
185                 return -ENODEV;
186         size1 = res_mem1->end - res_mem1->start + 1;
187
188         if (!request_mem_region(res_mem0->start, size0, "sa11x0-mcp"))
189                 return -EBUSY;
190
191         if (!request_mem_region(res_mem1->start, size1, "sa11x0-mcp")) {
192                 ret = -EBUSY;
193                 goto release;
194         }
195
196         mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
197         if (!mcp) {
198                 ret = -ENOMEM;
199                 goto release2;
200         }
201
202         priv = priv(mcp);
203
204         mcp->owner              = THIS_MODULE;
205         mcp->ops                = &mcp_sa11x0;
206         mcp->sclk_rate          = data->sclk_rate;
207         mcp->dma_audio_rd       = DDAR_DevAdd(res_mem0->start + MCDR0)
208                                 + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev;
209         mcp->dma_audio_wr       = DDAR_DevAdd(res_mem0->start + MCDR0)
210                                 + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev;
211         mcp->dma_telco_rd       = DDAR_DevAdd(res_mem0->start + MCDR1)
212                                 + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev;
213         mcp->dma_telco_wr       = DDAR_DevAdd(res_mem0->start + MCDR1)
214                                 + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev;
215         mcp->codec              = data->codec;
216
217         platform_set_drvdata(pdev, mcp);
218
219         /*
220          * Initialise device.  Note that we initially
221          * set the sampling rate to minimum.
222          */
223         priv->mccr0_base = ioremap(res_mem0->start, size0);
224         priv->mccr1_base = ioremap(res_mem1->start, size1);
225
226         __raw_writel(-1, priv->mccr0_base + MCSR);
227         priv->mccr1 = data->mccr1;
228         priv->mccr0 = data->mccr0 | 0x7f7f;
229         __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
230         __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1);
231
232         /*
233          * Calculate the read/write timeout (us) from the bit clock
234          * rate.  This is the period for 3 64-bit frames.  Always
235          * round this time up.
236          */
237         mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
238                           mcp->sclk_rate;
239
240         ret = mcp_host_register(mcp, data->codec_pdata);
241         if (ret == 0)
242                 goto out;
243
244  release2:
245         release_mem_region(res_mem1->start, size1);
246  release:
247         release_mem_region(res_mem0->start, size0);
248         platform_set_drvdata(pdev, NULL);
249
250  out:
251         return ret;
252 }
253
254 static int mcp_sa11x0_remove(struct platform_device *pdev)
255 {
256         struct mcp *mcp = platform_get_drvdata(pdev);
257         struct mcp_sa11x0 *priv = priv(mcp);
258         struct resource *res_mem;
259         u32 size;
260
261         platform_set_drvdata(pdev, NULL);
262         mcp_host_unregister(mcp);
263
264         res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
265         if (res_mem) {
266                 size = res_mem->end - res_mem->start + 1;
267                 release_mem_region(res_mem->start, size);
268         }
269         res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
270         if (res_mem) {
271                 size = res_mem->end - res_mem->start + 1;
272                 release_mem_region(res_mem->start, size);
273         }
274         iounmap(priv->mccr0_base);
275         iounmap(priv->mccr1_base);
276         return 0;
277 }
278
279 static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
280 {
281         struct mcp *mcp = platform_get_drvdata(dev);
282         struct mcp_sa11x0 *priv = priv(mcp);
283         u32 mccr0;
284
285         mccr0 = priv->mccr0 & ~MCCR0_MCE;
286         __raw_writel(mccr0, priv->mccr0_base + MCCR0);
287
288         return 0;
289 }
290
291 static int mcp_sa11x0_resume(struct platform_device *dev)
292 {
293         struct mcp *mcp = platform_get_drvdata(dev);
294         struct mcp_sa11x0 *priv = priv(mcp);
295
296         __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
297         __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1);
298
299         return 0;
300 }
301
302 /*
303  * The driver for the SA11x0 MCP port.
304  */
305 MODULE_ALIAS("platform:sa11x0-mcp");
306
307 static struct platform_driver mcp_sa11x0_driver = {
308         .probe          = mcp_sa11x0_probe,
309         .remove         = mcp_sa11x0_remove,
310         .suspend        = mcp_sa11x0_suspend,
311         .resume         = mcp_sa11x0_resume,
312         .driver         = {
313                 .name   = "sa11x0-mcp",
314                 .owner  = THIS_MODULE,
315         },
316 };
317
318 /*
319  * This needs re-working
320  */
321 module_platform_driver(mcp_sa11x0_driver);
322
323 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
324 MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
325 MODULE_LICENSE("GPL");