Commit | Line | Data |
---|---|---|
5e742ad6 RK |
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/slab.h> | |
d052d1be | 21 | #include <linux/platform_device.h> |
5e742ad6 | 22 | |
dcea83ad | 23 | #include <mach/dma.h> |
a09e64fb | 24 | #include <mach/hardware.h> |
5e742ad6 RK |
25 | #include <asm/mach-types.h> |
26 | #include <asm/system.h> | |
a09e64fb | 27 | #include <mach/mcp.h> |
5e742ad6 | 28 | |
a09e64fb | 29 | #include <mach/assabet.h> |
5e742ad6 RK |
30 | |
31 | #include "mcp.h" | |
32 | ||
33 | struct mcp_sa11x0 { | |
34 | u32 mccr0; | |
35 | u32 mccr1; | |
36 | }; | |
37 | ||
38 | #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) | |
39 | ||
40 | static void | |
41 | mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) | |
42 | { | |
43 | unsigned int mccr0; | |
44 | ||
45 | divisor /= 32; | |
46 | ||
47 | mccr0 = Ser4MCCR0 & ~0x00007f00; | |
48 | mccr0 |= divisor << 8; | |
49 | Ser4MCCR0 = mccr0; | |
50 | } | |
51 | ||
52 | static void | |
53 | mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) | |
54 | { | |
55 | unsigned int mccr0; | |
56 | ||
57 | divisor /= 32; | |
58 | ||
59 | mccr0 = Ser4MCCR0 & ~0x0000007f; | |
60 | mccr0 |= divisor; | |
61 | Ser4MCCR0 = mccr0; | |
62 | } | |
63 | ||
64 | /* | |
65 | * Write data to the device. The bit should be set after 3 subframe | |
66 | * times (each frame is 64 clocks). We wait a maximum of 6 subframes. | |
67 | * We really should try doing something more productive while we | |
68 | * wait. | |
69 | */ | |
70 | static void | |
71 | mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) | |
72 | { | |
73 | int ret = -ETIME; | |
74 | int i; | |
75 | ||
76 | Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); | |
77 | ||
78 | for (i = 0; i < 2; i++) { | |
79 | udelay(mcp->rw_timeout); | |
80 | if (Ser4MCSR & MCSR_CWC) { | |
81 | ret = 0; | |
82 | break; | |
83 | } | |
84 | } | |
85 | ||
86 | if (ret < 0) | |
87 | printk(KERN_WARNING "mcp: write timed out\n"); | |
88 | } | |
89 | ||
90 | /* | |
91 | * Read data from the device. The bit should be set after 3 subframe | |
92 | * times (each frame is 64 clocks). We wait a maximum of 6 subframes. | |
93 | * We really should try doing something more productive while we | |
94 | * wait. | |
95 | */ | |
96 | static unsigned int | |
97 | mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) | |
98 | { | |
99 | int ret = -ETIME; | |
100 | int i; | |
101 | ||
102 | Ser4MCDR2 = reg << 17 | MCDR2_Rd; | |
103 | ||
104 | for (i = 0; i < 2; i++) { | |
105 | udelay(mcp->rw_timeout); | |
106 | if (Ser4MCSR & MCSR_CRC) { | |
107 | ret = Ser4MCDR2 & 0xffff; | |
108 | break; | |
109 | } | |
110 | } | |
111 | ||
112 | if (ret < 0) | |
113 | printk(KERN_WARNING "mcp: read timed out\n"); | |
114 | ||
115 | return ret; | |
116 | } | |
117 | ||
118 | static void mcp_sa11x0_enable(struct mcp *mcp) | |
119 | { | |
120 | Ser4MCSR = -1; | |
121 | Ser4MCCR0 |= MCCR0_MCE; | |
122 | } | |
123 | ||
124 | static void mcp_sa11x0_disable(struct mcp *mcp) | |
125 | { | |
126 | Ser4MCCR0 &= ~MCCR0_MCE; | |
127 | } | |
128 | ||
129 | /* | |
130 | * Our methods. | |
131 | */ | |
132 | static struct mcp_ops mcp_sa11x0 = { | |
133 | .set_telecom_divisor = mcp_sa11x0_set_telecom_divisor, | |
134 | .set_audio_divisor = mcp_sa11x0_set_audio_divisor, | |
135 | .reg_write = mcp_sa11x0_write, | |
136 | .reg_read = mcp_sa11x0_read, | |
137 | .enable = mcp_sa11x0_enable, | |
138 | .disable = mcp_sa11x0_disable, | |
139 | }; | |
140 | ||
3ae5eaec | 141 | static int mcp_sa11x0_probe(struct platform_device *pdev) |
5e742ad6 | 142 | { |
323cdfc1 | 143 | struct mcp_plat_data *data = pdev->dev.platform_data; |
5e742ad6 RK |
144 | struct mcp *mcp; |
145 | int ret; | |
146 | ||
323cdfc1 | 147 | if (!data) |
5e742ad6 RK |
148 | return -ENODEV; |
149 | ||
150 | if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) | |
151 | return -EBUSY; | |
152 | ||
153 | mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); | |
154 | if (!mcp) { | |
155 | ret = -ENOMEM; | |
156 | goto release; | |
157 | } | |
158 | ||
159 | mcp->owner = THIS_MODULE; | |
160 | mcp->ops = &mcp_sa11x0; | |
323cdfc1 | 161 | mcp->sclk_rate = data->sclk_rate; |
5e742ad6 RK |
162 | mcp->dma_audio_rd = DMA_Ser4MCP0Rd; |
163 | mcp->dma_audio_wr = DMA_Ser4MCP0Wr; | |
164 | mcp->dma_telco_rd = DMA_Ser4MCP1Rd; | |
165 | mcp->dma_telco_wr = DMA_Ser4MCP1Wr; | |
166 | ||
3ae5eaec | 167 | platform_set_drvdata(pdev, mcp); |
5e742ad6 RK |
168 | |
169 | if (machine_is_assabet()) { | |
170 | ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); | |
171 | } | |
172 | ||
173 | /* | |
174 | * Setup the PPC unit correctly. | |
175 | */ | |
176 | PPDR &= ~PPC_RXD4; | |
177 | PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; | |
178 | PSDR |= PPC_RXD4; | |
179 | PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | |
180 | PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | |
181 | ||
323cdfc1 RK |
182 | /* |
183 | * Initialise device. Note that we initially | |
184 | * set the sampling rate to minimum. | |
185 | */ | |
5e742ad6 | 186 | Ser4MCSR = -1; |
323cdfc1 RK |
187 | Ser4MCCR1 = data->mccr1; |
188 | Ser4MCCR0 = data->mccr0 | 0x7f7f; | |
5e742ad6 RK |
189 | |
190 | /* | |
191 | * Calculate the read/write timeout (us) from the bit clock | |
192 | * rate. This is the period for 3 64-bit frames. Always | |
193 | * round this time up. | |
194 | */ | |
195 | mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / | |
196 | mcp->sclk_rate; | |
197 | ||
198 | ret = mcp_host_register(mcp); | |
199 | if (ret == 0) | |
200 | goto out; | |
201 | ||
202 | release: | |
203 | release_mem_region(0x80060000, 0x60); | |
3ae5eaec | 204 | platform_set_drvdata(pdev, NULL); |
5e742ad6 RK |
205 | |
206 | out: | |
207 | return ret; | |
208 | } | |
209 | ||
3ae5eaec | 210 | static int mcp_sa11x0_remove(struct platform_device *dev) |
5e742ad6 | 211 | { |
3ae5eaec | 212 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 213 | |
3ae5eaec | 214 | platform_set_drvdata(dev, NULL); |
5e742ad6 RK |
215 | mcp_host_unregister(mcp); |
216 | release_mem_region(0x80060000, 0x60); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
3ae5eaec | 221 | static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) |
5e742ad6 | 222 | { |
3ae5eaec | 223 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 224 | |
9480e307 RK |
225 | priv(mcp)->mccr0 = Ser4MCCR0; |
226 | priv(mcp)->mccr1 = Ser4MCCR1; | |
227 | Ser4MCCR0 &= ~MCCR0_MCE; | |
228 | ||
5e742ad6 RK |
229 | return 0; |
230 | } | |
231 | ||
3ae5eaec | 232 | static int mcp_sa11x0_resume(struct platform_device *dev) |
5e742ad6 | 233 | { |
3ae5eaec | 234 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 235 | |
9480e307 RK |
236 | Ser4MCCR1 = priv(mcp)->mccr1; |
237 | Ser4MCCR0 = priv(mcp)->mccr0; | |
238 | ||
5e742ad6 RK |
239 | return 0; |
240 | } | |
241 | ||
242 | /* | |
243 | * The driver for the SA11x0 MCP port. | |
244 | */ | |
4f46d6e7 KS |
245 | MODULE_ALIAS("platform:sa11x0-mcp"); |
246 | ||
3ae5eaec | 247 | static struct platform_driver mcp_sa11x0_driver = { |
5e742ad6 RK |
248 | .probe = mcp_sa11x0_probe, |
249 | .remove = mcp_sa11x0_remove, | |
250 | .suspend = mcp_sa11x0_suspend, | |
251 | .resume = mcp_sa11x0_resume, | |
3ae5eaec RK |
252 | .driver = { |
253 | .name = "sa11x0-mcp", | |
254 | }, | |
5e742ad6 RK |
255 | }; |
256 | ||
257 | /* | |
258 | * This needs re-working | |
259 | */ | |
260 | static int __init mcp_sa11x0_init(void) | |
261 | { | |
3ae5eaec | 262 | return platform_driver_register(&mcp_sa11x0_driver); |
5e742ad6 RK |
263 | } |
264 | ||
265 | static void __exit mcp_sa11x0_exit(void) | |
266 | { | |
3ae5eaec | 267 | platform_driver_unregister(&mcp_sa11x0_driver); |
5e742ad6 RK |
268 | } |
269 | ||
270 | module_init(mcp_sa11x0_init); | |
271 | module_exit(mcp_sa11x0_exit); | |
272 | ||
273 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
274 | MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); | |
275 | MODULE_LICENSE("GPL"); |