]>
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 | |
a09e64fb | 23 | #include <mach/hardware.h> |
5e742ad6 RK |
24 | #include <asm/mach-types.h> |
25 | #include <asm/system.h> | |
a09e64fb | 26 | #include <mach/mcp.h> |
5e742ad6 | 27 | |
216f63c4 RK |
28 | #include <mach/assabet.h> |
29 | ||
c4592ce4 | 30 | #define DRIVER_NAME "sa11x0-mcp" |
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; |
65f2e753 | 161 | mcp->gpio_base = data->gpio_base; |
5e742ad6 | 162 | |
3ae5eaec | 163 | platform_set_drvdata(pdev, mcp); |
5e742ad6 | 164 | |
216f63c4 RK |
165 | if (machine_is_assabet()) { |
166 | ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); | |
167 | } | |
168 | ||
323cdfc1 RK |
169 | /* |
170 | * Initialise device. Note that we initially | |
171 | * set the sampling rate to minimum. | |
172 | */ | |
216f63c4 RK |
173 | Ser4MCSR = -1; |
174 | Ser4MCCR1 = data->mccr1; | |
175 | Ser4MCCR0 = data->mccr0 | 0x7f7f; | |
5e742ad6 RK |
176 | |
177 | /* | |
178 | * Calculate the read/write timeout (us) from the bit clock | |
179 | * rate. This is the period for 3 64-bit frames. Always | |
180 | * round this time up. | |
181 | */ | |
182 | mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / | |
183 | mcp->sclk_rate; | |
184 | ||
30816ac0 | 185 | ret = mcp_host_add(mcp); |
5e742ad6 RK |
186 | if (ret == 0) |
187 | goto out; | |
188 | ||
30816ac0 | 189 | mcp_host_free(mcp); |
5e742ad6 | 190 | release: |
216f63c4 | 191 | release_mem_region(0x80060000, 0x60); |
3ae5eaec | 192 | platform_set_drvdata(pdev, NULL); |
5e742ad6 RK |
193 | |
194 | out: | |
195 | return ret; | |
196 | } | |
197 | ||
216f63c4 | 198 | static int mcp_sa11x0_remove(struct platform_device *dev) |
5e742ad6 | 199 | { |
216f63c4 | 200 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 201 | |
216f63c4 | 202 | platform_set_drvdata(dev, NULL); |
30816ac0 RK |
203 | mcp_host_del(mcp); |
204 | mcp_host_free(mcp); | |
216f63c4 | 205 | release_mem_region(0x80060000, 0x60); |
5e742ad6 RK |
206 | |
207 | return 0; | |
208 | } | |
209 | ||
3ae5eaec | 210 | static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state) |
5e742ad6 | 211 | { |
3ae5eaec | 212 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 213 | |
216f63c4 RK |
214 | priv(mcp)->mccr0 = Ser4MCCR0; |
215 | priv(mcp)->mccr1 = Ser4MCCR1; | |
216 | Ser4MCCR0 &= ~MCCR0_MCE; | |
9480e307 | 217 | |
5e742ad6 RK |
218 | return 0; |
219 | } | |
220 | ||
3ae5eaec | 221 | static int mcp_sa11x0_resume(struct platform_device *dev) |
5e742ad6 | 222 | { |
3ae5eaec | 223 | struct mcp *mcp = platform_get_drvdata(dev); |
5e742ad6 | 224 | |
216f63c4 RK |
225 | Ser4MCCR1 = priv(mcp)->mccr1; |
226 | Ser4MCCR0 = priv(mcp)->mccr0; | |
9480e307 | 227 | |
5e742ad6 RK |
228 | return 0; |
229 | } | |
230 | ||
3ae5eaec | 231 | static struct platform_driver mcp_sa11x0_driver = { |
5e742ad6 RK |
232 | .probe = mcp_sa11x0_probe, |
233 | .remove = mcp_sa11x0_remove, | |
234 | .suspend = mcp_sa11x0_suspend, | |
235 | .resume = mcp_sa11x0_resume, | |
3ae5eaec | 236 | .driver = { |
c4592ce4 RK |
237 | .name = DRIVER_NAME, |
238 | .owner = THIS_MODULE, | |
3ae5eaec | 239 | }, |
5e742ad6 RK |
240 | }; |
241 | ||
242 | /* | |
243 | * This needs re-working | |
244 | */ | |
65349d60 | 245 | module_platform_driver(mcp_sa11x0_driver); |
5e742ad6 | 246 | |
c4592ce4 | 247 | MODULE_ALIAS("platform:" DRIVER_NAME); |
5e742ad6 RK |
248 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); |
249 | MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); | |
250 | MODULE_LICENSE("GPL"); |