aboutsummaryrefslogtreecommitdiffstats
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
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
-rw-r--r--Documentation/DocBook/device-drivers.tmpl17
-rw-r--r--Documentation/ioctl/ioctl-number.txt1
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-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
-rw-r--r--include/linux/Kbuild1
-rw-r--r--include/linux/hsi/Kbuild1
-rw-r--r--include/linux/hsi/hsi.h410
-rw-r--r--include/linux/hsi/hsi_char.h63
16 files changed, 1932 insertions, 0 deletions
diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl
index 9c27e5125dd..7514dbf0a67 100644
--- a/Documentation/DocBook/device-drivers.tmpl
+++ b/Documentation/DocBook/device-drivers.tmpl
@@ -446,4 +446,21 @@ X!Idrivers/video/console/fonts.c
446!Edrivers/i2c/i2c-core.c 446!Edrivers/i2c/i2c-core.c
447 </chapter> 447 </chapter>
448 448
449 <chapter id="hsi">
450 <title>High Speed Synchronous Serial Interface (HSI)</title>
451
452 <para>
453 High Speed Synchronous Serial Interface (HSI) is a
454 serial interface mainly used for connecting application
455 engines (APE) with cellular modem engines (CMT) in cellular
456 handsets.
457
458 HSI provides multiplexing for up to 16 logical channels,
459 low-latency and full duplex communication.
460 </para>
461
462!Iinclude/linux/hsi/hsi.h
463!Edrivers/hsi/hsi.c
464 </chapter>
465
449</book> 466</book>
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3b7488fc337..e34b531dc31 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -225,6 +225,7 @@ Code Seq#(hex) Include File Comments
225'j' 00-3F linux/joystick.h 225'j' 00-3F linux/joystick.h
226'k' 00-0F linux/spi/spidev.h conflict! 226'k' 00-0F linux/spi/spidev.h conflict!
227'k' 00-05 video/kyro.h conflict! 227'k' 00-05 video/kyro.h conflict!
228'k' 10-17 linux/hsi/hsi_char.h HSI character device
228'l' 00-3F linux/tcfs_fs.h transparent cryptographic file system 229'l' 00-3F linux/tcfs_fs.h transparent cryptographic file system
229 <http://web.archive.org/web/*/http://mikonos.dia.unisa.it/tcfs> 230 <http://web.archive.org/web/*/http://mikonos.dia.unisa.it/tcfs>
230'l' 40-7F linux/udf_fs_i.h in development: 231'l' 40-7F linux/udf_fs_i.h in development:
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 6f0459cb745..d236aef7e59 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -50,6 +50,8 @@ source "drivers/i2c/Kconfig"
50 50
51source "drivers/spi/Kconfig" 51source "drivers/spi/Kconfig"
52 52
53source "drivers/hsi/Kconfig"
54
53source "drivers/pps/Kconfig" 55source "drivers/pps/Kconfig"
54 56
55source "drivers/ptp/Kconfig" 57source "drivers/ptp/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 262b19d6b62..95952c82bf1 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ATA) += ata/
53obj-$(CONFIG_TARGET_CORE) += target/ 53obj-$(CONFIG_TARGET_CORE) += target/
54obj-$(CONFIG_MTD) += mtd/ 54obj-$(CONFIG_MTD) += mtd/
55obj-$(CONFIG_SPI) += spi/ 55obj-$(CONFIG_SPI) += spi/
56obj-y += hsi/
56obj-y += net/ 57obj-y += net/
57obj-$(CONFIG_ATM) += atm/ 58obj-$(CONFIG_ATM) += atm/
58obj-$(CONFIG_FUSION) += message/ 59obj-$(CONFIG_FUSION) += message/
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__ */
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index d05df281035..3c9b616c834 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -3,6 +3,7 @@ header-y += can/
3header-y += caif/ 3header-y += caif/
4header-y += dvb/ 4header-y += dvb/
5header-y += hdlc/ 5header-y += hdlc/
6header-y += hsi/
6header-y += isdn/ 7header-y += isdn/
7header-y += mmc/ 8header-y += mmc/
8header-y += nfsd/ 9header-y += nfsd/
diff --git a/include/linux/hsi/Kbuild b/include/linux/hsi/Kbuild
new file mode 100644
index 00000000000..271a770b478
--- /dev/null
+++ b/include/linux/hsi/Kbuild
@@ -0,0 +1 @@
header-y += hsi_char.h
diff --git a/include/linux/hsi/hsi.h b/include/linux/hsi/hsi.h
new file mode 100644
index 00000000000..4b178067f40
--- /dev/null
+++ b/include/linux/hsi/hsi.h
@@ -0,0 +1,410 @@
1/*
2 * HSI core header file.
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_H__
24#define __LINUX_HSI_H__
25
26#include <linux/device.h>
27#include <linux/mutex.h>
28#include <linux/scatterlist.h>
29#include <linux/spinlock.h>
30#include <linux/list.h>
31#include <linux/module.h>
32
33/* HSI message ttype */
34#define HSI_MSG_READ 0
35#define HSI_MSG_WRITE 1
36
37/* HSI configuration values */
38enum {
39 HSI_MODE_STREAM = 1,
40 HSI_MODE_FRAME,
41};
42
43enum {
44 HSI_FLOW_SYNC, /* Synchronized flow */
45 HSI_FLOW_PIPE, /* Pipelined flow */
46};
47
48enum {
49 HSI_ARB_RR, /* Round-robin arbitration */
50 HSI_ARB_PRIO, /* Channel priority arbitration */
51};
52
53#define HSI_MAX_CHANNELS 16
54
55/* HSI message status codes */
56enum {
57 HSI_STATUS_COMPLETED, /* Message transfer is completed */
58 HSI_STATUS_PENDING, /* Message pending to be read/write (POLL) */
59 HSI_STATUS_PROCEEDING, /* Message transfer is ongoing */
60 HSI_STATUS_QUEUED, /* Message waiting to be served */
61 HSI_STATUS_ERROR, /* Error when message transfer was ongoing */
62};
63
64/* HSI port event codes */
65enum {
66 HSI_EVENT_START_RX,
67 HSI_EVENT_STOP_RX,
68};
69
70/**
71 * struct hsi_config - Configuration for RX/TX HSI modules
72 * @mode: Bit transmission mode (STREAM or FRAME)
73 * @channels: Number of channels to use [1..16]
74 * @speed: Max bit transmission speed (Kbit/s)
75 * @flow: RX flow type (SYNCHRONIZED or PIPELINE)
76 * @arb_mode: Arbitration mode for TX frame (Round robin, priority)
77 */
78struct hsi_config {
79 unsigned int mode;
80 unsigned int channels;
81 unsigned int speed;
82 union {
83 unsigned int flow; /* RX only */
84 unsigned int arb_mode; /* TX only */
85 };
86};
87
88/**
89 * struct hsi_board_info - HSI client board info
90 * @name: Name for the HSI device
91 * @hsi_id: HSI controller id where the client sits
92 * @port: Port number in the controller where the client sits
93 * @tx_cfg: HSI TX configuration
94 * @rx_cfg: HSI RX configuration
95 * @platform_data: Platform related data
96 * @archdata: Architecture-dependent device data
97 */
98struct hsi_board_info {
99 const char *name;
100 unsigned int hsi_id;
101 unsigned int port;
102 struct hsi_config tx_cfg;
103 struct hsi_config rx_cfg;
104 void *platform_data;
105 struct dev_archdata *archdata;
106};
107
108#ifdef CONFIG_HSI_BOARDINFO
109extern int hsi_register_board_info(struct hsi_board_info const *info,
110 unsigned int len);
111#else
112static inline int hsi_register_board_info(struct hsi_board_info const *info,
113 unsigned int len)
114{
115 return 0;
116}
117#endif /* CONFIG_HSI_BOARDINFO */
118
119/**
120 * struct hsi_client - HSI client attached to an HSI port
121 * @device: Driver model representation of the device
122 * @tx_cfg: HSI TX configuration
123 * @rx_cfg: HSI RX configuration
124 * @hsi_start_rx: Called after incoming wake line goes high
125 * @hsi_stop_rx: Called after incoming wake line goes low
126 */
127struct hsi_client {
128 struct device device;
129 struct hsi_config tx_cfg;
130 struct hsi_config rx_cfg;
131 void (*hsi_start_rx)(struct hsi_client *cl);
132 void (*hsi_stop_rx)(struct hsi_client *cl);
133 /* private: */
134 unsigned int pclaimed:1;
135 struct list_head link;
136};
137
138#define to_hsi_client(dev) container_of(dev, struct hsi_client, device)
139
140static inline void hsi_client_set_drvdata(struct hsi_client *cl, void *data)
141{
142 dev_set_drvdata(&cl->device, data);
143}
144
145static inline void *hsi_client_drvdata(struct hsi_client *cl)
146{
147 return dev_get_drvdata(&cl->device);
148}
149
150/**
151 * struct hsi_client_driver - Driver associated to an HSI client
152 * @driver: Driver model representation of the driver
153 */
154struct hsi_client_driver {
155 struct device_driver driver;
156};
157
158#define to_hsi_client_driver(drv) container_of(drv, struct hsi_client_driver,\
159 driver)
160
161int hsi_register_client_driver(struct hsi_client_driver *drv);
162
163static inline void hsi_unregister_client_driver(struct hsi_client_driver *drv)
164{
165 driver_unregister(&drv->driver);
166}
167
168/**
169 * struct hsi_msg - HSI message descriptor
170 * @link: Free to use by the current descriptor owner
171 * @cl: HSI device client that issues the transfer
172 * @sgt: Head of the scatterlist array
173 * @context: Client context data associated to the transfer
174 * @complete: Transfer completion callback
175 * @destructor: Destructor to free resources when flushing
176 * @status: Status of the transfer when completed
177 * @actual_len: Actual length of data transfered on completion
178 * @channel: Channel were to TX/RX the message
179 * @ttype: Transfer type (TX if set, RX otherwise)
180 * @break_frame: if true HSI will send/receive a break frame. Data buffers are
181 * ignored in the request.
182 */
183struct hsi_msg {
184 struct list_head link;
185 struct hsi_client *cl;
186 struct sg_table sgt;
187 void *context;
188
189 void (*complete)(struct hsi_msg *msg);
190 void (*destructor)(struct hsi_msg *msg);
191
192 int status;
193 unsigned int actual_len;
194 unsigned int channel;
195 unsigned int ttype:1;
196 unsigned int break_frame:1;
197};
198
199struct hsi_msg *hsi_alloc_msg(unsigned int n_frag, gfp_t flags);
200void hsi_free_msg(struct hsi_msg *msg);
201
202/**
203 * struct hsi_port - HSI port device
204 * @device: Driver model representation of the device
205 * @tx_cfg: Current TX path configuration
206 * @rx_cfg: Current RX path configuration
207 * @num: Port number
208 * @shared: Set when port can be shared by different clients
209 * @claimed: Reference count of clients which claimed the port
210 * @lock: Serialize port claim
211 * @async: Asynchronous transfer callback
212 * @setup: Callback to set the HSI client configuration
213 * @flush: Callback to clean the HW state and destroy all pending transfers
214 * @start_tx: Callback to inform that a client wants to TX data
215 * @stop_tx: Callback to inform that a client no longer wishes to TX data
216 * @release: Callback to inform that a client no longer uses the port
217 * @clients: List of hsi_clients using the port.
218 * @clock: Lock to serialize access to the clients list.
219 */
220struct hsi_port {
221 struct device device;
222 struct hsi_config tx_cfg;
223 struct hsi_config rx_cfg;
224 unsigned int num;
225 unsigned int shared:1;
226 int claimed;
227 struct mutex lock;
228 int (*async)(struct hsi_msg *msg);
229 int (*setup)(struct hsi_client *cl);
230 int (*flush)(struct hsi_client *cl);
231 int (*start_tx)(struct hsi_client *cl);
232 int (*stop_tx)(struct hsi_client *cl);
233 int (*release)(struct hsi_client *cl);
234 struct list_head clients;
235 spinlock_t clock;
236};
237
238#define to_hsi_port(dev) container_of(dev, struct hsi_port, device)
239#define hsi_get_port(cl) to_hsi_port((cl)->device.parent)
240
241void hsi_event(struct hsi_port *port, unsigned int event);
242int hsi_claim_port(struct hsi_client *cl, unsigned int share);
243void hsi_release_port(struct hsi_client *cl);
244
245static inline int hsi_port_claimed(struct hsi_client *cl)
246{
247 return cl->pclaimed;
248}
249
250static inline void hsi_port_set_drvdata(struct hsi_port *port, void *data)
251{
252 dev_set_drvdata(&port->device, data);
253}
254
255static inline void *hsi_port_drvdata(struct hsi_port *port)
256{
257 return dev_get_drvdata(&port->device);
258}
259
260/**
261 * struct hsi_controller - HSI controller device
262 * @device: Driver model representation of the device
263 * @owner: Pointer to the module owning the controller
264 * @id: HSI controller ID
265 * @num_ports: Number of ports in the HSI controller
266 * @port: Array of HSI ports
267 */
268struct hsi_controller {
269 struct device device;
270 struct module *owner;
271 unsigned int id;
272 unsigned int num_ports;
273 struct hsi_port *port;
274};
275
276#define to_hsi_controller(dev) container_of(dev, struct hsi_controller, device)
277
278struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags);
279void hsi_free_controller(struct hsi_controller *hsi);
280int hsi_register_controller(struct hsi_controller *hsi);
281void hsi_unregister_controller(struct hsi_controller *hsi);
282
283static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi,
284 void *data)
285{
286 dev_set_drvdata(&hsi->device, data);
287}
288
289static inline void *hsi_controller_drvdata(struct hsi_controller *hsi)
290{
291 return dev_get_drvdata(&hsi->device);
292}
293
294static inline struct hsi_port *hsi_find_port_num(struct hsi_controller *hsi,
295 unsigned int num)
296{
297 return (num < hsi->num_ports) ? &hsi->port[num] : NULL;
298}
299
300/*
301 * API for HSI clients
302 */
303int hsi_async(struct hsi_client *cl, struct hsi_msg *msg);
304
305/**
306 * hsi_id - Get HSI controller ID associated to a client
307 * @cl: Pointer to a HSI client
308 *
309 * Return the controller id where the client is attached to
310 */
311static inline unsigned int hsi_id(struct hsi_client *cl)
312{
313 return to_hsi_controller(cl->device.parent->parent)->id;
314}
315
316/**
317 * hsi_port_id - Gets the port number a client is attached to
318 * @cl: Pointer to HSI client
319 *
320 * Return the port number associated to the client
321 */
322static inline unsigned int hsi_port_id(struct hsi_client *cl)
323{
324 return to_hsi_port(cl->device.parent)->num;
325}
326
327/**
328 * hsi_setup - Configure the client's port
329 * @cl: Pointer to the HSI client
330 *
331 * When sharing ports, clients should either relay on a single
332 * client setup or have the same setup for all of them.
333 *
334 * Return -errno on failure, 0 on success
335 */
336static inline int hsi_setup(struct hsi_client *cl)
337{
338 if (!hsi_port_claimed(cl))
339 return -EACCES;
340 return hsi_get_port(cl)->setup(cl);
341}
342
343/**
344 * hsi_flush - Flush all pending transactions on the client's port
345 * @cl: Pointer to the HSI client
346 *
347 * This function will destroy all pending hsi_msg in the port and reset
348 * the HW port so it is ready to receive and transmit from a clean state.
349 *
350 * Return -errno on failure, 0 on success
351 */
352static inline int hsi_flush(struct hsi_client *cl)
353{
354 if (!hsi_port_claimed(cl))
355 return -EACCES;
356 return hsi_get_port(cl)->flush(cl);
357}
358
359/**
360 * hsi_async_read - Submit a read transfer
361 * @cl: Pointer to the HSI client
362 * @msg: HSI message descriptor of the transfer
363 *
364 * Return -errno on failure, 0 on success
365 */
366static inline int hsi_async_read(struct hsi_client *cl, struct hsi_msg *msg)
367{
368 msg->ttype = HSI_MSG_READ;
369 return hsi_async(cl, msg);
370}
371
372/**
373 * hsi_async_write - Submit a write transfer
374 * @cl: Pointer to the HSI client
375 * @msg: HSI message descriptor of the transfer
376 *
377 * Return -errno on failure, 0 on success
378 */
379static inline int hsi_async_write(struct hsi_client *cl, struct hsi_msg *msg)
380{
381 msg->ttype = HSI_MSG_WRITE;
382 return hsi_async(cl, msg);
383}
384
385/**
386 * hsi_start_tx - Signal the port that the client wants to start a TX
387 * @cl: Pointer to the HSI client
388 *
389 * Return -errno on failure, 0 on success
390 */
391static inline int hsi_start_tx(struct hsi_client *cl)
392{
393 if (!hsi_port_claimed(cl))
394 return -EACCES;
395 return hsi_get_port(cl)->start_tx(cl);
396}
397
398/**
399 * hsi_stop_tx - Signal the port that the client no longer wants to transmit
400 * @cl: Pointer to the HSI client
401 *
402 * Return -errno on failure, 0 on success
403 */
404static inline int hsi_stop_tx(struct hsi_client *cl)
405{
406 if (!hsi_port_claimed(cl))
407 return -EACCES;
408 return hsi_get_port(cl)->stop_tx(cl);
409}
410#endif /* __LINUX_HSI_H__ */
diff --git a/include/linux/hsi/hsi_char.h b/include/linux/hsi/hsi_char.h
new file mode 100644
index 00000000000..76160b4f455
--- /dev/null
+++ b/include/linux/hsi/hsi_char.h
@@ -0,0 +1,63 @@
1/*
2 * Part of the HSI character device driver.
3 *
4 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
5 *
6 * Contact: Andras Domokos <andras.domokos at 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
24#ifndef __HSI_CHAR_H
25#define __HSI_CHAR_H
26
27#define HSI_CHAR_MAGIC 'k'
28#define HSC_IOW(num, dtype) _IOW(HSI_CHAR_MAGIC, num, dtype)
29#define HSC_IOR(num, dtype) _IOR(HSI_CHAR_MAGIC, num, dtype)
30#define HSC_IOWR(num, dtype) _IOWR(HSI_CHAR_MAGIC, num, dtype)
31#define HSC_IO(num) _IO(HSI_CHAR_MAGIC, num)
32
33#define HSC_RESET HSC_IO(16)
34#define HSC_SET_PM HSC_IO(17)
35#define HSC_SEND_BREAK HSC_IO(18)
36#define HSC_SET_RX HSC_IOW(19, struct hsc_rx_config)
37#define HSC_GET_RX HSC_IOW(20, struct hsc_rx_config)
38#define HSC_SET_TX HSC_IOW(21, struct hsc_tx_config)
39#define HSC_GET_TX HSC_IOW(22, struct hsc_tx_config)
40
41#define HSC_PM_DISABLE 0
42#define HSC_PM_ENABLE 1
43
44#define HSC_MODE_STREAM 1
45#define HSC_MODE_FRAME 2
46#define HSC_FLOW_SYNC 0
47#define HSC_ARB_RR 0
48#define HSC_ARB_PRIO 1
49
50struct hsc_rx_config {
51 uint32_t mode;
52 uint32_t flow;
53 uint32_t channels;
54};
55
56struct hsc_tx_config {
57 uint32_t mode;
58 uint32_t channels;
59 uint32_t speed;
60 uint32_t arb_mode;
61};
62
63#endif /* __HSI_CHAR_H */