Commit a4e137ab1447fc5009f21e257971aa60a9ec98fb
Committed by
Russell King
1 parent
099d44e869
Exists in
master
and in
39 other branches
[MFD] Add multimedia communication port core support
Add support for the core of the multimedia communication port framework. This is a port used to communicate with devices with two DMA paths and a control path. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Showing 7 changed files with 341 additions and 1 deletions Side-by-side Diff
arch/arm/Kconfig
drivers/Kconfig
drivers/Makefile
... | ... | @@ -26,7 +26,7 @@ |
26 | 26 | obj-$(CONFIG_SERIO) += input/serio/ |
27 | 27 | obj-y += serial/ |
28 | 28 | obj-$(CONFIG_PARPORT) += parport/ |
29 | -obj-y += base/ block/ misc/ net/ media/ | |
29 | +obj-y += base/ block/ misc/ mfd/ net/ media/ | |
30 | 30 | obj-$(CONFIG_NUBUS) += nubus/ |
31 | 31 | obj-$(CONFIG_ATM) += atm/ |
32 | 32 | obj-$(CONFIG_PPC_PMAC) += macintosh/ |
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/mcp-core.c
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 | + | |
27 | +static int mcp_bus_match(struct device *dev, struct device_driver *drv) | |
28 | +{ | |
29 | + return 1; | |
30 | +} | |
31 | + | |
32 | +static 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 | + | |
40 | +static 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 | + | |
49 | +static 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 | + | |
62 | +static 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 | + | |
75 | +static 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 | + */ | |
90 | +void 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 | +} | |
96 | +EXPORT_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 | + */ | |
105 | +void 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 | +} | |
111 | +EXPORT_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 | + */ | |
122 | +void 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 | +} | |
130 | +EXPORT_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 | + */ | |
140 | +unsigned 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 | +} | |
151 | +EXPORT_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 | + */ | |
160 | +void 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 | +} | |
167 | +EXPORT_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 | + */ | |
177 | +void 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 | +} | |
186 | +EXPORT_SYMBOL(mcp_disable); | |
187 | + | |
188 | +static void mcp_release(struct device *dev) | |
189 | +{ | |
190 | + struct mcp *mcp = container_of(dev, struct mcp, attached_device); | |
191 | + | |
192 | + kfree(mcp); | |
193 | +} | |
194 | + | |
195 | +struct 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 | +} | |
210 | +EXPORT_SYMBOL(mcp_host_alloc); | |
211 | + | |
212 | +int mcp_host_register(struct mcp *mcp) | |
213 | +{ | |
214 | + strcpy(mcp->attached_device.bus_id, "mcp0"); | |
215 | + return device_register(&mcp->attached_device); | |
216 | +} | |
217 | +EXPORT_SYMBOL(mcp_host_register); | |
218 | + | |
219 | +void mcp_host_unregister(struct mcp *mcp) | |
220 | +{ | |
221 | + device_unregister(&mcp->attached_device); | |
222 | +} | |
223 | +EXPORT_SYMBOL(mcp_host_unregister); | |
224 | + | |
225 | +int 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 | +} | |
232 | +EXPORT_SYMBOL(mcp_driver_register); | |
233 | + | |
234 | +void mcp_driver_unregister(struct mcp_driver *mcpdrv) | |
235 | +{ | |
236 | + driver_unregister(&mcpdrv->drv); | |
237 | +} | |
238 | +EXPORT_SYMBOL(mcp_driver_unregister); | |
239 | + | |
240 | +static int __init mcp_init(void) | |
241 | +{ | |
242 | + return bus_register(&mcp_bus_type); | |
243 | +} | |
244 | + | |
245 | +static void __exit mcp_exit(void) | |
246 | +{ | |
247 | + bus_unregister(&mcp_bus_type); | |
248 | +} | |
249 | + | |
250 | +module_init(mcp_init); | |
251 | +module_exit(mcp_exit); | |
252 | + | |
253 | +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
254 | +MODULE_DESCRIPTION("Core multimedia communications port driver"); | |
255 | +MODULE_LICENSE("GPL"); |
drivers/mfd/mcp.h
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 | + | |
13 | +struct mcp_ops; | |
14 | + | |
15 | +struct 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 | + | |
29 | +struct 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 | + | |
38 | +void mcp_set_telecom_divisor(struct mcp *, unsigned int); | |
39 | +void mcp_set_audio_divisor(struct mcp *, unsigned int); | |
40 | +void mcp_reg_write(struct mcp *, unsigned int, unsigned int); | |
41 | +unsigned int mcp_reg_read(struct mcp *, unsigned int); | |
42 | +void mcp_enable(struct mcp *); | |
43 | +void mcp_disable(struct mcp *); | |
44 | +#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) | |
45 | + | |
46 | +struct mcp *mcp_host_alloc(struct device *, size_t); | |
47 | +int mcp_host_register(struct mcp *); | |
48 | +void mcp_host_unregister(struct mcp *); | |
49 | + | |
50 | +struct 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 | + | |
58 | +int mcp_driver_register(struct mcp_driver *); | |
59 | +void 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 |