Blame view
drivers/mfd/mcp-sa11x0.c
6.87 KB
84a14ae8c
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
5e742ad66
|
2 3 4 5 6 |
/* * linux/drivers/mfd/mcp-sa11x0.c * * Copyright (C) 2001-2005 Russell King * |
5e742ad66
|
7 8 9 10 11 |
* SA11x0 MCP (Multimedia Communications Port) driver. * * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. */ #include <linux/module.h> |
45c7f75fd
|
12 |
#include <linux/io.h> |
5e742ad66
|
13 14 15 16 |
#include <linux/errno.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/spinlock.h> |
d052d1bef
|
17 |
#include <linux/platform_device.h> |
2796e3973
|
18 |
#include <linux/pm.h> |
c8602edf3
|
19 |
#include <linux/mfd/mcp.h> |
5e742ad66
|
20 |
|
a09e64fbc
|
21 |
#include <mach/hardware.h> |
5e742ad66
|
22 |
#include <asm/mach-types.h> |
a1fd844c6
|
23 |
#include <linux/platform_data/mfd-mcp-sa11x0.h> |
5e742ad66
|
24 |
|
c4592ce4e
|
25 |
#define DRIVER_NAME "sa11x0-mcp" |
5e742ad66
|
26 27 |
struct mcp_sa11x0 { |
45c7f75fd
|
28 29 30 31 |
void __iomem *base0; void __iomem *base1; u32 mccr0; u32 mccr1; |
5e742ad66
|
32 |
}; |
45c7f75fd
|
33 34 35 36 37 38 39 |
/* Register offsets */ #define MCCR0(m) ((m)->base0 + 0x00) #define MCDR0(m) ((m)->base0 + 0x08) #define MCDR1(m) ((m)->base0 + 0x0c) #define MCDR2(m) ((m)->base0 + 0x10) #define MCSR(m) ((m)->base0 + 0x18) #define MCCR1(m) ((m)->base1 + 0x00) |
5e742ad66
|
40 41 42 43 44 |
#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp)) static void mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) { |
45c7f75fd
|
45 |
struct mcp_sa11x0 *m = priv(mcp); |
5e742ad66
|
46 47 |
divisor /= 32; |
45c7f75fd
|
48 49 50 |
m->mccr0 &= ~0x00007f00; m->mccr0 |= divisor << 8; writel_relaxed(m->mccr0, MCCR0(m)); |
5e742ad66
|
51 52 53 54 55 |
} static void mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor) { |
45c7f75fd
|
56 |
struct mcp_sa11x0 *m = priv(mcp); |
5e742ad66
|
57 58 |
divisor /= 32; |
45c7f75fd
|
59 60 61 |
m->mccr0 &= ~0x0000007f; m->mccr0 |= divisor; writel_relaxed(m->mccr0, MCCR0(m)); |
5e742ad66
|
62 63 64 65 66 67 68 69 70 71 72 |
} /* * Write data to the device. The bit should be set after 3 subframe * times (each frame is 64 clocks). We wait a maximum of 6 subframes. * We really should try doing something more productive while we * wait. */ static void mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val) { |
45c7f75fd
|
73 |
struct mcp_sa11x0 *m = priv(mcp); |
5e742ad66
|
74 75 |
int ret = -ETIME; int i; |
45c7f75fd
|
76 |
writel_relaxed(reg << 17 | MCDR2_Wr | (val & 0xffff), MCDR2(m)); |
5e742ad66
|
77 78 79 |
for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); |
45c7f75fd
|
80 |
if (readl_relaxed(MCSR(m)) & MCSR_CWC) { |
5e742ad66
|
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
ret = 0; break; } } if (ret < 0) printk(KERN_WARNING "mcp: write timed out "); } /* * Read data from the device. The bit should be set after 3 subframe * times (each frame is 64 clocks). We wait a maximum of 6 subframes. * We really should try doing something more productive while we * wait. */ static unsigned int mcp_sa11x0_read(struct mcp *mcp, unsigned int reg) { |
45c7f75fd
|
100 |
struct mcp_sa11x0 *m = priv(mcp); |
5e742ad66
|
101 102 |
int ret = -ETIME; int i; |
45c7f75fd
|
103 |
writel_relaxed(reg << 17 | MCDR2_Rd, MCDR2(m)); |
5e742ad66
|
104 105 106 |
for (i = 0; i < 2; i++) { udelay(mcp->rw_timeout); |
45c7f75fd
|
107 108 |
if (readl_relaxed(MCSR(m)) & MCSR_CRC) { ret = readl_relaxed(MCDR2(m)) & 0xffff; |
5e742ad66
|
109 110 111 112 113 114 115 116 117 118 119 120 121 |
break; } } if (ret < 0) printk(KERN_WARNING "mcp: read timed out "); return ret; } static void mcp_sa11x0_enable(struct mcp *mcp) { |
45c7f75fd
|
122 123 124 125 126 |
struct mcp_sa11x0 *m = priv(mcp); writel(-1, MCSR(m)); m->mccr0 |= MCCR0_MCE; writel_relaxed(m->mccr0, MCCR0(m)); |
5e742ad66
|
127 128 129 130 |
} static void mcp_sa11x0_disable(struct mcp *mcp) { |
45c7f75fd
|
131 132 133 134 |
struct mcp_sa11x0 *m = priv(mcp); m->mccr0 &= ~MCCR0_MCE; writel_relaxed(m->mccr0, MCCR0(m)); |
5e742ad66
|
135 136 137 138 139 140 141 142 143 144 145 146 147 |
} /* * Our methods. */ static struct mcp_ops mcp_sa11x0 = { .set_telecom_divisor = mcp_sa11x0_set_telecom_divisor, .set_audio_divisor = mcp_sa11x0_set_audio_divisor, .reg_write = mcp_sa11x0_write, .reg_read = mcp_sa11x0_read, .enable = mcp_sa11x0_enable, .disable = mcp_sa11x0_disable, }; |
45c7f75fd
|
148 |
static int mcp_sa11x0_probe(struct platform_device *dev) |
5e742ad66
|
149 |
{ |
334a41ce9
|
150 |
struct mcp_plat_data *data = dev_get_platdata(&dev->dev); |
45c7f75fd
|
151 152 |
struct resource *mem0, *mem1; struct mcp_sa11x0 *m; |
5e742ad66
|
153 154 |
struct mcp *mcp; int ret; |
323cdfc19
|
155 |
if (!data) |
5e742ad66
|
156 |
return -ENODEV; |
45c7f75fd
|
157 158 159 160 161 162 163 164 165 166 |
mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); if (!mem0 || !mem1) return -ENXIO; if (!request_mem_region(mem0->start, resource_size(mem0), DRIVER_NAME)) { ret = -EBUSY; goto err_mem0; } |
5e742ad66
|
167 |
|
45c7f75fd
|
168 169 170 171 172 173 174 |
if (!request_mem_region(mem1->start, resource_size(mem1), DRIVER_NAME)) { ret = -EBUSY; goto err_mem1; } mcp = mcp_host_alloc(&dev->dev, sizeof(struct mcp_sa11x0)); |
5e742ad66
|
175 176 |
if (!mcp) { ret = -ENOMEM; |
45c7f75fd
|
177 |
goto err_alloc; |
5e742ad66
|
178 179 180 181 |
} mcp->owner = THIS_MODULE; mcp->ops = &mcp_sa11x0; |
323cdfc19
|
182 |
mcp->sclk_rate = data->sclk_rate; |
5e742ad66
|
183 |
|
45c7f75fd
|
184 185 186 187 188 189 190 191 192 193 194 195 |
m = priv(mcp); m->mccr0 = data->mccr0 | 0x7f7f; m->mccr1 = data->mccr1; m->base0 = ioremap(mem0->start, resource_size(mem0)); m->base1 = ioremap(mem1->start, resource_size(mem1)); if (!m->base0 || !m->base1) { ret = -ENOMEM; goto err_ioremap; } platform_set_drvdata(dev, mcp); |
5e742ad66
|
196 |
|
216f63c41
|
197 |
/* |
323cdfc19
|
198 199 200 |
* Initialise device. Note that we initially * set the sampling rate to minimum. */ |
45c7f75fd
|
201 202 203 |
writel_relaxed(-1, MCSR(m)); writel_relaxed(m->mccr1, MCCR1(m)); writel_relaxed(m->mccr0, MCCR0(m)); |
5e742ad66
|
204 205 206 207 208 209 210 211 |
/* * Calculate the read/write timeout (us) from the bit clock * rate. This is the period for 3 64-bit frames. Always * round this time up. */ mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / mcp->sclk_rate; |
abe06082d
|
212 |
ret = mcp_host_add(mcp, data->codec_pdata); |
5e742ad66
|
213 |
if (ret == 0) |
45c7f75fd
|
214 |
return 0; |
5e742ad66
|
215 |
|
45c7f75fd
|
216 217 218 219 220 221 222 223 224 |
err_ioremap: iounmap(m->base1); iounmap(m->base0); mcp_host_free(mcp); err_alloc: release_mem_region(mem1->start, resource_size(mem1)); err_mem1: release_mem_region(mem0->start, resource_size(mem0)); err_mem0: |
5e742ad66
|
225 226 |
return ret; } |
216f63c41
|
227 |
static int mcp_sa11x0_remove(struct platform_device *dev) |
5e742ad66
|
228 |
{ |
216f63c41
|
229 |
struct mcp *mcp = platform_get_drvdata(dev); |
45c7f75fd
|
230 231 |
struct mcp_sa11x0 *m = priv(mcp); struct resource *mem0, *mem1; |
a4b54acf9
|
232 233 234 235 |
if (m->mccr0 & MCCR0_MCE) dev_warn(&dev->dev, "device left active (missing disable call?) "); |
45c7f75fd
|
236 237 |
mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); |
5e742ad66
|
238 |
|
30816ac04
|
239 |
mcp_host_del(mcp); |
45c7f75fd
|
240 241 |
iounmap(m->base1); iounmap(m->base0); |
30816ac04
|
242 |
mcp_host_free(mcp); |
45c7f75fd
|
243 244 |
release_mem_region(mem1->start, resource_size(mem1)); release_mem_region(mem0->start, resource_size(mem0)); |
5e742ad66
|
245 246 247 |
return 0; } |
2796e3973
|
248 249 |
#ifdef CONFIG_PM_SLEEP static int mcp_sa11x0_suspend(struct device *dev) |
5e742ad66
|
250 |
{ |
2796e3973
|
251 |
struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); |
5e742ad66
|
252 |
|
a4b54acf9
|
253 254 255 |
if (m->mccr0 & MCCR0_MCE) dev_warn(dev, "device left active (missing disable call?) "); |
45c7f75fd
|
256 |
writel(m->mccr0 & ~MCCR0_MCE, MCCR0(m)); |
9480e307c
|
257 |
|
5e742ad66
|
258 259 |
return 0; } |
2796e3973
|
260 |
static int mcp_sa11x0_resume(struct device *dev) |
5e742ad66
|
261 |
{ |
2796e3973
|
262 |
struct mcp_sa11x0 *m = priv(dev_get_drvdata(dev)); |
5e742ad66
|
263 |
|
45c7f75fd
|
264 265 |
writel_relaxed(m->mccr1, MCCR1(m)); writel_relaxed(m->mccr0, MCCR0(m)); |
9480e307c
|
266 |
|
5e742ad66
|
267 268 |
return 0; } |
2796e3973
|
269 270 271 |
#endif static const struct dev_pm_ops mcp_sa11x0_pm_ops = { |
a6aecae29
|
272 273 274 275 276 277 278 279 |
#ifdef CONFIG_PM_SLEEP .suspend = mcp_sa11x0_suspend, .freeze = mcp_sa11x0_suspend, .poweroff = mcp_sa11x0_suspend, .resume_noirq = mcp_sa11x0_resume, .thaw_noirq = mcp_sa11x0_resume, .restore_noirq = mcp_sa11x0_resume, #endif |
2796e3973
|
280 |
}; |
5e742ad66
|
281 |
|
3ae5eaec1
|
282 |
static struct platform_driver mcp_sa11x0_driver = { |
5e742ad66
|
283 284 |
.probe = mcp_sa11x0_probe, .remove = mcp_sa11x0_remove, |
3ae5eaec1
|
285 |
.driver = { |
c4592ce4e
|
286 |
.name = DRIVER_NAME, |
2796e3973
|
287 |
.pm = &mcp_sa11x0_pm_ops, |
3ae5eaec1
|
288 |
}, |
5e742ad66
|
289 290 291 292 293 |
}; /* * This needs re-working */ |
65349d60d
|
294 |
module_platform_driver(mcp_sa11x0_driver); |
5e742ad66
|
295 |
|
c4592ce4e
|
296 |
MODULE_ALIAS("platform:" DRIVER_NAME); |
5e742ad66
|
297 298 299 |
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); MODULE_LICENSE("GPL"); |