aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/mcp-sa11x0.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/mcp-sa11x0.c')
-rw-r--r--drivers/mfd/mcp-sa11x0.c198
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
31struct mcp_sa11x0 { 31struct 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
38static void 48static void
39mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) 49mcp_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
50static void 60static void
51mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) 61mcp_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)
68static void 78static void
69mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) 79mcp_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)
94static unsigned int 105static unsigned int
95mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) 106mcp_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
116static void mcp_sa11x0_enable(struct mcp *mcp) 128static 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
122static void mcp_sa11x0_disable(struct mcp *mcp) 137static 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
139static int mcp_sa11x0_probe(struct platform_device *pdev) 157static 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
209static int mcp_sa11x0_remove(struct platform_device *dev) 242static 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
220static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) 266#ifdef CONFIG_PM_SLEEP
267static 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
231static int mcp_sa11x0_resume(struct platform_device *dev) 279static 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. 290static const struct dev_pm_ops mcp_sa11x0_pm_ops = {
243 */ 291#ifdef CONFIG_PM_SLEEP
244MODULE_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
246static struct platform_driver mcp_sa11x0_driver = { 301static 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 */
259module_platform_driver(mcp_sa11x0_driver); 314module_platform_driver(mcp_sa11x0_driver);
260 315
316MODULE_ALIAS("platform:" DRIVER_NAME);
261MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); 317MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
262MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); 318MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
263MODULE_LICENSE("GPL"); 319MODULE_LICENSE("GPL");