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> | |
d052d1be | 20 | #include <linux/platform_device.h> |
c8602edf | 21 | #include <linux/mfd/mcp.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 | |
216f63c4 RK |
29 | #include <mach/assabet.h> |
30 | ||
5e742ad6 RK |
31 | |
32 | struct mcp_sa11x0 { | |
216f63c4 RK |
33 | u32 mccr0; |
34 | u32 mccr1; | |
5e742ad6 RK |
35 | }; |
36 | ||
37 | #define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) | |
38 | ||
39 | static void | |
40 | mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) | |
41 | { | |
216f63c4 | 42 | unsigned int mccr0; |
5e742ad6 RK |
43 | |
44 | divisor /= 32; | |
45 | ||
216f63c4 RK |
46 | mccr0 = Ser4MCCR0 & ~0x00007f00; |
47 | mccr0 |= divisor << 8; | |
48 | Ser4MCCR0 = mccr0; | |
5e742ad6 RK |
49 | } |
50 | ||
51 | static void | |
52 | mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) | |
53 | { | |
216f63c4 | 54 | unsigned int mccr0; |
5e742ad6 RK |
55 | |
56 | divisor /= 32; | |
57 | ||
216f63c4 RK |
58 | mccr0 = Ser4MCCR0 & ~0x0000007f; |
59 | mccr0 |= divisor; | |
60 | Ser4MCCR0 = mccr0; | |
5e742ad6 RK |
61 | } |
62 | ||
63 | /* | |
64 | * Write data to the device. The bit should be set after 3 subframe | |
65 | * times (each frame is 64 clocks). We wait a maximum of 6 subframes. | |
66 | * We really should try doing something more productive while we | |
67 | * wait. | |
68 | */ | |
69 | static void | |
70 | mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) | |
71 | { | |
72 | int ret = -ETIME; | |
73 | int i; | |
74 | ||
216f63c4 | 75 | Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); |
5e742ad6 RK |
76 | |
77 | for (i = 0; i < 2; i++) { | |
78 | udelay(mcp->rw_timeout); | |
216f63c4 | 79 | if (Ser4MCSR & MCSR_CWC) { |
5e742ad6 RK |
80 | ret = 0; |
81 | break; | |
82 | } | |
83 | } | |
84 | ||
85 | if (ret < 0) | |
86 | printk(KERN_WARNING "mcp: write timed out\n"); | |
87 | } | |
88 | ||
89 | /* | |
90 | * Read data from the device. The bit should be set after 3 subframe | |
91 | * times (each frame is 64 clocks). We wait a maximum of 6 subframes. | |
92 | * We really should try doing something more productive while we | |
93 | * wait. | |
94 | */ | |
95 | static unsigned int | |
96 | mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) | |
97 | { | |
98 | int ret = -ETIME; | |
99 | int i; | |
100 | ||
216f63c4 | 101 | Ser4MCDR2 = reg << 17 | MCDR2_Rd; |
5e742ad6 RK |
102 | |
103 | for (i = 0; i < 2; i++) { | |
104 | udelay(mcp->rw_timeout); | |
216f63c4 RK |
105 | if (Ser4MCSR & MCSR_CRC) { |
106 | ret = Ser4MCDR2 & 0xffff; | |
5e742ad6 RK |
107 | break; |
108 | } | |
109 | } | |
110 | ||
111 | if (ret < 0) | |
112 | printk(KERN_WARNING "mcp: read timed out\n"); | |
113 | ||
114 | return ret; | |
115 | } | |
116 | ||
117 | static void mcp_sa11x0_enable(struct mcp *mcp) | |
118 | { | |
216f63c4 RK |
119 | Ser4MCSR = -1; |
120 | Ser4MCCR0 |= MCCR0_MCE; | |
5e742ad6 RK |
121 | } |
122 | ||
123 | static void mcp_sa11x0_disable(struct mcp *mcp) | |
124 | { | |
216f63c4 | 125 | Ser4MCCR0 &= ~MCCR0_MCE; |
5e742ad6 RK |
126 | } |
127 | ||
128 | /* | |
129 | * Our methods. | |
130 | */ | |
131 | static struct mcp_ops mcp_sa11x0 = { | |
132 | .set_telecom_divisor = mcp_sa11x0_set_telecom_divisor, | |
133 | .set_audio_divisor = mcp_sa11x0_set_audio_divisor, | |
134 | .reg_write = mcp_sa11x0_write, | |
135 | .reg_read = mcp_sa11x0_read, | |
136 | .enable = mcp_sa11x0_enable, | |
137 | .disable = mcp_sa11x0_disable, | |
138 | }; | |
139 | ||
3ae5eaec | 140 | static int mcp_sa11x0_probe(struct platform_device *pdev) |
5e742ad6 | 141 | { |
323cdfc1 | 142 | struct mcp_plat_data *data = pdev->dev.platform_data; |
5e742ad6 RK |
143 | struct mcp *mcp; |
144 | int ret; | |
145 | ||
323cdfc1 | 146 | if (!data) |
5e742ad6 RK |
147 | return -ENODEV; |
148 | ||
216f63c4 | 149 | if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) |
5e742ad6 RK |
150 | return -EBUSY; |
151 | ||
152 | mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0)); | |
153 | if (!mcp) { | |
154 | ret = -ENOMEM; | |
216f63c4 | 155 | goto release; |
5e742ad6 RK |
156 | } |
157 | ||
158 | mcp->owner = THIS_MODULE; | |
159 | mcp->ops = &mcp_sa11x0; | |
323cdfc1 | 160 | mcp->sclk_rate = data->sclk_rate; |
216f63c4 RK |
161 | mcp->dma_audio_rd = DMA_Ser4MCP0Rd; |
162 | mcp->dma_audio_wr = DMA_Ser4MCP0Wr; | |
163 | mcp->dma_telco_rd = DMA_Ser4MCP1Rd; | |
164 | mcp->dma_telco_wr = DMA_Ser4MCP1Wr; | |
65f2e753 | 165 | mcp->gpio_base = data->gpio_base; |
5e742ad6 | 166 | |
3ae5eaec | 167 | platform_set_drvdata(pdev, mcp); |
5e742ad6 | 168 | |
216f63c4 RK |
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 | */ | |
216f63c4 RK |
186 | Ser4MCSR = -1; |
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 | ||
65f2e753 | 198 | ret = mcp_host_register(mcp); |
5e742ad6 RK |
199 | if (ret == 0) |
200 | goto out; | |
201 | ||
202 | release: | |
216f63c4 | 203 | release_mem_region(0x80060000, 0x60); |
3ae5eaec | 204 | platform_set_drvdata(pdev, NULL); |
5e742ad6 RK |
205 | |
206 | out: | |
207 | return ret; | |
208 | } | |
209 | ||
216f63c4 | 210 | static int mcp_sa11x0_remove(struct platform_device *dev) |
5e742ad6 | 211 | { |
216f63c4 | 212 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 213 | |
216f63c4 | 214 | platform_set_drvdata(dev, NULL); |
5e742ad6 | 215 | mcp_host_unregister(mcp); |
216f63c4 | 216 | release_mem_region(0x80060000, 0x60); |
5e742ad6 RK |
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 | |
216f63c4 RK |
225 | priv(mcp)->mccr0 = Ser4MCCR0; |
226 | priv(mcp)->mccr1 = Ser4MCCR1; | |
227 | Ser4MCCR0 &= ~MCCR0_MCE; | |
9480e307 | 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 | |
216f63c4 RK |
236 | Ser4MCCR1 = priv(mcp)->mccr1; |
237 | Ser4MCCR0 = priv(mcp)->mccr0; | |
9480e307 | 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 | */ | |
65349d60 | 260 | module_platform_driver(mcp_sa11x0_driver); |
5e742ad6 RK |
261 | |
262 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
263 | MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); | |
264 | MODULE_LICENSE("GPL"); |