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 RK |
22 | |
23 | #include <asm/dma.h> | |
24 | #include <asm/hardware.h> | |
25 | #include <asm/mach-types.h> | |
26 | #include <asm/system.h> | |
323cdfc1 | 27 | #include <asm/arch/mcp.h> |
5e742ad6 RK |
28 | |
29 | #include <asm/arch/assabet.h> | |
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 | ||
141 | static int mcp_sa11x0_probe(struct device *dev) | |
142 | { | |
143 | struct platform_device *pdev = to_platform_device(dev); | |
323cdfc1 | 144 | struct mcp_plat_data *data = pdev->dev.platform_data; |
5e742ad6 RK |
145 | struct mcp *mcp; |
146 | int ret; | |
147 | ||
323cdfc1 | 148 | if (!data) |
5e742ad6 RK |
149 | return -ENODEV; |
150 | ||
151 | if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) | |
152 | return -EBUSY; | |
153 | ||
154 | mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); | |
155 | if (!mcp) { | |
156 | ret = -ENOMEM; | |
157 | goto release; | |
158 | } | |
159 | ||
160 | mcp->owner = THIS_MODULE; | |
161 | mcp->ops = &mcp_sa11x0; | |
323cdfc1 | 162 | mcp->sclk_rate = data->sclk_rate; |
5e742ad6 RK |
163 | mcp->dma_audio_rd = DMA_Ser4MCP0Rd; |
164 | mcp->dma_audio_wr = DMA_Ser4MCP0Wr; | |
165 | mcp->dma_telco_rd = DMA_Ser4MCP1Rd; | |
166 | mcp->dma_telco_wr = DMA_Ser4MCP1Wr; | |
167 | ||
168 | dev_set_drvdata(dev, mcp); | |
169 | ||
170 | if (machine_is_assabet()) { | |
171 | ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); | |
172 | } | |
173 | ||
174 | /* | |
175 | * Setup the PPC unit correctly. | |
176 | */ | |
177 | PPDR &= ~PPC_RXD4; | |
178 | PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; | |
179 | PSDR |= PPC_RXD4; | |
180 | PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | |
181 | PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); | |
182 | ||
323cdfc1 RK |
183 | /* |
184 | * Initialise device. Note that we initially | |
185 | * set the sampling rate to minimum. | |
186 | */ | |
5e742ad6 | 187 | Ser4MCSR = -1; |
323cdfc1 RK |
188 | Ser4MCCR1 = data->mccr1; |
189 | Ser4MCCR0 = data->mccr0 | 0x7f7f; | |
5e742ad6 RK |
190 | |
191 | /* | |
192 | * Calculate the read/write timeout (us) from the bit clock | |
193 | * rate. This is the period for 3 64-bit frames. Always | |
194 | * round this time up. | |
195 | */ | |
196 | mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / | |
197 | mcp->sclk_rate; | |
198 | ||
199 | ret = mcp_host_register(mcp); | |
200 | if (ret == 0) | |
201 | goto out; | |
202 | ||
203 | release: | |
204 | release_mem_region(0x80060000, 0x60); | |
205 | dev_set_drvdata(dev, NULL); | |
206 | ||
207 | out: | |
208 | return ret; | |
209 | } | |
210 | ||
211 | static int mcp_sa11x0_remove(struct device *dev) | |
212 | { | |
213 | struct mcp *mcp = dev_get_drvdata(dev); | |
214 | ||
215 | dev_set_drvdata(dev, NULL); | |
216 | mcp_host_unregister(mcp); | |
217 | release_mem_region(0x80060000, 0x60); | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
9480e307 | 222 | static int mcp_sa11x0_suspend(struct device *dev, pm_message_t state) |
5e742ad6 RK |
223 | { |
224 | struct mcp *mcp = dev_get_drvdata(dev); | |
225 | ||
9480e307 RK |
226 | priv(mcp)->mccr0 = Ser4MCCR0; |
227 | priv(mcp)->mccr1 = Ser4MCCR1; | |
228 | Ser4MCCR0 &= ~MCCR0_MCE; | |
229 | ||
5e742ad6 RK |
230 | return 0; |
231 | } | |
232 | ||
9480e307 | 233 | static int mcp_sa11x0_resume(struct device *dev) |
5e742ad6 RK |
234 | { |
235 | struct mcp *mcp = dev_get_drvdata(dev); | |
236 | ||
9480e307 RK |
237 | Ser4MCCR1 = priv(mcp)->mccr1; |
238 | Ser4MCCR0 = priv(mcp)->mccr0; | |
239 | ||
5e742ad6 RK |
240 | return 0; |
241 | } | |
242 | ||
243 | /* | |
244 | * The driver for the SA11x0 MCP port. | |
245 | */ | |
246 | static struct device_driver mcp_sa11x0_driver = { | |
247 | .name = "sa11x0-mcp", | |
248 | .bus = &platform_bus_type, | |
249 | .probe = mcp_sa11x0_probe, | |
250 | .remove = mcp_sa11x0_remove, | |
251 | .suspend = mcp_sa11x0_suspend, | |
252 | .resume = mcp_sa11x0_resume, | |
253 | }; | |
254 | ||
255 | /* | |
256 | * This needs re-working | |
257 | */ | |
258 | static int __init mcp_sa11x0_init(void) | |
259 | { | |
260 | return driver_register(&mcp_sa11x0_driver); | |
261 | } | |
262 | ||
263 | static void __exit mcp_sa11x0_exit(void) | |
264 | { | |
265 | driver_unregister(&mcp_sa11x0_driver); | |
266 | } | |
267 | ||
268 | module_init(mcp_sa11x0_init); | |
269 | module_exit(mcp_sa11x0_exit); | |
270 | ||
271 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
272 | MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); | |
273 | MODULE_LICENSE("GPL"); |