Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/mfd/mcp-sa11x0.c
15109 views
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/platform_device.h>
21
#include <linux/mfd/mcp.h>
22
23
#include <mach/dma.h>
24
#include <mach/hardware.h>
25
#include <asm/mach-types.h>
26
#include <asm/system.h>
27
#include <mach/mcp.h>
28
29
#include <mach/assabet.h>
30
31
32
struct mcp_sa11x0 {
33
u32 mccr0;
34
u32 mccr1;
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
{
42
unsigned int mccr0;
43
44
divisor /= 32;
45
46
mccr0 = Ser4MCCR0 & ~0x00007f00;
47
mccr0 |= divisor << 8;
48
Ser4MCCR0 = mccr0;
49
}
50
51
static void
52
mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
53
{
54
unsigned int mccr0;
55
56
divisor /= 32;
57
58
mccr0 = Ser4MCCR0 & ~0x0000007f;
59
mccr0 |= divisor;
60
Ser4MCCR0 = mccr0;
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
75
Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
76
77
for (i = 0; i < 2; i++) {
78
udelay(mcp->rw_timeout);
79
if (Ser4MCSR & MCSR_CWC) {
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
101
Ser4MCDR2 = reg << 17 | MCDR2_Rd;
102
103
for (i = 0; i < 2; i++) {
104
udelay(mcp->rw_timeout);
105
if (Ser4MCSR & MCSR_CRC) {
106
ret = Ser4MCDR2 & 0xffff;
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
{
119
Ser4MCSR = -1;
120
Ser4MCCR0 |= MCCR0_MCE;
121
}
122
123
static void mcp_sa11x0_disable(struct mcp *mcp)
124
{
125
Ser4MCCR0 &= ~MCCR0_MCE;
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
140
static int mcp_sa11x0_probe(struct platform_device *pdev)
141
{
142
struct mcp_plat_data *data = pdev->dev.platform_data;
143
struct mcp *mcp;
144
int ret;
145
146
if (!data)
147
return -ENODEV;
148
149
if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
150
return -EBUSY;
151
152
mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
153
if (!mcp) {
154
ret = -ENOMEM;
155
goto release;
156
}
157
158
mcp->owner = THIS_MODULE;
159
mcp->ops = &mcp_sa11x0;
160
mcp->sclk_rate = data->sclk_rate;
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;
165
mcp->gpio_base = data->gpio_base;
166
167
platform_set_drvdata(pdev, mcp);
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
182
/*
183
* Initialise device. Note that we initially
184
* set the sampling rate to minimum.
185
*/
186
Ser4MCSR = -1;
187
Ser4MCCR1 = data->mccr1;
188
Ser4MCCR0 = data->mccr0 | 0x7f7f;
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);
204
platform_set_drvdata(pdev, NULL);
205
206
out:
207
return ret;
208
}
209
210
static int mcp_sa11x0_remove(struct platform_device *dev)
211
{
212
struct mcp *mcp = platform_get_drvdata(dev);
213
214
platform_set_drvdata(dev, NULL);
215
mcp_host_unregister(mcp);
216
release_mem_region(0x80060000, 0x60);
217
218
return 0;
219
}
220
221
static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
222
{
223
struct mcp *mcp = platform_get_drvdata(dev);
224
225
priv(mcp)->mccr0 = Ser4MCCR0;
226
priv(mcp)->mccr1 = Ser4MCCR1;
227
Ser4MCCR0 &= ~MCCR0_MCE;
228
229
return 0;
230
}
231
232
static int mcp_sa11x0_resume(struct platform_device *dev)
233
{
234
struct mcp *mcp = platform_get_drvdata(dev);
235
236
Ser4MCCR1 = priv(mcp)->mccr1;
237
Ser4MCCR0 = priv(mcp)->mccr0;
238
239
return 0;
240
}
241
242
/*
243
* The driver for the SA11x0 MCP port.
244
*/
245
MODULE_ALIAS("platform:sa11x0-mcp");
246
247
static struct platform_driver mcp_sa11x0_driver = {
248
.probe = mcp_sa11x0_probe,
249
.remove = mcp_sa11x0_remove,
250
.suspend = mcp_sa11x0_suspend,
251
.resume = mcp_sa11x0_resume,
252
.driver = {
253
.name = "sa11x0-mcp",
254
},
255
};
256
257
/*
258
* This needs re-working
259
*/
260
static int __init mcp_sa11x0_init(void)
261
{
262
return platform_driver_register(&mcp_sa11x0_driver);
263
}
264
265
static void __exit mcp_sa11x0_exit(void)
266
{
267
platform_driver_unregister(&mcp_sa11x0_driver);
268
}
269
270
module_init(mcp_sa11x0_init);
271
module_exit(mcp_sa11x0_exit);
272
273
MODULE_AUTHOR("Russell King <[email protected]>");
274
MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
275
MODULE_LICENSE("GPL");
276
277