aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hsi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-04-02 12:50:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-04-02 12:50:40 -0400
commitb1a808ff436343956a6ae63178ea1810c5e5a3a1 (patch)
treebd510d5ea6b407df6ea0b8353e1788e12f95e427 /drivers/hsi
parent97754175042e77d02c4232d5fb24c16abd1c77f1 (diff)
parent43139a61fc68f4b0af7327a0e63f340a7c81c69a (diff)
Merge branch 'for-next' of git://gitorious.org/kernel-hsi/kernel-hsi
Pull HSI (High Speed Synchronous Serial Interface) framework from Carlos Chinea: "The High Speed Synchronous Serial Interface (HSI) is a serial interface mainly used for connecting application engines (APE) with cellular modem engines (CMT) in cellular handsets. The framework is currently being used for some people and we would like to see it integrated into the kernel for 3.3. There is no HW controller drivers in this pull, but some people have already some of them pending which they would like to push as soon as this integrated. I am also working on the acceptance for an TI OMAP one, based on a compatible legacy version of the interface called SSI." Ok, so it didn't get into 3.3, but here it is pulled into 3.4. Several people piped up to say "yeah, we want this". * 'for-next' of git://gitorious.org/kernel-hsi/kernel-hsi: HSI: hsi_char: Update ioctl-number.txt HSI: Add HSI API documentation HSI: hsi_char: Add HSI char device kernel configuration HSI: hsi_char: Add HSI char device driver HSI: hsi: Introducing HSI framework
Diffstat (limited to 'drivers/hsi')
-rw-r--r--drivers/hsi/Kconfig19
-rw-r--r--drivers/hsi/Makefile6
-rw-r--r--drivers/hsi/clients/Kconfig13
-rw-r--r--drivers/hsi/clients/Makefile5
-rw-r--r--drivers/hsi/clients/hsi_char.c802
-rw-r--r--drivers/hsi/hsi.c494
-rw-r--r--drivers/hsi/hsi_boardinfo.c62
-rw-r--r--drivers/hsi/hsi_core.h35
8 files changed, 1436 insertions, 0 deletions
diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
new file mode 100644
index 00000000000..d94e38dd80c
--- /dev/null
+++ b/drivers/hsi/Kconfig
@@ -0,0 +1,19 @@
1#
2# HSI driver configuration
3#
4menuconfig HSI
5 tristate "HSI support"
6 ---help---
7 The "High speed synchronous Serial Interface" is
8 synchronous serial interface used mainly to connect
9 application engines and cellular modems.
10
11if HSI
12
13config HSI_BOARDINFO
14 bool
15 default y
16
17source "drivers/hsi/clients/Kconfig"
18
19endif # HSI
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
new file mode 100644
index 00000000000..9d5d33f90de
--- /dev/null
+++ b/drivers/hsi/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for HSI
3#
4obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o
5obj-$(CONFIG_HSI) += hsi.o
6obj-y += clients/
diff --git a/drivers/hsi/clients/Kconfig b/drivers/hsi/clients/Kconfig
new file mode 100644
index 00000000000..3bacd275f47
--- /dev/null
+++ b/drivers/hsi/clients/Kconfig
@@ -0,0 +1,13 @@
1#
2# HSI clients configuration
3#
4
5comment "HSI clients"
6
7config HSI_CHAR
8 tristate "HSI/SSI character driver"
9 depends on HSI
10 ---help---
11 If you say Y here, you will enable the HSI/SSI character driver.
12 This driver provides a simple character device interface for
13 serial communication with the cellular modem over HSI/SSI bus.
diff --git a/drivers/hsi/clients/Makefile b/drivers/hsi/clients/Makefile
new file mode 100644
index 00000000000..327c0e27c8b
--- /dev/null
+++ b/drivers/hsi/clients/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for HSI clients
3#
4
5obj-$(CONFIG_HSI_CHAR) += hsi_char.o
diff --git a/drivers/hsi/clients/hsi_char.c b/drivers/hsi/clients/hsi_char.c
new file mode 100644
index 00000000000..88a050df238
--- /dev/null
+++ b/drivers/hsi/clients/hsi_char.c
@@ -0,0 +1,802 @@
1/*
2 * HSI character device driver, implements the character device
3 * interface.
4 *
5 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
6 *
7 * Contact: Andras Domokos <andras.domokos@nokia.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * version 2 as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 */
23
24#include <linux/errno.h>
25#include <linux/types.h>
26#include <linux/atomic.h>
27#include <linux/kernel.h>
28#include <linux/init.h>
29#include <linux/module.h>
30#include <linux/mutex.h>
31#include <linux/list.h>
32#include <linux/slab.h>
33#include <linux/kmemleak.h>
34#include <linux/ioctl.h>
35#include <linux/wait.h>
36#include <linux/fs.h>
37#include <linux/sched.h>
38#include <linux/device.h>
39#include <linux/cdev.h>
40#include <linux/uaccess.h>
41#include <linux/scatterlist.h>
42#include <linux/stat.h>
43#include <linux/hsi/hsi.h>
44#include <linux/hsi/hsi_char.h>
45
46#define HSC_DEVS 16 /* Num of channels */
47#define HSC_MSGS 4
48
49#define HSC_RXBREAK 0
50
51#define HSC_ID_BITS 6
52#define HSC_PORT_ID_BITS 4
53#define HSC_ID_MASK 3
54#define HSC_PORT_ID_MASK 3
55#define HSC_CH_MASK 0xf
56
57/*
58 * We support up to 4 controllers that can have up to 4
59 * ports, which should currently be more than enough.
60 */
61#define HSC_BASEMINOR(id, port_id) \
62 ((((id) & HSC_ID_MASK) << HSC_ID_BITS) | \
63 (((port_id) & HSC_PORT_ID_MASK) << HSC_PORT_ID_BITS))
64
65enum {
66 HSC_CH_OPEN,
67 HSC_CH_READ,
68 HSC_CH_WRITE,
69 HSC_CH_WLINE,
70};
71
72enum {
73 HSC_RX,
74 HSC_TX,
75};
76
77struct hsc_client_data;
78/**
79 * struct hsc_channel - hsi_char internal channel data
80 * @ch: channel number
81 * @flags: Keeps state of the channel (open/close, reading, writing)
82 * @free_msgs_list: List of free HSI messages/requests
83 * @rx_msgs_queue: List of pending RX requests
84 * @tx_msgs_queue: List of pending TX requests
85 * @lock: Serialize access to the lists
86 * @cl: reference to the associated hsi_client
87 * @cl_data: reference to the client data that this channels belongs to
88 * @rx_wait: RX requests wait queue
89 * @tx_wait: TX requests wait queue
90 */
91struct hsc_channel {
92 unsigned int ch;
93 unsigned long flags;
94 struct list_head free_msgs_list;
95 struct list_head rx_msgs_queue;
96 struct list_head tx_msgs_queue;
97 spinlock_t lock;
98 struct hsi_client *cl;
99 struct hsc_client_data *cl_data;
100 wait_queue_head_t rx_wait;
101 wait_queue_head_t tx_wait;
102};
103
104/**
105 * struct hsc_client_data - hsi_char internal client data
106 * @cdev: Characther device associated to the hsi_client
107 * @lock: Lock to serialize open/close access
108 * @flags: Keeps track of port state (rx hwbreak armed)
109 * @usecnt: Use count for claiming the HSI port (mutex protected)
110 * @cl: Referece to the HSI client
111 * @channels: Array of channels accessible by the client
112 */
113struct hsc_client_data {
114 struct cdev cdev;
115 struct mutex lock;
116 unsigned long flags;
117 unsigned int usecnt;
118 struct hsi_client *cl;
119 struct hsc_channel channels[HSC_DEVS];
120};
121
122/* Stores the major number dynamically allocated for hsi_char */
123static unsigned int hsc_major;
124/* Maximum buffer size that hsi_char will accept from userspace */
125static unsigned int max_data_size = 0x1000;
126module_param(max_data_size, uint, S_IRUSR | S_IWUSR);
127MODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)");
128
129static void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg,
130 struct list_head *queue)
131{
132 unsigned long flags;
133
134 spin_lock_irqsave(&channel->lock, flags);
135 list_add_tail(&msg->link, queue);
136 spin_unlock_irqrestore(&channel->lock, flags);
137}
138
139static struct hsi_msg *hsc_get_first_msg(struct hsc_channel *channel,
140 struct list_head *queue)
141{
142 struct hsi_msg *msg = NULL;
143 unsigned long flags;
144
145 spin_lock_irqsave(&channel->lock, flags);
146
147 if (list_empty(queue))
148 goto out;
149
150 msg = list_first_entry(queue, struct hsi_msg, link);
151 list_del(&msg->link);
152out:
153 spin_unlock_irqrestore(&channel->lock, flags);
154
155 return msg;
156}
157
158static inline void hsc_msg_free(struct hsi_msg *msg)
159{
160 kfree(sg_virt(msg->sgt.sgl));
161 hsi_free_msg(msg);
162}
163
164static void hsc_free_list(struct list_head *list)
165{
166 struct hsi_msg *msg, *tmp;
167
168 list_for_each_entry_safe(msg, tmp, list, link) {
169 list_del(&msg->link);
170 hsc_msg_free(msg);
171 }
172}
173
174static void hsc_reset_list(struct hsc_channel *channel, struct list_head *l)
175{
176 unsigned long flags;
177 LIST_HEAD(list);
178
179 spin_lock_irqsave(&channel->lock, flags);
180 list_splice_init(l, &list);
181 spin_unlock_irqrestore(&channel->lock, flags);
182
183 hsc_free_list(&list);
184}
185
186static inline struct hsi_msg *hsc_msg_alloc(unsigned int alloc_size)
187{
188 struct hsi_msg *msg;
189 void *buf;
190
191 msg = hsi_alloc_msg(1, GFP_KERNEL);
192 if (!msg)
193 goto out;
194 buf = kmalloc(alloc_size, GFP_KERNEL);
195 if (!buf) {
196 hsi_free_msg(msg);
197 goto out;
198 }
199 sg_init_one(msg->sgt.sgl, buf, alloc_size);
200 /* Ignore false positive, due to sg pointer handling */
201 kmemleak_ignore(buf);
202
203 return msg;
204out:
205 return NULL;
206}
207
208static inline int hsc_msgs_alloc(struct hsc_channel *channel)
209{
210 struct hsi_msg *msg;
211 int i;
212
213 for (i = 0; i < HSC_MSGS; i++) {
214 msg = hsc_msg_alloc(max_data_size);
215 if (!msg)
216 goto out;
217 msg->channel = channel->ch;
218 list_add_tail(&msg->link, &channel->free_msgs_list);
219 }
220
221 return 0;
222out:
223 hsc_free_list(&channel->free_msgs_list);
224
225 return -ENOMEM;
226}
227
228static inline unsigned int hsc_msg_len_get(struct hsi_msg *msg)
229{
230 return msg->sgt.sgl->length;
231}
232
233static inline void hsc_msg_len_set(struct hsi_msg *msg, unsigned int len)
234{
235 msg->sgt.sgl->length = len;
236}
237
238static void hsc_rx_completed(struct hsi_msg *msg)
239{
240 struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
241 struct hsc_channel *channel = cl_data->channels + msg->channel;
242
243 if (test_bit(HSC_CH_READ, &channel->flags)) {
244 hsc_add_tail(channel, msg, &channel->rx_msgs_queue);
245 wake_up(&channel->rx_wait);
246 } else {
247 hsc_add_tail(channel, msg, &channel->free_msgs_list);
248 }
249}
250
251static void hsc_rx_msg_destructor(struct hsi_msg *msg)
252{
253 msg->status = HSI_STATUS_ERROR;
254 hsc_msg_len_set(msg, 0);
255 hsc_rx_completed(msg);
256}
257
258static void hsc_tx_completed(struct hsi_msg *msg)
259{
260 struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
261 struct hsc_channel *channel = cl_data->channels + msg->channel;
262
263 if (test_bit(HSC_CH_WRITE, &channel->flags)) {
264 hsc_add_tail(channel, msg, &channel->tx_msgs_queue);
265 wake_up(&channel->tx_wait);
266 } else {
267 hsc_add_tail(channel, msg, &channel->free_msgs_list);
268 }
269}
270
271static void hsc_tx_msg_destructor(struct hsi_msg *msg)
272{
273 msg->status = HSI_STATUS_ERROR;
274 hsc_msg_len_set(msg, 0);
275 hsc_tx_completed(msg);
276}
277
278static void hsc_break_req_destructor(struct hsi_msg *msg)
279{
280 struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
281
282 hsi_free_msg(msg);
283 clear_bit(HSC_RXBREAK, &cl_data->flags);
284}
285
286static void hsc_break_received(struct hsi_msg *msg)
287{
288 struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
289 struct hsc_channel *channel = cl_data->channels;
290 int i, ret;
291
292 /* Broadcast HWBREAK on all channels */
293 for (i = 0; i < HSC_DEVS; i++, channel++) {
294 struct hsi_msg *msg2;
295
296 if (!test_bit(HSC_CH_READ, &channel->flags))
297 continue;
298 msg2 = hsc_get_first_msg(channel, &channel->free_msgs_list);
299 if (!msg2)
300 continue;
301 clear_bit(HSC_CH_READ, &channel->flags);
302 hsc_msg_len_set(msg2, 0);
303 msg2->status = HSI_STATUS_COMPLETED;
304 hsc_add_tail(channel, msg2, &channel->rx_msgs_queue);
305 wake_up(&channel->rx_wait);
306 }
307 hsi_flush(msg->cl);
308 ret = hsi_async_read(msg->cl, msg);
309 if (ret < 0)
310 hsc_break_req_destructor(msg);
311}
312
313static int hsc_break_request(struct hsi_client *cl)
314{
315 struct hsc_client_data *cl_data = hsi_client_drvdata(cl);
316 struct hsi_msg *msg;
317 int ret;
318
319 if (test_and_set_bit(HSC_RXBREAK, &cl_data->flags))
320 return -EBUSY;
321
322 msg = hsi_alloc_msg(0, GFP_KERNEL);
323 if (!msg) {
324 clear_bit(HSC_RXBREAK, &cl_data->flags);
325 return -ENOMEM;
326 }
327 msg->break_frame = 1;
328 msg->complete = hsc_break_received;
329 msg->destructor = hsc_break_req_destructor;
330 ret = hsi_async_read(cl, msg);
331 if (ret < 0)
332 hsc_break_req_destructor(msg);
333
334 return ret;
335}
336
337static int hsc_break_send(struct hsi_client *cl)
338{
339 struct hsi_msg *msg;
340 int ret;
341
342 msg = hsi_alloc_msg(0, GFP_ATOMIC);
343 if (!msg)
344 return -ENOMEM;
345 msg->break_frame = 1;
346 msg->complete = hsi_free_msg;
347 msg->destructor = hsi_free_msg;
348 ret = hsi_async_write(cl, msg);
349 if (ret < 0)
350 hsi_free_msg(msg);
351
352 return ret;
353}
354
355static int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc)
356{
357 struct hsi_config tmp;
358 int ret;
359
360 if ((rxc->mode != HSI_MODE_STREAM) && (rxc->mode != HSI_MODE_FRAME))
361 return -EINVAL;
362 if ((rxc->channels == 0) || (rxc->channels > HSC_DEVS))
363 return -EINVAL;
364 if (rxc->channels & (rxc->channels - 1))
365 return -EINVAL;
366 if ((rxc->flow != HSI_FLOW_SYNC) && (rxc->flow != HSI_FLOW_PIPE))
367 return -EINVAL;
368 tmp = cl->rx_cfg;
369 cl->rx_cfg.mode = rxc->mode;
370 cl->rx_cfg.channels = rxc->channels;
371 cl->rx_cfg.flow = rxc->flow;
372 ret = hsi_setup(cl);
373 if (ret < 0) {
374 cl->rx_cfg = tmp;
375 return ret;
376 }
377 if (rxc->mode == HSI_MODE_FRAME)
378 hsc_break_request(cl);
379
380 return ret;
381}
382
383static inline void hsc_rx_get(struct hsi_client *cl, struct hsc_rx_config *rxc)
384{
385 rxc->mode = cl->rx_cfg.mode;
386 rxc->channels = cl->rx_cfg.channels;
387 rxc->flow = cl->rx_cfg.flow;
388}
389
390static int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc)
391{
392 struct hsi_config tmp;
393 int ret;
394
395 if ((txc->mode != HSI_MODE_STREAM) && (txc->mode != HSI_MODE_FRAME))
396 return -EINVAL;
397 if ((txc->channels == 0) || (txc->channels > HSC_DEVS))
398 return -EINVAL;
399 if (txc->channels & (txc->channels - 1))
400 return -EINVAL;
401 if ((txc->arb_mode != HSI_ARB_RR) && (txc->arb_mode != HSI_ARB_PRIO))
402 return -EINVAL;
403 tmp = cl->tx_cfg;
404 cl->tx_cfg.mode = txc->mode;
405 cl->tx_cfg.channels = txc->channels;
406 cl->tx_cfg.speed = txc->speed;
407 cl->tx_cfg.arb_mode = txc->arb_mode;
408 ret = hsi_setup(cl);
409 if (ret < 0) {
410 cl->tx_cfg = tmp;
411 return ret;
412 }
413
414 return ret;
415}
416
417static inline void hsc_tx_get(struct hsi_client *cl, struct hsc_tx_config *txc)
418{
419 txc->mode = cl->tx_cfg.mode;
420 txc->channels = cl->tx_cfg.channels;
421 txc->speed = cl->tx_cfg.speed;
422 txc->arb_mode = cl->tx_cfg.arb_mode;
423}
424
425static ssize_t hsc_read(struct file *file, char __user *buf, size_t len,
426 loff_t *ppos __maybe_unused)
427{
428 struct hsc_channel *channel = file->private_data;
429 struct hsi_msg *msg;
430 ssize_t ret;
431
432 if (len == 0)
433 return 0;
434 if (!IS_ALIGNED(len, sizeof(u32)))
435 return -EINVAL;
436 if (len > max_data_size)
437 len = max_data_size;
438 if (channel->ch >= channel->cl->rx_cfg.channels)
439 return -ECHRNG;
440 if (test_and_set_bit(HSC_CH_READ, &channel->flags))
441 return -EBUSY;
442 msg = hsc_get_first_msg(channel, &channel->free_msgs_list);
443 if (!msg) {
444 ret = -ENOSPC;
445 goto out;
446 }
447 hsc_msg_len_set(msg, len);
448 msg->complete = hsc_rx_completed;
449 msg->destructor = hsc_rx_msg_destructor;
450 ret = hsi_async_read(channel->cl, msg);
451 if (ret < 0) {
452 hsc_add_tail(channel, msg, &channel->free_msgs_list);
453 goto out;
454 }
455
456 ret = wait_event_interruptible(channel->rx_wait,
457 !list_empty(&channel->rx_msgs_queue));
458 if (ret < 0) {
459 clear_bit(HSC_CH_READ, &channel->flags);
460 hsi_flush(channel->cl);
461 return -EINTR;
462 }
463
464 msg = hsc_get_first_msg(channel, &channel->rx_msgs_queue);
465 if (msg) {
466 if (msg->status != HSI_STATUS_ERROR) {
467 ret = copy_to_user((void __user *)buf,
468 sg_virt(msg->sgt.sgl), hsc_msg_len_get(msg));
469 if (ret)
470 ret = -EFAULT;
471 else
472 ret = hsc_msg_len_get(msg);
473 } else {
474 ret = -EIO;
475 }
476 hsc_add_tail(channel, msg, &channel->free_msgs_list);
477 }
478out:
479 clear_bit(HSC_CH_READ, &channel->flags);
480
481 return ret;
482}
483
484static ssize_t hsc_write(struct file *file, const char __user *buf, size_t len,
485 loff_t *ppos __maybe_unused)
486{
487 struct hsc_channel *channel = file->private_data;
488 struct hsi_msg *msg;
489 ssize_t ret;
490
491 if ((len == 0) || !IS_ALIGNED(len, sizeof(u32)))
492 return -EINVAL;
493 if (len > max_data_size)
494 len = max_data_size;
495 if (channel->ch >= channel->cl->tx_cfg.channels)
496 return -ECHRNG;
497 if (test_and_set_bit(HSC_CH_WRITE, &channel->flags))
498 return -EBUSY;
499 msg = hsc_get_first_msg(channel, &channel->free_msgs_list);
500 if (!msg) {
501 clear_bit(HSC_CH_WRITE, &channel->flags);
502 return -ENOSPC;
503 }
504 if (copy_from_user(sg_virt(msg->sgt.sgl), (void __user *)buf, len)) {
505 ret = -EFAULT;
506 goto out;
507 }
508 hsc_msg_len_set(msg, len);
509 msg->complete = hsc_tx_completed;
510 msg->destructor = hsc_tx_msg_destructor;
511 ret = hsi_async_write(channel->cl, msg);
512 if (ret < 0)
513 goto out;
514
515 ret = wait_event_interruptible(channel->tx_wait,
516 !list_empty(&channel->tx_msgs_queue));
517 if (ret < 0) {
518 clear_bit(HSC_CH_WRITE, &channel->flags);
519 hsi_flush(channel->cl);
520 return -EINTR;
521 }
522
523 msg = hsc_get_first_msg(channel, &channel->tx_msgs_queue);
524 if (msg) {
525 if (msg->status == HSI_STATUS_ERROR)
526 ret = -EIO;
527 else
528 ret = hsc_msg_len_get(msg);
529
530 hsc_add_tail(channel, msg, &channel->free_msgs_list);
531 }
532out:
533 clear_bit(HSC_CH_WRITE, &channel->flags);
534
535 return ret;
536}
537
538static long hsc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
539{
540 struct hsc_channel *channel = file->private_data;
541 unsigned int state;
542 struct hsc_rx_config rxc;
543 struct hsc_tx_config txc;
544 long ret = 0;
545
546 switch (cmd) {
547 case HSC_RESET:
548 hsi_flush(channel->cl);
549 break;
550 case HSC_SET_PM:
551 if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
552 return -EFAULT;
553 if (state == HSC_PM_DISABLE) {
554 if (test_and_set_bit(HSC_CH_WLINE, &channel->flags))
555 return -EINVAL;
556 ret = hsi_start_tx(channel->cl);
557 } else if (state == HSC_PM_ENABLE) {
558 if (!test_and_clear_bit(HSC_CH_WLINE, &channel->flags))
559 return -EINVAL;
560 ret = hsi_stop_tx(channel->cl);
561 } else {
562 ret = -EINVAL;
563 }
564 break;
565 case HSC_SEND_BREAK:
566 return hsc_break_send(channel->cl);
567 case HSC_SET_RX:
568 if (copy_from_user(&rxc, (void __user *)arg, sizeof(rxc)))
569 return -EFAULT;
570 return hsc_rx_set(channel->cl, &rxc);
571 case HSC_GET_RX:
572 hsc_rx_get(channel->cl, &rxc);
573 if (copy_to_user((void __user *)arg, &rxc, sizeof(rxc)))
574 return -EFAULT;
575 break;
576 case HSC_SET_TX:
577 if (copy_from_user(&txc, (void __user *)arg, sizeof(txc)))
578 return -EFAULT;
579 return hsc_tx_set(channel->cl, &txc);
580 case HSC_GET_TX:
581 hsc_tx_get(channel->cl, &txc);
582 if (copy_to_user((void __user *)arg, &txc, sizeof(txc)))
583 return -EFAULT;
584 break;
585 default:
586 return -ENOIOCTLCMD;
587 }
588
589 return ret;
590}
591
592static inline void __hsc_port_release(struct hsc_client_data *cl_data)
593{
594 BUG_ON(cl_data->usecnt == 0);
595
596 if (--cl_data->usecnt == 0) {
597 hsi_flush(cl_data->cl);
598 hsi_release_port(cl_data->cl);
599 }
600}
601
602static int hsc_open(struct inode *inode, struct file *file)
603{
604 struct hsc_client_data *cl_data;
605 struct hsc_channel *channel;
606 int ret = 0;
607
608 pr_debug("open, minor = %d\n", iminor(inode));
609
610 cl_data = container_of(inode->i_cdev, struct hsc_client_data, cdev);
611 mutex_lock(&cl_data->lock);
612 channel = cl_data->channels + (iminor(inode) & HSC_CH_MASK);
613
614 if (test_and_set_bit(HSC_CH_OPEN, &channel->flags)) {
615 ret = -EBUSY;
616 goto out;
617 }
618 /*
619 * Check if we have already claimed the port associated to the HSI
620 * client. If not then try to claim it, else increase its refcount
621 */
622 if (cl_data->usecnt == 0) {
623 ret = hsi_claim_port(cl_data->cl, 0);
624 if (ret < 0)
625 goto out;
626 hsi_setup(cl_data->cl);
627 }
628 cl_data->usecnt++;
629
630 ret = hsc_msgs_alloc(channel);
631 if (ret < 0) {
632 __hsc_port_release(cl_data);
633 goto out;
634 }
635
636 file->private_data = channel;
637 mutex_unlock(&cl_data->lock);
638
639 return ret;
640out:
641 mutex_unlock(&cl_data->lock);
642
643 return ret;
644}
645
646static int hsc_release(struct inode *inode __maybe_unused, struct file *file)
647{
648 struct hsc_channel *channel = file->private_data;
649 struct hsc_client_data *cl_data = channel->cl_data;
650
651 mutex_lock(&cl_data->lock);
652 file->private_data = NULL;
653 if (test_and_clear_bit(HSC_CH_WLINE, &channel->flags))
654 hsi_stop_tx(channel->cl);
655 __hsc_port_release(cl_data);
656 hsc_reset_list(channel, &channel->rx_msgs_queue);
657 hsc_reset_list(channel, &channel->tx_msgs_queue);
658 hsc_reset_list(channel, &channel->free_msgs_list);
659 clear_bit(HSC_CH_READ, &channel->flags);
660 clear_bit(HSC_CH_WRITE, &channel->flags);
661 clear_bit(HSC_CH_OPEN, &channel->flags);
662 wake_up(&channel->rx_wait);
663 wake_up(&channel->tx_wait);
664 mutex_unlock(&cl_data->lock);
665
666 return 0;
667}
668
669static const struct file_operations hsc_fops = {
670 .owner = THIS_MODULE,
671 .read = hsc_read,
672 .write = hsc_write,
673 .unlocked_ioctl = hsc_ioctl,
674 .open = hsc_open,
675 .release = hsc_release,
676};
677
678static void __devinit hsc_channel_init(struct hsc_channel *channel)
679{
680 init_waitqueue_head(&channel->rx_wait);
681 init_waitqueue_head(&channel->tx_wait);
682 spin_lock_init(&channel->lock);
683 INIT_LIST_HEAD(&channel->free_msgs_list);
684 INIT_LIST_HEAD(&channel->rx_msgs_queue);
685 INIT_LIST_HEAD(&channel->tx_msgs_queue);
686}
687
688static int __devinit hsc_probe(struct device *dev)
689{
690 const char devname[] = "hsi_char";
691 struct hsc_client_data *cl_data;
692 struct hsc_channel *channel;
693 struct hsi_client *cl = to_hsi_client(dev);
694 unsigned int hsc_baseminor;
695 dev_t hsc_dev;
696 int ret;
697 int i;
698
699 cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL);
700 if (!cl_data) {
701 dev_err(dev, "Could not allocate hsc_client_data\n");
702 return -ENOMEM;
703 }
704 hsc_baseminor = HSC_BASEMINOR(hsi_id(cl), hsi_port_id(cl));
705 if (!hsc_major) {
706 ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor,
707 HSC_DEVS, devname);
708 if (ret > 0)
709 hsc_major = MAJOR(hsc_dev);
710 } else {
711 hsc_dev = MKDEV(hsc_major, hsc_baseminor);
712 ret = register_chrdev_region(hsc_dev, HSC_DEVS, devname);
713 }
714 if (ret < 0) {
715 dev_err(dev, "Device %s allocation failed %d\n",
716 hsc_major ? "minor" : "major", ret);
717 goto out1;
718 }
719 mutex_init(&cl_data->lock);
720 hsi_client_set_drvdata(cl, cl_data);
721 cdev_init(&cl_data->cdev, &hsc_fops);
722 cl_data->cdev.owner = THIS_MODULE;
723 cl_data->cl = cl;
724 for (i = 0, channel = cl_data->channels; i < HSC_DEVS; i++, channel++) {
725 hsc_channel_init(channel);
726 channel->ch = i;
727 channel->cl = cl;
728 channel->cl_data = cl_data;
729 }
730
731 /* 1 hsi client -> N char devices (one for each channel) */
732 ret = cdev_add(&cl_data->cdev, hsc_dev, HSC_DEVS);
733 if (ret) {
734 dev_err(dev, "Could not add char device %d\n", ret);
735 goto out2;
736 }
737
738 return 0;
739out2:
740 unregister_chrdev_region(hsc_dev, HSC_DEVS);
741out1:
742 kfree(cl_data);
743
744 return ret;
745}
746
747static int __devexit hsc_remove(struct device *dev)
748{
749 struct hsi_client *cl = to_hsi_client(dev);
750 struct hsc_client_data *cl_data = hsi_client_drvdata(cl);
751 dev_t hsc_dev = cl_data->cdev.dev;
752
753 cdev_del(&cl_data->cdev);
754 unregister_chrdev_region(hsc_dev, HSC_DEVS);
755 hsi_client_set_drvdata(cl, NULL);
756 kfree(cl_data);
757
758 return 0;
759}
760
761static struct hsi_client_driver hsc_driver = {
762 .driver = {
763 .name = "hsi_char",
764 .owner = THIS_MODULE,
765 .probe = hsc_probe,
766 .remove = __devexit_p(hsc_remove),
767 },
768};
769
770static int __init hsc_init(void)
771{
772 int ret;
773
774 if ((max_data_size < 4) || (max_data_size > 0x10000) ||
775 (max_data_size & (max_data_size - 1))) {
776 pr_err("Invalid max read/write data size");
777 return -EINVAL;
778 }
779
780 ret = hsi_register_client_driver(&hsc_driver);
781 if (ret) {
782 pr_err("Error while registering HSI/SSI driver %d", ret);
783 return ret;
784 }
785
786 pr_info("HSI/SSI char device loaded\n");
787
788 return 0;
789}
790module_init(hsc_init);
791
792static void __exit hsc_exit(void)
793{
794 hsi_unregister_client_driver(&hsc_driver);
795 pr_info("HSI char device removed\n");
796}
797module_exit(hsc_exit);
798
799MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>");
800MODULE_ALIAS("hsi:hsi_char");
801MODULE_DESCRIPTION("HSI character device");
802MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c
new file mode 100644
index 00000000000..4e2d79b7933
--- /dev/null
+++ b/drivers/hsi/hsi.c
@@ -0,0 +1,494 @@
1/*
2 * HSI core.
3 *
4 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
5 *
6 * Contact: Carlos Chinea <carlos.chinea@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22#include <linux/hsi/hsi.h>
23#include <linux/compiler.h>
24#include <linux/rwsem.h>
25#include <linux/list.h>
26#include <linux/spinlock.h>
27#include <linux/kobject.h>
28#include <linux/slab.h>
29#include <linux/string.h>
30#include "hsi_core.h"
31
32static struct device_type hsi_ctrl = {
33 .name = "hsi_controller",
34};
35
36static struct device_type hsi_cl = {
37 .name = "hsi_client",
38};
39
40static struct device_type hsi_port = {
41 .name = "hsi_port",
42};
43
44static ssize_t modalias_show(struct device *dev,
45 struct device_attribute *a __maybe_unused, char *buf)
46{
47 return sprintf(buf, "hsi:%s\n", dev_name(dev));
48}
49
50static struct device_attribute hsi_bus_dev_attrs[] = {
51 __ATTR_RO(modalias),
52 __ATTR_NULL,
53};
54
55static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
56{
57 if (dev->type == &hsi_cl)
58 add_uevent_var(env, "MODALIAS=hsi:%s", dev_name(dev));
59
60 return 0;
61}
62
63static int hsi_bus_match(struct device *dev, struct device_driver *driver)
64{
65 return strcmp(dev_name(dev), driver->name) == 0;
66}
67
68static struct bus_type hsi_bus_type = {
69 .name = "hsi",
70 .dev_attrs = hsi_bus_dev_attrs,
71 .match = hsi_bus_match,
72 .uevent = hsi_bus_uevent,
73};
74
75static void hsi_client_release(struct device *dev)
76{
77 kfree(to_hsi_client(dev));
78}
79
80static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info)
81{
82 struct hsi_client *cl;
83 unsigned long flags;
84
85 cl = kzalloc(sizeof(*cl), GFP_KERNEL);
86 if (!cl)
87 return;
88 cl->device.type = &hsi_cl;
89 cl->tx_cfg = info->tx_cfg;
90 cl->rx_cfg = info->rx_cfg;
91 cl->device.bus = &hsi_bus_type;
92 cl->device.parent = &port->device;
93 cl->device.release = hsi_client_release;
94 dev_set_name(&cl->device, info->name);
95 cl->device.platform_data = info->platform_data;
96 spin_lock_irqsave(&port->clock, flags);
97 list_add_tail(&cl->link, &port->clients);
98 spin_unlock_irqrestore(&port->clock, flags);
99 if (info->archdata)
100 cl->device.archdata = *info->archdata;
101 if (device_register(&cl->device) < 0) {
102 pr_err("hsi: failed to register client: %s\n", info->name);
103 kfree(cl);
104 }
105}
106
107static void hsi_scan_board_info(struct hsi_controller *hsi)
108{
109 struct hsi_cl_info *cl_info;
110 struct hsi_port *p;
111
112 list_for_each_entry(cl_info, &hsi_board_list, list)
113 if (cl_info->info.hsi_id == hsi->id) {
114 p = hsi_find_port_num(hsi, cl_info->info.port);
115 if (!p)
116 continue;
117 hsi_new_client(p, &cl_info->info);
118 }
119}
120
121static int hsi_remove_client(struct device *dev, void *data __maybe_unused)
122{
123 struct hsi_client *cl = to_hsi_client(dev);
124 struct hsi_port *port = to_hsi_port(dev->parent);
125 unsigned long flags;
126
127 spin_lock_irqsave(&port->clock, flags);
128 list_del(&cl->link);
129 spin_unlock_irqrestore(&port->clock, flags);
130 device_unregister(dev);
131
132 return 0;
133}
134
135static int hsi_remove_port(struct device *dev, void *data __maybe_unused)
136{
137 device_for_each_child(dev, NULL, hsi_remove_client);
138 device_unregister(dev);
139
140 return 0;
141}
142
143static void hsi_controller_release(struct device *dev __maybe_unused)
144{
145}
146
147static void hsi_port_release(struct device *dev __maybe_unused)
148{
149}
150
151/**
152 * hsi_unregister_controller - Unregister an HSI controller
153 * @hsi: The HSI controller to register
154 */
155void hsi_unregister_controller(struct hsi_controller *hsi)
156{
157 device_for_each_child(&hsi->device, NULL, hsi_remove_port);
158 device_unregister(&hsi->device);
159}
160EXPORT_SYMBOL_GPL(hsi_unregister_controller);
161
162/**
163 * hsi_register_controller - Register an HSI controller and its ports
164 * @hsi: The HSI controller to register
165 *
166 * Returns -errno on failure, 0 on success.
167 */
168int hsi_register_controller(struct hsi_controller *hsi)
169{
170 unsigned int i;
171 int err;
172
173 hsi->device.type = &hsi_ctrl;
174 hsi->device.bus = &hsi_bus_type;
175 hsi->device.release = hsi_controller_release;
176 err = device_register(&hsi->device);
177 if (err < 0)
178 return err;
179 for (i = 0; i < hsi->num_ports; i++) {
180 hsi->port[i].device.parent = &hsi->device;
181 hsi->port[i].device.bus = &hsi_bus_type;
182 hsi->port[i].device.release = hsi_port_release;
183 hsi->port[i].device.type = &hsi_port;
184 INIT_LIST_HEAD(&hsi->port[i].clients);
185 spin_lock_init(&hsi->port[i].clock);
186 err = device_register(&hsi->port[i].device);
187 if (err < 0)
188 goto out;
189 }
190 /* Populate HSI bus with HSI clients */
191 hsi_scan_board_info(hsi);
192
193 return 0;
194out:
195 hsi_unregister_controller(hsi);
196
197 return err;
198}
199EXPORT_SYMBOL_GPL(hsi_register_controller);
200
201/**
202 * hsi_register_client_driver - Register an HSI client to the HSI bus
203 * @drv: HSI client driver to register
204 *
205 * Returns -errno on failure, 0 on success.
206 */
207int hsi_register_client_driver(struct hsi_client_driver *drv)
208{
209 drv->driver.bus = &hsi_bus_type;
210
211 return driver_register(&drv->driver);
212}
213EXPORT_SYMBOL_GPL(hsi_register_client_driver);
214
215static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused)
216{
217 return 0;
218}
219
220static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused)
221{
222 return 0;
223}
224
225/**
226 * hsi_alloc_controller - Allocate an HSI controller and its ports
227 * @n_ports: Number of ports on the HSI controller
228 * @flags: Kernel allocation flags
229 *
230 * Return NULL on failure or a pointer to an hsi_controller on success.
231 */
232struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags)
233{
234 struct hsi_controller *hsi;
235 struct hsi_port *port;
236 unsigned int i;
237
238 if (!n_ports)
239 return NULL;
240
241 port = kzalloc(sizeof(*port)*n_ports, flags);
242 if (!port)
243 return NULL;
244 hsi = kzalloc(sizeof(*hsi), flags);
245 if (!hsi)
246 goto out;
247 for (i = 0; i < n_ports; i++) {
248 dev_set_name(&port[i].device, "port%d", i);
249 port[i].num = i;
250 port[i].async = hsi_dummy_msg;
251 port[i].setup = hsi_dummy_cl;
252 port[i].flush = hsi_dummy_cl;
253 port[i].start_tx = hsi_dummy_cl;
254 port[i].stop_tx = hsi_dummy_cl;
255 port[i].release = hsi_dummy_cl;
256 mutex_init(&port[i].lock);
257 }
258 hsi->num_ports = n_ports;
259 hsi->port = port;
260
261 return hsi;
262out:
263 kfree(port);
264
265 return NULL;
266}
267EXPORT_SYMBOL_GPL(hsi_alloc_controller);
268
269/**
270 * hsi_free_controller - Free an HSI controller
271 * @hsi: Pointer to HSI controller
272 */
273void hsi_free_controller(struct hsi_controller *hsi)
274{
275 if (!hsi)
276 return;
277
278 kfree(hsi->port);
279 kfree(hsi);
280}
281EXPORT_SYMBOL_GPL(hsi_free_controller);
282
283/**
284 * hsi_free_msg - Free an HSI message
285 * @msg: Pointer to the HSI message
286 *
287 * Client is responsible to free the buffers pointed by the scatterlists.
288 */
289void hsi_free_msg(struct hsi_msg *msg)
290{
291 if (!msg)
292 return;
293 sg_free_table(&msg->sgt);
294 kfree(msg);
295}
296EXPORT_SYMBOL_GPL(hsi_free_msg);
297
298/**
299 * hsi_alloc_msg - Allocate an HSI message
300 * @nents: Number of memory entries
301 * @flags: Kernel allocation flags
302 *
303 * nents can be 0. This mainly makes sense for read transfer.
304 * In that case, HSI drivers will call the complete callback when
305 * there is data to be read without consuming it.
306 *
307 * Return NULL on failure or a pointer to an hsi_msg on success.
308 */
309struct hsi_msg *hsi_alloc_msg(unsigned int nents, gfp_t flags)
310{
311 struct hsi_msg *msg;
312 int err;
313
314 msg = kzalloc(sizeof(*msg), flags);
315 if (!msg)
316 return NULL;
317
318 if (!nents)
319 return msg;
320
321 err = sg_alloc_table(&msg->sgt, nents, flags);
322 if (unlikely(err)) {
323 kfree(msg);
324 msg = NULL;
325 }
326
327 return msg;
328}
329EXPORT_SYMBOL_GPL(hsi_alloc_msg);
330
331/**
332 * hsi_async - Submit an HSI transfer to the controller
333 * @cl: HSI client sending the transfer
334 * @msg: The HSI transfer passed to controller
335 *
336 * The HSI message must have the channel, ttype, complete and destructor
337 * fields set beforehand. If nents > 0 then the client has to initialize
338 * also the scatterlists to point to the buffers to write to or read from.
339 *
340 * HSI controllers relay on pre-allocated buffers from their clients and they
341 * do not allocate buffers on their own.
342 *
343 * Once the HSI message transfer finishes, the HSI controller calls the
344 * complete callback with the status and actual_len fields of the HSI message
345 * updated. The complete callback can be called before returning from
346 * hsi_async.
347 *
348 * Returns -errno on failure or 0 on success
349 */
350int hsi_async(struct hsi_client *cl, struct hsi_msg *msg)
351{
352 struct hsi_port *port = hsi_get_port(cl);
353
354 if (!hsi_port_claimed(cl))
355 return -EACCES;
356
357 WARN_ON_ONCE(!msg->destructor || !msg->complete);
358 msg->cl = cl;
359
360 return port->async(msg);
361}
362EXPORT_SYMBOL_GPL(hsi_async);
363
364/**
365 * hsi_claim_port - Claim the HSI client's port
366 * @cl: HSI client that wants to claim its port
367 * @share: Flag to indicate if the client wants to share the port or not.
368 *
369 * Returns -errno on failure, 0 on success.
370 */
371int hsi_claim_port(struct hsi_client *cl, unsigned int share)
372{
373 struct hsi_port *port = hsi_get_port(cl);
374 int err = 0;
375
376 mutex_lock(&port->lock);
377 if ((port->claimed) && (!port->shared || !share)) {
378 err = -EBUSY;
379 goto out;
380 }
381 if (!try_module_get(to_hsi_controller(port->device.parent)->owner)) {
382 err = -ENODEV;
383 goto out;
384 }
385 port->claimed++;
386 port->shared = !!share;
387 cl->pclaimed = 1;
388out:
389 mutex_unlock(&port->lock);
390
391 return err;
392}
393EXPORT_SYMBOL_GPL(hsi_claim_port);
394
395/**
396 * hsi_release_port - Release the HSI client's port
397 * @cl: HSI client which previously claimed its port
398 */
399void hsi_release_port(struct hsi_client *cl)
400{
401 struct hsi_port *port = hsi_get_port(cl);
402
403 mutex_lock(&port->lock);
404 /* Allow HW driver to do some cleanup */
405 port->release(cl);
406 if (cl->pclaimed)
407 port->claimed--;
408 BUG_ON(port->claimed < 0);
409 cl->pclaimed = 0;
410 if (!port->claimed)
411 port->shared = 0;
412 module_put(to_hsi_controller(port->device.parent)->owner);
413 mutex_unlock(&port->lock);
414}
415EXPORT_SYMBOL_GPL(hsi_release_port);
416
417static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused)
418{
419 if (cl->hsi_start_rx)
420 (*cl->hsi_start_rx)(cl);
421
422 return 0;
423}
424
425static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused)
426{
427 if (cl->hsi_stop_rx)
428 (*cl->hsi_stop_rx)(cl);
429
430 return 0;
431}
432
433static int hsi_port_for_each_client(struct hsi_port *port, void *data,
434 int (*fn)(struct hsi_client *cl, void *data))
435{
436 struct hsi_client *cl;
437
438 spin_lock(&port->clock);
439 list_for_each_entry(cl, &port->clients, link) {
440 spin_unlock(&port->clock);
441 (*fn)(cl, data);
442 spin_lock(&port->clock);
443 }
444 spin_unlock(&port->clock);
445
446 return 0;
447}
448
449/**
450 * hsi_event -Notifies clients about port events
451 * @port: Port where the event occurred
452 * @event: The event type
453 *
454 * Clients should not be concerned about wake line behavior. However, due
455 * to a race condition in HSI HW protocol, clients need to be notified
456 * about wake line changes, so they can implement a workaround for it.
457 *
458 * Events:
459 * HSI_EVENT_START_RX - Incoming wake line high
460 * HSI_EVENT_STOP_RX - Incoming wake line down
461 */
462void hsi_event(struct hsi_port *port, unsigned int event)
463{
464 int (*fn)(struct hsi_client *cl, void *data);
465
466 switch (event) {
467 case HSI_EVENT_START_RX:
468 fn = hsi_start_rx;
469 break;
470 case HSI_EVENT_STOP_RX:
471 fn = hsi_stop_rx;
472 break;
473 default:
474 return;
475 }
476 hsi_port_for_each_client(port, NULL, fn);
477}
478EXPORT_SYMBOL_GPL(hsi_event);
479
480static int __init hsi_init(void)
481{
482 return bus_register(&hsi_bus_type);
483}
484postcore_initcall(hsi_init);
485
486static void __exit hsi_exit(void)
487{
488 bus_unregister(&hsi_bus_type);
489}
490module_exit(hsi_exit);
491
492MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>");
493MODULE_DESCRIPTION("High-speed Synchronous Serial Interface (HSI) framework");
494MODULE_LICENSE("GPL v2");
diff --git a/drivers/hsi/hsi_boardinfo.c b/drivers/hsi/hsi_boardinfo.c
new file mode 100644
index 00000000000..e56bc6da5f9
--- /dev/null
+++ b/drivers/hsi/hsi_boardinfo.c
@@ -0,0 +1,62 @@
1/*
2 * HSI clients registration interface
3 *
4 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
5 *
6 * Contact: Carlos Chinea <carlos.chinea@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22#include <linux/hsi/hsi.h>
23#include <linux/list.h>
24#include <linux/slab.h>
25#include "hsi_core.h"
26
27/*
28 * hsi_board_list is only used internally by the HSI framework.
29 * No one else is allowed to make use of it.
30 */
31LIST_HEAD(hsi_board_list);
32EXPORT_SYMBOL_GPL(hsi_board_list);
33
34/**
35 * hsi_register_board_info - Register HSI clients information
36 * @info: Array of HSI clients on the board
37 * @len: Length of the array
38 *
39 * HSI clients are statically declared and registered on board files.
40 *
41 * HSI clients will be automatically registered to the HSI bus once the
42 * controller and the port where the clients wishes to attach are registered
43 * to it.
44 *
45 * Return -errno on failure, 0 on success.
46 */
47int __init hsi_register_board_info(struct hsi_board_info const *info,
48 unsigned int len)
49{
50 struct hsi_cl_info *cl_info;
51
52 cl_info = kzalloc(sizeof(*cl_info) * len, GFP_KERNEL);
53 if (!cl_info)
54 return -ENOMEM;
55
56 for (; len; len--, info++, cl_info++) {
57 cl_info->info = *info;
58 list_add_tail(&cl_info->list, &hsi_board_list);
59 }
60
61 return 0;
62}
diff --git a/drivers/hsi/hsi_core.h b/drivers/hsi/hsi_core.h
new file mode 100644
index 00000000000..ab5c2fb175f
--- /dev/null
+++ b/drivers/hsi/hsi_core.h
@@ -0,0 +1,35 @@
1/*
2 * HSI framework internal interfaces,
3 *
4 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
5 *
6 * Contact: Carlos Chinea <carlos.chinea@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#ifndef __LINUX_HSI_CORE_H__
24#define __LINUX_HSI_CORE_H__
25
26#include <linux/hsi/hsi.h>
27
28struct hsi_cl_info {
29 struct list_head list;
30 struct hsi_board_info info;
31};
32
33extern struct list_head hsi_board_list;
34
35#endif /* __LINUX_HSI_CORE_H__ */