1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright(c) 2015-17 Intel Corporation. |
3 | |
4 | #include <linux/module.h> |
5 | #include <linux/mod_devicetable.h> |
6 | #include <linux/pm_domain.h> |
7 | #include <linux/soundwire/sdw.h> |
8 | #include <linux/soundwire/sdw_type.h> |
9 | #include "bus.h" |
10 | #include "irq.h" |
11 | #include "sysfs_local.h" |
12 | |
13 | /** |
14 | * sdw_get_device_id - find the matching SoundWire device id |
15 | * @slave: SoundWire Slave Device |
16 | * @drv: SoundWire Slave Driver |
17 | * |
18 | * The match is done by comparing the mfg_id and part_id from the |
19 | * struct sdw_device_id. |
20 | */ |
21 | static const struct sdw_device_id * |
22 | sdw_get_device_id(struct sdw_slave *slave, struct sdw_driver *drv) |
23 | { |
24 | const struct sdw_device_id *id; |
25 | |
26 | for (id = drv->id_table; id && id->mfg_id; id++) |
27 | if (slave->id.mfg_id == id->mfg_id && |
28 | slave->id.part_id == id->part_id && |
29 | (!id->sdw_version || |
30 | slave->id.sdw_version == id->sdw_version) && |
31 | (!id->class_id || |
32 | slave->id.class_id == id->class_id)) |
33 | return id; |
34 | |
35 | return NULL; |
36 | } |
37 | |
38 | static int sdw_bus_match(struct device *dev, struct device_driver *ddrv) |
39 | { |
40 | struct sdw_slave *slave; |
41 | struct sdw_driver *drv; |
42 | int ret = 0; |
43 | |
44 | if (is_sdw_slave(dev)) { |
45 | slave = dev_to_sdw_dev(dev); |
46 | drv = drv_to_sdw_driver(ddrv); |
47 | |
48 | ret = !!sdw_get_device_id(slave, drv); |
49 | } |
50 | return ret; |
51 | } |
52 | |
53 | int sdw_slave_modalias(const struct sdw_slave *slave, char *buf, size_t size) |
54 | { |
55 | /* modalias is sdw:m<mfg_id>p<part_id>v<version>c<class_id> */ |
56 | |
57 | return snprintf(buf, size, fmt: "sdw:m%04Xp%04Xv%02Xc%02X\n" , |
58 | slave->id.mfg_id, slave->id.part_id, |
59 | slave->id.sdw_version, slave->id.class_id); |
60 | } |
61 | |
62 | int sdw_slave_uevent(const struct device *dev, struct kobj_uevent_env *env) |
63 | { |
64 | const struct sdw_slave *slave = dev_to_sdw_dev(dev); |
65 | char modalias[32]; |
66 | |
67 | sdw_slave_modalias(slave, buf: modalias, size: sizeof(modalias)); |
68 | |
69 | if (add_uevent_var(env, format: "MODALIAS=%s" , modalias)) |
70 | return -ENOMEM; |
71 | |
72 | return 0; |
73 | } |
74 | |
75 | const struct bus_type sdw_bus_type = { |
76 | .name = "soundwire" , |
77 | .match = sdw_bus_match, |
78 | }; |
79 | EXPORT_SYMBOL_GPL(sdw_bus_type); |
80 | |
81 | static int sdw_drv_probe(struct device *dev) |
82 | { |
83 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
84 | struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); |
85 | const struct sdw_device_id *id; |
86 | const char *name; |
87 | int ret; |
88 | |
89 | /* |
90 | * fw description is mandatory to bind |
91 | */ |
92 | if (!dev->fwnode) |
93 | return -ENODEV; |
94 | |
95 | if (!IS_ENABLED(CONFIG_ACPI) && !dev->of_node) |
96 | return -ENODEV; |
97 | |
98 | id = sdw_get_device_id(slave, drv); |
99 | if (!id) |
100 | return -ENODEV; |
101 | |
102 | /* |
103 | * attach to power domain but don't turn on (last arg) |
104 | */ |
105 | ret = dev_pm_domain_attach(dev, power_on: false); |
106 | if (ret) |
107 | return ret; |
108 | |
109 | ret = drv->probe(slave, id); |
110 | if (ret) { |
111 | name = drv->name; |
112 | if (!name) |
113 | name = drv->driver.name; |
114 | |
115 | dev_err(dev, "Probe of %s failed: %d\n" , name, ret); |
116 | dev_pm_domain_detach(dev, power_off: false); |
117 | return ret; |
118 | } |
119 | |
120 | mutex_lock(&slave->sdw_dev_lock); |
121 | |
122 | /* device is probed so let's read the properties now */ |
123 | if (drv->ops && drv->ops->read_prop) |
124 | drv->ops->read_prop(slave); |
125 | |
126 | if (slave->prop.use_domain_irq) |
127 | sdw_irq_create_mapping(slave); |
128 | |
129 | /* init the sysfs as we have properties now */ |
130 | ret = sdw_slave_sysfs_init(slave); |
131 | if (ret < 0) |
132 | dev_warn(dev, "Slave sysfs init failed:%d\n" , ret); |
133 | |
134 | /* |
135 | * Check for valid clk_stop_timeout, use DisCo worst case value of |
136 | * 300ms |
137 | * |
138 | * TODO: check the timeouts and driver removal case |
139 | */ |
140 | if (slave->prop.clk_stop_timeout == 0) |
141 | slave->prop.clk_stop_timeout = 300; |
142 | |
143 | slave->bus->clk_stop_timeout = max_t(u32, slave->bus->clk_stop_timeout, |
144 | slave->prop.clk_stop_timeout); |
145 | |
146 | slave->probed = true; |
147 | |
148 | /* |
149 | * if the probe happened after the bus was started, notify the codec driver |
150 | * of the current hardware status to e.g. start the initialization. |
151 | * Errors are only logged as warnings to avoid failing the probe. |
152 | */ |
153 | if (drv->ops && drv->ops->update_status) { |
154 | ret = drv->ops->update_status(slave, slave->status); |
155 | if (ret < 0) |
156 | dev_warn(dev, "%s: update_status failed with status %d\n" , __func__, ret); |
157 | } |
158 | |
159 | mutex_unlock(lock: &slave->sdw_dev_lock); |
160 | |
161 | dev_dbg(dev, "probe complete\n" ); |
162 | |
163 | return 0; |
164 | } |
165 | |
166 | static int sdw_drv_remove(struct device *dev) |
167 | { |
168 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
169 | struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); |
170 | int ret = 0; |
171 | |
172 | mutex_lock(&slave->sdw_dev_lock); |
173 | |
174 | slave->probed = false; |
175 | |
176 | if (slave->prop.use_domain_irq) |
177 | sdw_irq_dispose_mapping(slave); |
178 | |
179 | mutex_unlock(lock: &slave->sdw_dev_lock); |
180 | |
181 | if (drv->remove) |
182 | ret = drv->remove(slave); |
183 | |
184 | dev_pm_domain_detach(dev, power_off: false); |
185 | |
186 | return ret; |
187 | } |
188 | |
189 | static void sdw_drv_shutdown(struct device *dev) |
190 | { |
191 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
192 | struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); |
193 | |
194 | if (drv->shutdown) |
195 | drv->shutdown(slave); |
196 | } |
197 | |
198 | /** |
199 | * __sdw_register_driver() - register a SoundWire Slave driver |
200 | * @drv: driver to register |
201 | * @owner: owning module/driver |
202 | * |
203 | * Return: zero on success, else a negative error code. |
204 | */ |
205 | int __sdw_register_driver(struct sdw_driver *drv, struct module *owner) |
206 | { |
207 | const char *name; |
208 | |
209 | drv->driver.bus = &sdw_bus_type; |
210 | |
211 | if (!drv->probe) { |
212 | name = drv->name; |
213 | if (!name) |
214 | name = drv->driver.name; |
215 | |
216 | pr_err("driver %s didn't provide SDW probe routine\n" , name); |
217 | return -EINVAL; |
218 | } |
219 | |
220 | drv->driver.owner = owner; |
221 | drv->driver.probe = sdw_drv_probe; |
222 | drv->driver.remove = sdw_drv_remove; |
223 | drv->driver.shutdown = sdw_drv_shutdown; |
224 | |
225 | return driver_register(drv: &drv->driver); |
226 | } |
227 | EXPORT_SYMBOL_GPL(__sdw_register_driver); |
228 | |
229 | /** |
230 | * sdw_unregister_driver() - unregisters the SoundWire Slave driver |
231 | * @drv: driver to unregister |
232 | */ |
233 | void sdw_unregister_driver(struct sdw_driver *drv) |
234 | { |
235 | driver_unregister(drv: &drv->driver); |
236 | } |
237 | EXPORT_SYMBOL_GPL(sdw_unregister_driver); |
238 | |
239 | static int __init sdw_bus_init(void) |
240 | { |
241 | sdw_debugfs_init(); |
242 | return bus_register(bus: &sdw_bus_type); |
243 | } |
244 | |
245 | static void __exit sdw_bus_exit(void) |
246 | { |
247 | sdw_debugfs_exit(); |
248 | bus_unregister(bus: &sdw_bus_type); |
249 | } |
250 | |
251 | postcore_initcall(sdw_bus_init); |
252 | module_exit(sdw_bus_exit); |
253 | |
254 | MODULE_DESCRIPTION("SoundWire bus" ); |
255 | MODULE_LICENSE("GPL v2" ); |
256 | |