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