aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/mfd/Kconfig16
-rw-r--r--drivers/mfd/Makefile6
-rw-r--r--drivers/mfd/mcp-core.c255
-rw-r--r--drivers/mfd/mcp-sa11x0.c275
-rw-r--r--drivers/mfd/mcp.h66
7 files changed, 621 insertions, 1 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index cecab0acc3fe..46d655fab115 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -48,6 +48,8 @@ source "drivers/hwmon/Kconfig"
48 48
49source "drivers/misc/Kconfig" 49source "drivers/misc/Kconfig"
50 50
51source "drivers/mfd/Kconfig"
52
51source "drivers/media/Kconfig" 53source "drivers/media/Kconfig"
52 54
53source "drivers/video/Kconfig" 55source "drivers/video/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 126a851d5653..9663132ed825 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -26,7 +26,7 @@ obj-$(CONFIG_FB_INTEL) += video/intelfb/
26obj-$(CONFIG_SERIO) += input/serio/ 26obj-$(CONFIG_SERIO) += input/serio/
27obj-y += serial/ 27obj-y += serial/
28obj-$(CONFIG_PARPORT) += parport/ 28obj-$(CONFIG_PARPORT) += parport/
29obj-y += base/ block/ misc/ net/ media/ 29obj-y += base/ block/ misc/ mfd/ net/ media/
30obj-$(CONFIG_NUBUS) += nubus/ 30obj-$(CONFIG_NUBUS) += nubus/
31obj-$(CONFIG_ATM) += atm/ 31obj-$(CONFIG_ATM) += atm/
32obj-$(CONFIG_PPC_PMAC) += macintosh/ 32obj-$(CONFIG_PPC_PMAC) += macintosh/
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
new file mode 100644
index 000000000000..1588a59e3767
--- /dev/null
+++ b/drivers/mfd/Kconfig
@@ -0,0 +1,16 @@
1#
2# Multifunction miscellaneous devices
3#
4
5menu "Multimedia Capabilities Port drivers"
6
7config MCP
8 tristate
9
10# Interface drivers
11config MCP_SA11X0
12 tristate "Support SA11x0 MCP interface"
13 depends on ARCH_SA1100
14 select MCP
15
16endmenu
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
new file mode 100644
index 000000000000..98bdd6a42188
--- /dev/null
+++ b/drivers/mfd/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for multifunction miscellaneous devices
3#
4
5obj-$(CONFIG_MCP) += mcp-core.o
6obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
diff --git a/drivers/mfd/mcp-core.c b/drivers/mfd/mcp-core.c
new file mode 100644
index 000000000000..c75d713c01e4
--- /dev/null
+++ b/drivers/mfd/mcp-core.c
@@ -0,0 +1,255 @@
1/*
2 * linux/drivers/mfd/mcp-core.c
3 *
4 * Copyright (C) 2001 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 * Generic MCP (Multimedia Communications Port) layer. All MCP locking
11 * is solely held within this file.
12 */
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/errno.h>
16#include <linux/smp.h>
17#include <linux/device.h>
18
19#include <asm/dma.h>
20#include <asm/system.h>
21
22#include "mcp.h"
23
24#define to_mcp(d) container_of(d, struct mcp, attached_device)
25#define to_mcp_driver(d) container_of(d, struct mcp_driver, drv)
26
27static int mcp_bus_match(struct device *dev, struct device_driver *drv)
28{
29 return 1;
30}
31
32static int mcp_bus_probe(struct device *dev)
33{
34 struct mcp *mcp = to_mcp(dev);
35 struct mcp_driver *drv = to_mcp_driver(dev->driver);
36
37 return drv->probe(mcp);
38}
39
40static int mcp_bus_remove(struct device *dev)
41{
42 struct mcp *mcp = to_mcp(dev);
43 struct mcp_driver *drv = to_mcp_driver(dev->driver);
44
45 drv->remove(mcp);
46 return 0;
47}
48
49static int mcp_bus_suspend(struct device *dev, pm_message_t state)
50{
51 struct mcp *mcp = to_mcp(dev);
52 int ret = 0;
53
54 if (dev->driver) {
55 struct mcp_driver *drv = to_mcp_driver(dev->driver);
56
57 ret = drv->suspend(mcp, state);
58 }
59 return ret;
60}
61
62static int mcp_bus_resume(struct device *dev)
63{
64 struct mcp *mcp = to_mcp(dev);
65 int ret = 0;
66
67 if (dev->driver) {
68 struct mcp_driver *drv = to_mcp_driver(dev->driver);
69
70 ret = drv->resume(mcp);
71 }
72 return ret;
73}
74
75static struct bus_type mcp_bus_type = {
76 .name = "mcp",
77 .match = mcp_bus_match,
78 .suspend = mcp_bus_suspend,
79 .resume = mcp_bus_resume,
80};
81
82/**
83 * mcp_set_telecom_divisor - set the telecom divisor
84 * @mcp: MCP interface structure
85 * @div: SIB clock divisor
86 *
87 * Set the telecom divisor on the MCP interface. The resulting
88 * sample rate is SIBCLOCK/div.
89 */
90void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div)
91{
92 spin_lock_irq(&mcp->lock);
93 mcp->ops->set_telecom_divisor(mcp, div);
94 spin_unlock_irq(&mcp->lock);
95}
96EXPORT_SYMBOL(mcp_set_telecom_divisor);
97
98/**
99 * mcp_set_audio_divisor - set the audio divisor
100 * @mcp: MCP interface structure
101 * @div: SIB clock divisor
102 *
103 * Set the audio divisor on the MCP interface.
104 */
105void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div)
106{
107 spin_lock_irq(&mcp->lock);
108 mcp->ops->set_audio_divisor(mcp, div);
109 spin_unlock_irq(&mcp->lock);
110}
111EXPORT_SYMBOL(mcp_set_audio_divisor);
112
113/**
114 * mcp_reg_write - write a device register
115 * @mcp: MCP interface structure
116 * @reg: 4-bit register index
117 * @val: 16-bit data value
118 *
119 * Write a device register. The MCP interface must be enabled
120 * to prevent this function hanging.
121 */
122void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val)
123{
124 unsigned long flags;
125
126 spin_lock_irqsave(&mcp->lock, flags);
127 mcp->ops->reg_write(mcp, reg, val);
128 spin_unlock_irqrestore(&mcp->lock, flags);
129}
130EXPORT_SYMBOL(mcp_reg_write);
131
132/**
133 * mcp_reg_read - read a device register
134 * @mcp: MCP interface structure
135 * @reg: 4-bit register index
136 *
137 * Read a device register and return its value. The MCP interface
138 * must be enabled to prevent this function hanging.
139 */
140unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg)
141{
142 unsigned long flags;
143 unsigned int val;
144
145 spin_lock_irqsave(&mcp->lock, flags);
146 val = mcp->ops->reg_read(mcp, reg);
147 spin_unlock_irqrestore(&mcp->lock, flags);
148
149 return val;
150}
151EXPORT_SYMBOL(mcp_reg_read);
152
153/**
154 * mcp_enable - enable the MCP interface
155 * @mcp: MCP interface to enable
156 *
157 * Enable the MCP interface. Each call to mcp_enable will need
158 * a corresponding call to mcp_disable to disable the interface.
159 */
160void mcp_enable(struct mcp *mcp)
161{
162 spin_lock_irq(&mcp->lock);
163 if (mcp->use_count++ == 0)
164 mcp->ops->enable(mcp);
165 spin_unlock_irq(&mcp->lock);
166}
167EXPORT_SYMBOL(mcp_enable);
168
169/**
170 * mcp_disable - disable the MCP interface
171 * @mcp: MCP interface to disable
172 *
173 * Disable the MCP interface. The MCP interface will only be
174 * disabled once the number of calls to mcp_enable matches the
175 * number of calls to mcp_disable.
176 */
177void mcp_disable(struct mcp *mcp)
178{
179 unsigned long flags;
180
181 spin_lock_irqsave(&mcp->lock, flags);
182 if (--mcp->use_count == 0)
183 mcp->ops->disable(mcp);
184 spin_unlock_irqrestore(&mcp->lock, flags);
185}
186EXPORT_SYMBOL(mcp_disable);
187
188static void mcp_release(struct device *dev)
189{
190 struct mcp *mcp = container_of(dev, struct mcp, attached_device);
191
192 kfree(mcp);
193}
194
195struct mcp *mcp_host_alloc(struct device *parent, size_t size)
196{
197 struct mcp *mcp;
198
199 mcp = kmalloc(sizeof(struct mcp) + size, GFP_KERNEL);
200 if (mcp) {
201 memset(mcp, 0, sizeof(struct mcp) + size);
202 spin_lock_init(&mcp->lock);
203 mcp->attached_device.parent = parent;
204 mcp->attached_device.bus = &mcp_bus_type;
205 mcp->attached_device.dma_mask = parent->dma_mask;
206 mcp->attached_device.release = mcp_release;
207 }
208 return mcp;
209}
210EXPORT_SYMBOL(mcp_host_alloc);
211
212int mcp_host_register(struct mcp *mcp)
213{
214 strcpy(mcp->attached_device.bus_id, "mcp0");
215 return device_register(&mcp->attached_device);
216}
217EXPORT_SYMBOL(mcp_host_register);
218
219void mcp_host_unregister(struct mcp *mcp)
220{
221 device_unregister(&mcp->attached_device);
222}
223EXPORT_SYMBOL(mcp_host_unregister);
224
225int mcp_driver_register(struct mcp_driver *mcpdrv)
226{
227 mcpdrv->drv.bus = &mcp_bus_type;
228 mcpdrv->drv.probe = mcp_bus_probe;
229 mcpdrv->drv.remove = mcp_bus_remove;
230 return driver_register(&mcpdrv->drv);
231}
232EXPORT_SYMBOL(mcp_driver_register);
233
234void mcp_driver_unregister(struct mcp_driver *mcpdrv)
235{
236 driver_unregister(&mcpdrv->drv);
237}
238EXPORT_SYMBOL(mcp_driver_unregister);
239
240static int __init mcp_init(void)
241{
242 return bus_register(&mcp_bus_type);
243}
244
245static void __exit mcp_exit(void)
246{
247 bus_unregister(&mcp_bus_type);
248}
249
250module_init(mcp_init);
251module_exit(mcp_exit);
252
253MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
254MODULE_DESCRIPTION("Core multimedia communications port driver");
255MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c
new file mode 100644
index 000000000000..e9806fbbe696
--- /dev/null
+++ b/drivers/mfd/mcp-sa11x0.c
@@ -0,0 +1,275 @@
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>
21#include <linux/device.h>
22
23#include <asm/dma.h>
24#include <asm/hardware.h>
25#include <asm/mach-types.h>
26#include <asm/system.h>
27#include <asm/arch/mcp.h>
28
29#include <asm/arch/assabet.h>
30
31#include "mcp.h"
32
33struct mcp_sa11x0 {
34 u32 mccr0;
35 u32 mccr1;
36};
37
38#define priv(mcp) ((struct mcp_sa11x0 *)mcp_priv(mcp))
39
40static void
41mcp_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
52static void
53mcp_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 */
70static void
71mcp_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 */
96static unsigned int
97mcp_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
118static void mcp_sa11x0_enable(struct mcp *mcp)
119{
120 Ser4MCSR = -1;
121 Ser4MCCR0 |= MCCR0_MCE;
122}
123
124static void mcp_sa11x0_disable(struct mcp *mcp)
125{
126 Ser4MCCR0 &= ~MCCR0_MCE;
127}
128
129/*
130 * Our methods.
131 */
132static 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
141static int mcp_sa11x0_probe(struct device *dev)
142{
143 struct platform_device *pdev = to_platform_device(dev);
144 struct mcp_plat_data *data = pdev->dev.platform_data;
145 struct mcp *mcp;
146 int ret;
147
148 if (!data)
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;
162 mcp->sclk_rate = data->sclk_rate;
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
183 /*
184 * Initialise device. Note that we initially
185 * set the sampling rate to minimum.
186 */
187 Ser4MCSR = -1;
188 Ser4MCCR1 = data->mccr1;
189 Ser4MCCR0 = data->mccr0 | 0x7f7f;
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
211static 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
222static int mcp_sa11x0_suspend(struct device *dev, pm_message_t state, u32 level)
223{
224 struct mcp *mcp = dev_get_drvdata(dev);
225
226 if (level == SUSPEND_DISABLE) {
227 priv(mcp)->mccr0 = Ser4MCCR0;
228 priv(mcp)->mccr1 = Ser4MCCR1;
229 Ser4MCCR0 &= ~MCCR0_MCE;
230 }
231 return 0;
232}
233
234static int mcp_sa11x0_resume(struct device *dev, u32 level)
235{
236 struct mcp *mcp = dev_get_drvdata(dev);
237
238 if (level == RESUME_RESTORE_STATE) {
239 Ser4MCCR1 = priv(mcp)->mccr1;
240 Ser4MCCR0 = priv(mcp)->mccr0;
241 }
242 return 0;
243}
244
245/*
246 * The driver for the SA11x0 MCP port.
247 */
248static struct device_driver mcp_sa11x0_driver = {
249 .name = "sa11x0-mcp",
250 .bus = &platform_bus_type,
251 .probe = mcp_sa11x0_probe,
252 .remove = mcp_sa11x0_remove,
253 .suspend = mcp_sa11x0_suspend,
254 .resume = mcp_sa11x0_resume,
255};
256
257/*
258 * This needs re-working
259 */
260static int __init mcp_sa11x0_init(void)
261{
262 return driver_register(&mcp_sa11x0_driver);
263}
264
265static void __exit mcp_sa11x0_exit(void)
266{
267 driver_unregister(&mcp_sa11x0_driver);
268}
269
270module_init(mcp_sa11x0_init);
271module_exit(mcp_sa11x0_exit);
272
273MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
274MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
275MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/mcp.h b/drivers/mfd/mcp.h
new file mode 100644
index 000000000000..c093a93b8808
--- /dev/null
+++ b/drivers/mfd/mcp.h
@@ -0,0 +1,66 @@
1/*
2 * linux/drivers/mfd/mcp.h
3 *
4 * Copyright (C) 2001 Russell King, All Rights Reserved.
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#ifndef MCP_H
11#define MCP_H
12
13struct mcp_ops;
14
15struct mcp {
16 struct module *owner;
17 struct mcp_ops *ops;
18 spinlock_t lock;
19 int use_count;
20 unsigned int sclk_rate;
21 unsigned int rw_timeout;
22 dma_device_t dma_audio_rd;
23 dma_device_t dma_audio_wr;
24 dma_device_t dma_telco_rd;
25 dma_device_t dma_telco_wr;
26 struct device attached_device;
27};
28
29struct mcp_ops {
30 void (*set_telecom_divisor)(struct mcp *, unsigned int);
31 void (*set_audio_divisor)(struct mcp *, unsigned int);
32 void (*reg_write)(struct mcp *, unsigned int, unsigned int);
33 unsigned int (*reg_read)(struct mcp *, unsigned int);
34 void (*enable)(struct mcp *);
35 void (*disable)(struct mcp *);
36};
37
38void mcp_set_telecom_divisor(struct mcp *, unsigned int);
39void mcp_set_audio_divisor(struct mcp *, unsigned int);
40void mcp_reg_write(struct mcp *, unsigned int, unsigned int);
41unsigned int mcp_reg_read(struct mcp *, unsigned int);
42void mcp_enable(struct mcp *);
43void mcp_disable(struct mcp *);
44#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate)
45
46struct mcp *mcp_host_alloc(struct device *, size_t);
47int mcp_host_register(struct mcp *);
48void mcp_host_unregister(struct mcp *);
49
50struct mcp_driver {
51 struct device_driver drv;
52 int (*probe)(struct mcp *);
53 void (*remove)(struct mcp *);
54 int (*suspend)(struct mcp *, pm_message_t);
55 int (*resume)(struct mcp *);
56};
57
58int mcp_driver_register(struct mcp_driver *);
59void mcp_driver_unregister(struct mcp_driver *);
60
61#define mcp_get_drvdata(mcp) dev_get_drvdata(&(mcp)->attached_device)
62#define mcp_set_drvdata(mcp,d) dev_set_drvdata(&(mcp)->attached_device, d)
63
64#define mcp_priv(mcp) ((void *)((mcp)+1))
65
66#endif