diff options
author | Sebastian Reichel <sre@kernel.org> | 2014-03-28 17:59:43 -0400 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2014-05-15 18:54:45 -0400 |
commit | a2aa24734d9dbbd3b9062c2459936c336278fa6a (patch) | |
tree | c398938da3956b80397f3154badbec24134739b9 /drivers/hsi | |
parent | 8491451024bcfabdcebd772ce9ec2fc5757acd42 (diff) |
HSI: Add common DT binding for HSI client devices
Implement and document generic DT bindings for HSI clients.
Signed-off-by: Sebastian Reichel <sre@kernel.org>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Tested-By: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
Diffstat (limited to 'drivers/hsi')
-rw-r--r-- | drivers/hsi/hsi.c | 208 |
1 files changed, 206 insertions, 2 deletions
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index 834a2d6b444e..fe9371271ce2 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/string.h> | 27 | #include <linux/string.h> |
28 | #include <linux/notifier.h> | 28 | #include <linux/notifier.h> |
29 | #include <linux/of.h> | ||
30 | #include <linux/of_device.h> | ||
29 | #include "hsi_core.h" | 31 | #include "hsi_core.h" |
30 | 32 | ||
31 | static ssize_t modalias_show(struct device *dev, | 33 | static ssize_t modalias_show(struct device *dev, |
@@ -50,7 +52,13 @@ static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
50 | 52 | ||
51 | static int hsi_bus_match(struct device *dev, struct device_driver *driver) | 53 | static int hsi_bus_match(struct device *dev, struct device_driver *driver) |
52 | { | 54 | { |
53 | return strcmp(dev_name(dev), driver->name) == 0; | 55 | if (of_driver_match_device(dev, driver)) |
56 | return true; | ||
57 | |||
58 | if (strcmp(dev_name(dev), driver->name) == 0) | ||
59 | return true; | ||
60 | |||
61 | return false; | ||
54 | } | 62 | } |
55 | 63 | ||
56 | static struct bus_type hsi_bus_type = { | 64 | static struct bus_type hsi_bus_type = { |
@@ -123,6 +131,202 @@ static void hsi_scan_board_info(struct hsi_controller *hsi) | |||
123 | } | 131 | } |
124 | } | 132 | } |
125 | 133 | ||
134 | #ifdef CONFIG_OF | ||
135 | static struct hsi_board_info hsi_char_dev_info = { | ||
136 | .name = "hsi_char", | ||
137 | }; | ||
138 | |||
139 | static int hsi_of_property_parse_mode(struct device_node *client, char *name, | ||
140 | unsigned int *result) | ||
141 | { | ||
142 | const char *mode; | ||
143 | int err; | ||
144 | |||
145 | err = of_property_read_string(client, name, &mode); | ||
146 | if (err < 0) | ||
147 | return err; | ||
148 | |||
149 | if (strcmp(mode, "stream") == 0) | ||
150 | *result = HSI_MODE_STREAM; | ||
151 | else if (strcmp(mode, "frame") == 0) | ||
152 | *result = HSI_MODE_FRAME; | ||
153 | else | ||
154 | return -EINVAL; | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int hsi_of_property_parse_flow(struct device_node *client, char *name, | ||
160 | unsigned int *result) | ||
161 | { | ||
162 | const char *flow; | ||
163 | int err; | ||
164 | |||
165 | err = of_property_read_string(client, name, &flow); | ||
166 | if (err < 0) | ||
167 | return err; | ||
168 | |||
169 | if (strcmp(flow, "synchronized") == 0) | ||
170 | *result = HSI_FLOW_SYNC; | ||
171 | else if (strcmp(flow, "pipeline") == 0) | ||
172 | *result = HSI_FLOW_PIPE; | ||
173 | else | ||
174 | return -EINVAL; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int hsi_of_property_parse_arb_mode(struct device_node *client, | ||
180 | char *name, unsigned int *result) | ||
181 | { | ||
182 | const char *arb_mode; | ||
183 | int err; | ||
184 | |||
185 | err = of_property_read_string(client, name, &arb_mode); | ||
186 | if (err < 0) | ||
187 | return err; | ||
188 | |||
189 | if (strcmp(arb_mode, "round-robin") == 0) | ||
190 | *result = HSI_ARB_RR; | ||
191 | else if (strcmp(arb_mode, "priority") == 0) | ||
192 | *result = HSI_ARB_PRIO; | ||
193 | else | ||
194 | return -EINVAL; | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static void hsi_add_client_from_dt(struct hsi_port *port, | ||
200 | struct device_node *client) | ||
201 | { | ||
202 | struct hsi_client *cl; | ||
203 | struct hsi_channel channel; | ||
204 | struct property *prop; | ||
205 | char name[32]; | ||
206 | int length, cells, err, i, max_chan, mode; | ||
207 | |||
208 | cl = kzalloc(sizeof(*cl), GFP_KERNEL); | ||
209 | if (!cl) | ||
210 | return; | ||
211 | |||
212 | err = of_modalias_node(client, name, sizeof(name)); | ||
213 | if (err) | ||
214 | goto err; | ||
215 | |||
216 | dev_set_name(&cl->device, "%s", name); | ||
217 | |||
218 | err = hsi_of_property_parse_mode(client, "hsi-mode", &mode); | ||
219 | if (err) { | ||
220 | err = hsi_of_property_parse_mode(client, "hsi-rx-mode", | ||
221 | &cl->rx_cfg.mode); | ||
222 | if (err) | ||
223 | goto err; | ||
224 | |||
225 | err = hsi_of_property_parse_mode(client, "hsi-tx-mode", | ||
226 | &cl->tx_cfg.mode); | ||
227 | if (err) | ||
228 | goto err; | ||
229 | } else { | ||
230 | cl->rx_cfg.mode = mode; | ||
231 | cl->tx_cfg.mode = mode; | ||
232 | } | ||
233 | |||
234 | err = of_property_read_u32(client, "hsi-speed-kbps", | ||
235 | &cl->tx_cfg.speed); | ||
236 | if (err) | ||
237 | goto err; | ||
238 | cl->rx_cfg.speed = cl->tx_cfg.speed; | ||
239 | |||
240 | err = hsi_of_property_parse_flow(client, "hsi-flow", | ||
241 | &cl->rx_cfg.flow); | ||
242 | if (err) | ||
243 | goto err; | ||
244 | |||
245 | err = hsi_of_property_parse_arb_mode(client, "hsi-arb-mode", | ||
246 | &cl->rx_cfg.arb_mode); | ||
247 | if (err) | ||
248 | goto err; | ||
249 | |||
250 | prop = of_find_property(client, "hsi-channel-ids", &length); | ||
251 | if (!prop) { | ||
252 | err = -EINVAL; | ||
253 | goto err; | ||
254 | } | ||
255 | |||
256 | cells = length / sizeof(u32); | ||
257 | |||
258 | cl->rx_cfg.num_channels = cells; | ||
259 | cl->tx_cfg.num_channels = cells; | ||
260 | |||
261 | cl->rx_cfg.channels = kzalloc(cells * sizeof(channel), GFP_KERNEL); | ||
262 | if (!cl->rx_cfg.channels) { | ||
263 | err = -ENOMEM; | ||
264 | goto err; | ||
265 | } | ||
266 | |||
267 | cl->tx_cfg.channels = kzalloc(cells * sizeof(channel), GFP_KERNEL); | ||
268 | if (!cl->tx_cfg.channels) { | ||
269 | err = -ENOMEM; | ||
270 | goto err2; | ||
271 | } | ||
272 | |||
273 | max_chan = 0; | ||
274 | for (i = 0; i < cells; i++) { | ||
275 | err = of_property_read_u32_index(client, "hsi-channel-ids", i, | ||
276 | &channel.id); | ||
277 | if (err) | ||
278 | goto err3; | ||
279 | |||
280 | err = of_property_read_string_index(client, "hsi-channel-names", | ||
281 | i, &channel.name); | ||
282 | if (err) | ||
283 | channel.name = NULL; | ||
284 | |||
285 | if (channel.id > max_chan) | ||
286 | max_chan = channel.id; | ||
287 | |||
288 | cl->rx_cfg.channels[i] = channel; | ||
289 | cl->tx_cfg.channels[i] = channel; | ||
290 | } | ||
291 | |||
292 | cl->rx_cfg.num_hw_channels = max_chan + 1; | ||
293 | cl->tx_cfg.num_hw_channels = max_chan + 1; | ||
294 | |||
295 | cl->device.bus = &hsi_bus_type; | ||
296 | cl->device.parent = &port->device; | ||
297 | cl->device.release = hsi_client_release; | ||
298 | cl->device.of_node = client; | ||
299 | |||
300 | if (device_register(&cl->device) < 0) { | ||
301 | pr_err("hsi: failed to register client: %s\n", name); | ||
302 | put_device(&cl->device); | ||
303 | goto err3; | ||
304 | } | ||
305 | |||
306 | return; | ||
307 | |||
308 | err3: | ||
309 | kfree(cl->tx_cfg.channels); | ||
310 | err2: | ||
311 | kfree(cl->rx_cfg.channels); | ||
312 | err: | ||
313 | kfree(cl); | ||
314 | pr_err("hsi client: missing or incorrect of property: err=%d\n", err); | ||
315 | } | ||
316 | |||
317 | void hsi_add_clients_from_dt(struct hsi_port *port, struct device_node *clients) | ||
318 | { | ||
319 | struct device_node *child; | ||
320 | |||
321 | /* register hsi-char device */ | ||
322 | hsi_new_client(port, &hsi_char_dev_info); | ||
323 | |||
324 | for_each_available_child_of_node(clients, child) | ||
325 | hsi_add_client_from_dt(port, child); | ||
326 | } | ||
327 | EXPORT_SYMBOL_GPL(hsi_add_clients_from_dt); | ||
328 | #endif | ||
329 | |||
126 | int hsi_remove_client(struct device *dev, void *data __maybe_unused) | 330 | int hsi_remove_client(struct device *dev, void *data __maybe_unused) |
127 | { | 331 | { |
128 | device_unregister(dev); | 332 | device_unregister(dev); |
@@ -505,7 +709,7 @@ int hsi_unregister_port_event(struct hsi_client *cl) | |||
505 | EXPORT_SYMBOL_GPL(hsi_unregister_port_event); | 709 | EXPORT_SYMBOL_GPL(hsi_unregister_port_event); |
506 | 710 | ||
507 | /** | 711 | /** |
508 | * hsi_event -Notifies clients about port events | 712 | * hsi_event - Notifies clients about port events |
509 | * @port: Port where the event occurred | 713 | * @port: Port where the event occurred |
510 | * @event: The event type | 714 | * @event: The event type |
511 | * | 715 | * |