xref: /OK3568_Linux_fs/kernel/drivers/mfd/mcp-core.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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