Commit | Line | Data |
---|---|---|
84a14ae8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a4e137ab RK |
2 | /* |
3 | * linux/drivers/mfd/mcp-core.c | |
4 | * | |
5 | * Copyright (C) 2001 Russell King | |
6 | * | |
a4e137ab RK |
7 | * Generic MCP (Multimedia Communications Port) layer. All MCP locking |
8 | * is solely held within this file. | |
9 | */ | |
10 | #include <linux/module.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/errno.h> | |
13 | #include <linux/smp.h> | |
14 | #include <linux/device.h> | |
8c65b4a6 TS |
15 | #include <linux/slab.h> |
16 | #include <linux/string.h> | |
c8602edf | 17 | #include <linux/mfd/mcp.h> |
a4e137ab | 18 | |
a4e137ab RK |
19 | |
20 | #define to_mcp(d) container_of(d, struct mcp, attached_device) | |
21 | #define to_mcp_driver(d) container_of(d, struct mcp_driver, drv) | |
22 | ||
23 | static int mcp_bus_match(struct device *dev, struct device_driver *drv) | |
24 | { | |
65f2e753 | 25 | return 1; |
a4e137ab RK |
26 | } |
27 | ||
28 | static int mcp_bus_probe(struct device *dev) | |
29 | { | |
30 | struct mcp *mcp = to_mcp(dev); | |
31 | struct mcp_driver *drv = to_mcp_driver(dev->driver); | |
32 | ||
33 | return drv->probe(mcp); | |
34 | } | |
35 | ||
36 | static int mcp_bus_remove(struct device *dev) | |
37 | { | |
38 | struct mcp *mcp = to_mcp(dev); | |
39 | struct mcp_driver *drv = to_mcp_driver(dev->driver); | |
40 | ||
41 | drv->remove(mcp); | |
42 | return 0; | |
43 | } | |
44 | ||
a4e137ab RK |
45 | static struct bus_type mcp_bus_type = { |
46 | .name = "mcp", | |
47 | .match = mcp_bus_match, | |
413b486e RK |
48 | .probe = mcp_bus_probe, |
49 | .remove = mcp_bus_remove, | |
a4e137ab RK |
50 | }; |
51 | ||
52 | /** | |
53 | * mcp_set_telecom_divisor - set the telecom divisor | |
54 | * @mcp: MCP interface structure | |
55 | * @div: SIB clock divisor | |
56 | * | |
57 | * Set the telecom divisor on the MCP interface. The resulting | |
58 | * sample rate is SIBCLOCK/div. | |
59 | */ | |
60 | void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) | |
61 | { | |
98250221 RK |
62 | unsigned long flags; |
63 | ||
64 | spin_lock_irqsave(&mcp->lock, flags); | |
a4e137ab | 65 | mcp->ops->set_telecom_divisor(mcp, div); |
98250221 | 66 | spin_unlock_irqrestore(&mcp->lock, flags); |
a4e137ab RK |
67 | } |
68 | EXPORT_SYMBOL(mcp_set_telecom_divisor); | |
69 | ||
70 | /** | |
71 | * mcp_set_audio_divisor - set the audio divisor | |
72 | * @mcp: MCP interface structure | |
73 | * @div: SIB clock divisor | |
74 | * | |
75 | * Set the audio divisor on the MCP interface. | |
76 | */ | |
77 | void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) | |
78 | { | |
98250221 RK |
79 | unsigned long flags; |
80 | ||
81 | spin_lock_irqsave(&mcp->lock, flags); | |
a4e137ab | 82 | mcp->ops->set_audio_divisor(mcp, div); |
98250221 | 83 | spin_unlock_irqrestore(&mcp->lock, flags); |
a4e137ab RK |
84 | } |
85 | EXPORT_SYMBOL(mcp_set_audio_divisor); | |
86 | ||
87 | /** | |
88 | * mcp_reg_write - write a device register | |
89 | * @mcp: MCP interface structure | |
90 | * @reg: 4-bit register index | |
91 | * @val: 16-bit data value | |
92 | * | |
93 | * Write a device register. The MCP interface must be enabled | |
94 | * to prevent this function hanging. | |
95 | */ | |
96 | void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) | |
97 | { | |
98 | unsigned long flags; | |
99 | ||
100 | spin_lock_irqsave(&mcp->lock, flags); | |
101 | mcp->ops->reg_write(mcp, reg, val); | |
102 | spin_unlock_irqrestore(&mcp->lock, flags); | |
103 | } | |
104 | EXPORT_SYMBOL(mcp_reg_write); | |
105 | ||
106 | /** | |
107 | * mcp_reg_read - read a device register | |
108 | * @mcp: MCP interface structure | |
109 | * @reg: 4-bit register index | |
110 | * | |
111 | * Read a device register and return its value. The MCP interface | |
112 | * must be enabled to prevent this function hanging. | |
113 | */ | |
114 | unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) | |
115 | { | |
116 | unsigned long flags; | |
117 | unsigned int val; | |
118 | ||
119 | spin_lock_irqsave(&mcp->lock, flags); | |
120 | val = mcp->ops->reg_read(mcp, reg); | |
121 | spin_unlock_irqrestore(&mcp->lock, flags); | |
122 | ||
123 | return val; | |
124 | } | |
125 | EXPORT_SYMBOL(mcp_reg_read); | |
126 | ||
127 | /** | |
128 | * mcp_enable - enable the MCP interface | |
129 | * @mcp: MCP interface to enable | |
130 | * | |
131 | * Enable the MCP interface. Each call to mcp_enable will need | |
132 | * a corresponding call to mcp_disable to disable the interface. | |
133 | */ | |
134 | void mcp_enable(struct mcp *mcp) | |
135 | { | |
98250221 | 136 | unsigned long flags; |
b9ac2270 | 137 | |
98250221 | 138 | spin_lock_irqsave(&mcp->lock, flags); |
a4e137ab RK |
139 | if (mcp->use_count++ == 0) |
140 | mcp->ops->enable(mcp); | |
98250221 | 141 | spin_unlock_irqrestore(&mcp->lock, flags); |
a4e137ab RK |
142 | } |
143 | EXPORT_SYMBOL(mcp_enable); | |
144 | ||
145 | /** | |
146 | * mcp_disable - disable the MCP interface | |
147 | * @mcp: MCP interface to disable | |
148 | * | |
149 | * Disable the MCP interface. The MCP interface will only be | |
150 | * disabled once the number of calls to mcp_enable matches the | |
151 | * number of calls to mcp_disable. | |
152 | */ | |
153 | void mcp_disable(struct mcp *mcp) | |
154 | { | |
155 | unsigned long flags; | |
156 | ||
157 | spin_lock_irqsave(&mcp->lock, flags); | |
158 | if (--mcp->use_count == 0) | |
159 | mcp->ops->disable(mcp); | |
160 | spin_unlock_irqrestore(&mcp->lock, flags); | |
161 | } | |
162 | EXPORT_SYMBOL(mcp_disable); | |
163 | ||
164 | static void mcp_release(struct device *dev) | |
165 | { | |
166 | struct mcp *mcp = container_of(dev, struct mcp, attached_device); | |
167 | ||
168 | kfree(mcp); | |
169 | } | |
170 | ||
171 | struct mcp *mcp_host_alloc(struct device *parent, size_t size) | |
172 | { | |
173 | struct mcp *mcp; | |
174 | ||
dd00cc48 | 175 | mcp = kzalloc(sizeof(struct mcp) + size, GFP_KERNEL); |
a4e137ab | 176 | if (mcp) { |
a4e137ab | 177 | spin_lock_init(&mcp->lock); |
30816ac0 | 178 | device_initialize(&mcp->attached_device); |
a4e137ab RK |
179 | mcp->attached_device.parent = parent; |
180 | mcp->attached_device.bus = &mcp_bus_type; | |
181 | mcp->attached_device.dma_mask = parent->dma_mask; | |
182 | mcp->attached_device.release = mcp_release; | |
183 | } | |
184 | return mcp; | |
185 | } | |
186 | EXPORT_SYMBOL(mcp_host_alloc); | |
187 | ||
abe06082 | 188 | int mcp_host_add(struct mcp *mcp, void *pdata) |
a4e137ab | 189 | { |
abe06082 | 190 | mcp->attached_device.platform_data = pdata; |
b2bf61f2 | 191 | dev_set_name(&mcp->attached_device, "mcp0"); |
30816ac0 | 192 | return device_add(&mcp->attached_device); |
a4e137ab | 193 | } |
30816ac0 | 194 | EXPORT_SYMBOL(mcp_host_add); |
a4e137ab | 195 | |
30816ac0 | 196 | void mcp_host_del(struct mcp *mcp) |
a4e137ab | 197 | { |
30816ac0 | 198 | device_del(&mcp->attached_device); |
a4e137ab | 199 | } |
30816ac0 RK |
200 | EXPORT_SYMBOL(mcp_host_del); |
201 | ||
202 | void mcp_host_free(struct mcp *mcp) | |
203 | { | |
204 | put_device(&mcp->attached_device); | |
205 | } | |
206 | EXPORT_SYMBOL(mcp_host_free); | |
a4e137ab RK |
207 | |
208 | int mcp_driver_register(struct mcp_driver *mcpdrv) | |
209 | { | |
210 | mcpdrv->drv.bus = &mcp_bus_type; | |
a4e137ab RK |
211 | return driver_register(&mcpdrv->drv); |
212 | } | |
213 | EXPORT_SYMBOL(mcp_driver_register); | |
214 | ||
215 | void mcp_driver_unregister(struct mcp_driver *mcpdrv) | |
216 | { | |
217 | driver_unregister(&mcpdrv->drv); | |
218 | } | |
219 | EXPORT_SYMBOL(mcp_driver_unregister); | |
220 | ||
221 | static int __init mcp_init(void) | |
222 | { | |
223 | return bus_register(&mcp_bus_type); | |
224 | } | |
225 | ||
226 | static void __exit mcp_exit(void) | |
227 | { | |
228 | bus_unregister(&mcp_bus_type); | |
229 | } | |
230 | ||
231 | module_init(mcp_init); | |
232 | module_exit(mcp_exit); | |
233 | ||
234 | MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); | |
235 | MODULE_DESCRIPTION("Core multimedia communications port driver"); | |
236 | MODULE_LICENSE("GPL"); |