14e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos/*
24e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * HSI character device driver, implements the character device
34e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * interface.
44e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos *
54e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * Copyright (C) 2010 Nokia Corporation. All rights reserved.
64e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos *
74e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * Contact: Andras Domokos <andras.domokos@nokia.com>
84e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos *
94e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * This program is free software; you can redistribute it and/or
104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * modify it under the terms of the GNU General Public License
114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * version 2 as published by the Free Software Foundation.
124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos *
134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * This program is distributed in the hope that it will be useful, but
144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * WITHOUT ANY WARRANTY; without even the implied warranty of
154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * General Public License for more details.
174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos *
184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * You should have received a copy of the GNU General Public License
194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * along with this program; if not, write to the Free Software
204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * 02110-1301 USA
224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos */
234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/errno.h>
254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/types.h>
264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/atomic.h>
274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/kernel.h>
284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/init.h>
294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/module.h>
304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/mutex.h>
314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/list.h>
324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/slab.h>
334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/kmemleak.h>
344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/ioctl.h>
354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/wait.h>
364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/fs.h>
374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/sched.h>
384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/device.h>
394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/cdev.h>
404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/uaccess.h>
414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/scatterlist.h>
424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/stat.h>
434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/hsi/hsi.h>
444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#include <linux/hsi/hsi_char.h>
454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_DEVS		16 /* Num of channels */
474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_MSGS		4
484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_RXBREAK		0
504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_ID_BITS		6
524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_PORT_ID_BITS	4
534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_ID_MASK		3
544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_PORT_ID_MASK	3
554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_CH_MASK		0xf
564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos/*
584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * We support up to 4 controllers that can have up to 4
594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * ports, which should currently be more than enough.
604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos */
614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos#define HSC_BASEMINOR(id, port_id) \
624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		((((id) & HSC_ID_MASK) << HSC_ID_BITS) | \
634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		(((port_id) & HSC_PORT_ID_MASK) << HSC_PORT_ID_BITS))
644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosenum {
664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	HSC_CH_OPEN,
674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	HSC_CH_READ,
684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	HSC_CH_WRITE,
694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	HSC_CH_WLINE,
704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos};
714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosenum {
734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	HSC_RX,
744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	HSC_TX,
754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos};
764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstruct hsc_client_data;
784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos/**
794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * struct hsc_channel - hsi_char internal channel data
804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @ch: channel number
814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @flags: Keeps state of the channel (open/close, reading, writing)
824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @free_msgs_list: List of free HSI messages/requests
834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @rx_msgs_queue: List of pending RX requests
844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @tx_msgs_queue: List of pending TX requests
854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @lock: Serialize access to the lists
864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @cl: reference to the associated hsi_client
874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @cl_data: reference to the client data that this channels belongs to
884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @rx_wait: RX requests wait queue
894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @tx_wait: TX requests wait queue
904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos */
914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstruct hsc_channel {
924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned int		ch;
934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned long		flags;
944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct list_head	free_msgs_list;
954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct list_head	rx_msgs_queue;
964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct list_head	tx_msgs_queue;
974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spinlock_t		lock;
984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_client	*cl;
994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data;
1004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	wait_queue_head_t	rx_wait;
1014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	wait_queue_head_t	tx_wait;
1024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos};
1034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos/**
1054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * struct hsc_client_data - hsi_char internal client data
1064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @cdev: Characther device associated to the hsi_client
1074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @lock: Lock to serialize open/close access
1084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @flags: Keeps track of port state (rx hwbreak armed)
1094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @usecnt: Use count for claiming the HSI port (mutex protected)
1104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @cl: Referece to the HSI client
1114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos * @channels: Array of channels accessible by the client
1124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos */
1134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstruct hsc_client_data {
1144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct cdev		cdev;
1154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct mutex		lock;
1164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned long		flags;
1174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned int		usecnt;
1184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_client	*cl;
1194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel	channels[HSC_DEVS];
1204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos};
1214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos/* Stores the major number dynamically allocated for hsi_char */
1234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic unsigned int hsc_major;
1244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos/* Maximum buffer size that hsi_char will accept from userspace */
1254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic unsigned int max_data_size = 0x1000;
126fdadb6e9a5cf65c7662b2ca817856f187d05ab7dCarlos Chineamodule_param(max_data_size, uint, 0);
1274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras DomokosMODULE_PARM_DESC(max_data_size, "max read/write data size [4,8..65536] (^2)");
1284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_add_tail(struct hsc_channel *channel, struct hsi_msg *msg,
1304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos							struct list_head *queue)
1314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
1324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned long flags;
1334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_lock_irqsave(&channel->lock, flags);
1354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	list_add_tail(&msg->link, queue);
1364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_unlock_irqrestore(&channel->lock, flags);
1374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
1384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic struct hsi_msg *hsc_get_first_msg(struct hsc_channel *channel,
1404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos							struct list_head *queue)
1414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
1424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg = NULL;
1434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned long flags;
1444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_lock_irqsave(&channel->lock, flags);
1464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (list_empty(queue))
1484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
1494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = list_first_entry(queue, struct hsi_msg, link);
1514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	list_del(&msg->link);
1524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout:
1534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_unlock_irqrestore(&channel->lock, flags);
1544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return msg;
1564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
1574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline void hsc_msg_free(struct hsi_msg *msg)
1594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
1604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	kfree(sg_virt(msg->sgt.sgl));
1614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsi_free_msg(msg);
1624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
1634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_free_list(struct list_head *list)
1654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
1664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg, *tmp;
1674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	list_for_each_entry_safe(msg, tmp, list, link) {
1694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		list_del(&msg->link);
1704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_msg_free(msg);
1714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
1724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
1734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_reset_list(struct hsc_channel *channel, struct list_head *l)
1754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
1764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned long flags;
1774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	LIST_HEAD(list);
1784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_lock_irqsave(&channel->lock, flags);
1804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	list_splice_init(l, &list);
1814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_unlock_irqrestore(&channel->lock, flags);
1824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_free_list(&list);
1844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
1854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline struct hsi_msg *hsc_msg_alloc(unsigned int alloc_size)
1874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
1884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg;
1894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	void *buf;
1904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
1914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsi_alloc_msg(1, GFP_KERNEL);
1924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!msg)
1934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
1944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	buf = kmalloc(alloc_size, GFP_KERNEL);
1954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!buf) {
1964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_free_msg(msg);
1974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
1984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
1994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	sg_init_one(msg->sgt.sgl, buf, alloc_size);
2004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	/* Ignore false positive, due to sg pointer handling */
2014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	kmemleak_ignore(buf);
2024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return msg;
2044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout:
2054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return NULL;
2064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline int hsc_msgs_alloc(struct hsc_channel *channel)
2094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg;
2114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int i;
2124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	for (i = 0; i < HSC_MSGS; i++) {
2144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		msg = hsc_msg_alloc(max_data_size);
2154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (!msg)
2164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			goto out;
2174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		msg->channel = channel->ch;
2184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		list_add_tail(&msg->link, &channel->free_msgs_list);
2194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
2204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return 0;
2224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout:
2234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_free_list(&channel->free_msgs_list);
2244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return -ENOMEM;
2264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline unsigned int hsc_msg_len_get(struct hsi_msg *msg)
2294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return msg->sgt.sgl->length;
2314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline void hsc_msg_len_set(struct hsi_msg *msg, unsigned int len)
2344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->sgt.sgl->length = len;
2364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_rx_completed(struct hsi_msg *msg)
2394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
2414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = cl_data->channels + msg->channel;
2424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_bit(HSC_CH_READ, &channel->flags)) {
2444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->rx_msgs_queue);
2454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		wake_up(&channel->rx_wait);
2464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	} else {
2474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->free_msgs_list);
2484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
2494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_rx_msg_destructor(struct hsi_msg *msg)
2524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->status = HSI_STATUS_ERROR;
2544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_msg_len_set(msg, 0);
2554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_rx_completed(msg);
2564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_tx_completed(struct hsi_msg *msg)
2594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
2614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = cl_data->channels + msg->channel;
2624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_bit(HSC_CH_WRITE, &channel->flags)) {
2644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->tx_msgs_queue);
2654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		wake_up(&channel->tx_wait);
2664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	} else {
2674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->free_msgs_list);
2684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
2694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_tx_msg_destructor(struct hsi_msg *msg)
2724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->status = HSI_STATUS_ERROR;
2744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_msg_len_set(msg, 0);
2754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_tx_completed(msg);
2764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_break_req_destructor(struct hsi_msg *msg)
2794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
2814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsi_free_msg(msg);
2834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	clear_bit(HSC_RXBREAK, &cl_data->flags);
2844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
2854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void hsc_break_received(struct hsi_msg *msg)
2874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
2884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = hsi_client_drvdata(msg->cl);
2894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = cl_data->channels;
2904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int i, ret;
2914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	/* Broadcast HWBREAK on all channels */
2934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	for (i = 0; i < HSC_DEVS; i++, channel++) {
2944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		struct hsi_msg *msg2;
2954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
2964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (!test_bit(HSC_CH_READ, &channel->flags))
2974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			continue;
2984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		msg2 = hsc_get_first_msg(channel, &channel->free_msgs_list);
2994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (!msg2)
3004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			continue;
3014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		clear_bit(HSC_CH_READ, &channel->flags);
3024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_msg_len_set(msg2, 0);
3034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		msg2->status = HSI_STATUS_COMPLETED;
3044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg2, &channel->rx_msgs_queue);
3054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		wake_up(&channel->rx_wait);
3064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
3074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsi_flush(msg->cl);
3084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_async_read(msg->cl, msg);
3094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0)
3104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_break_req_destructor(msg);
3114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
3124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int hsc_break_request(struct hsi_client *cl)
3144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
3154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = hsi_client_drvdata(cl);
3164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg;
3174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret;
3184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_and_set_bit(HSC_RXBREAK, &cl_data->flags))
3204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EBUSY;
3214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsi_alloc_msg(0, GFP_KERNEL);
3234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!msg) {
3244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		clear_bit(HSC_RXBREAK, &cl_data->flags);
3254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ENOMEM;
3264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
3274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->break_frame = 1;
3284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->complete = hsc_break_received;
3294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->destructor = hsc_break_req_destructor;
3304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_async_read(cl, msg);
3314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0)
3324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_break_req_destructor(msg);
3334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
3354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
3364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int hsc_break_send(struct hsi_client *cl)
3384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
3394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg;
3404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret;
3414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsi_alloc_msg(0, GFP_ATOMIC);
3434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!msg)
3444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ENOMEM;
3454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->break_frame = 1;
3464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->complete = hsi_free_msg;
3474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->destructor = hsi_free_msg;
3484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_async_write(cl, msg);
3494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0)
3504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_free_msg(msg);
3514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
3534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
3544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int hsc_rx_set(struct hsi_client *cl, struct hsc_rx_config *rxc)
3564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
3574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_config tmp;
3584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret;
3594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((rxc->mode != HSI_MODE_STREAM) && (rxc->mode != HSI_MODE_FRAME))
3614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
3624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((rxc->channels == 0) || (rxc->channels > HSC_DEVS))
3634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
3644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (rxc->channels & (rxc->channels - 1))
3654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
3664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((rxc->flow != HSI_FLOW_SYNC) && (rxc->flow != HSI_FLOW_PIPE))
3674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
3684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	tmp = cl->rx_cfg;
3694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->rx_cfg.mode = rxc->mode;
3704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->rx_cfg.channels = rxc->channels;
3714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->rx_cfg.flow = rxc->flow;
3724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_setup(cl);
3734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
3744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		cl->rx_cfg = tmp;
3754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return ret;
3764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
3774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (rxc->mode == HSI_MODE_FRAME)
3784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_break_request(cl);
3794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
3814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
3824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline void hsc_rx_get(struct hsi_client *cl, struct hsc_rx_config *rxc)
3844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
3854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	rxc->mode = cl->rx_cfg.mode;
3864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	rxc->channels = cl->rx_cfg.channels;
3874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	rxc->flow = cl->rx_cfg.flow;
3884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
3894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int hsc_tx_set(struct hsi_client *cl, struct hsc_tx_config *txc)
3914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
3924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_config tmp;
3934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret;
3944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
3954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((txc->mode != HSI_MODE_STREAM) && (txc->mode != HSI_MODE_FRAME))
3964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
3974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((txc->channels == 0) || (txc->channels > HSC_DEVS))
3984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
3994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (txc->channels & (txc->channels - 1))
4004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
4014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((txc->arb_mode != HSI_ARB_RR) && (txc->arb_mode != HSI_ARB_PRIO))
4024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
4034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	tmp = cl->tx_cfg;
4044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->tx_cfg.mode = txc->mode;
4054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->tx_cfg.channels = txc->channels;
4064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->tx_cfg.speed = txc->speed;
4074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl->tx_cfg.arb_mode = txc->arb_mode;
4084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_setup(cl);
4094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
4104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		cl->tx_cfg = tmp;
4114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return ret;
4124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
4134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
4154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
4164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline void hsc_tx_get(struct hsi_client *cl, struct hsc_tx_config *txc)
4184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
4194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	txc->mode = cl->tx_cfg.mode;
4204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	txc->channels = cl->tx_cfg.channels;
4214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	txc->speed = cl->tx_cfg.speed;
4224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	txc->arb_mode = cl->tx_cfg.arb_mode;
4234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
4244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic ssize_t hsc_read(struct file *file, char __user *buf, size_t len,
4264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos						loff_t *ppos __maybe_unused)
4274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
4284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = file->private_data;
4294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg;
4304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ssize_t ret;
4314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (len == 0)
4334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return 0;
4344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!IS_ALIGNED(len, sizeof(u32)))
4354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
4364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (len > max_data_size)
4374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		len = max_data_size;
4384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (channel->ch >= channel->cl->rx_cfg.channels)
4394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ECHRNG;
4404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_and_set_bit(HSC_CH_READ, &channel->flags))
4414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EBUSY;
4424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsc_get_first_msg(channel, &channel->free_msgs_list);
4434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!msg) {
4444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		ret = -ENOSPC;
4454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
4464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
4474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_msg_len_set(msg, len);
4484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->complete = hsc_rx_completed;
4494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->destructor = hsc_rx_msg_destructor;
4504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_async_read(channel->cl, msg);
4514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
4524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->free_msgs_list);
4534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
4544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
4554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = wait_event_interruptible(channel->rx_wait,
4574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos					!list_empty(&channel->rx_msgs_queue));
4584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
4594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		clear_bit(HSC_CH_READ, &channel->flags);
4604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_flush(channel->cl);
4614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINTR;
4624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
4634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsc_get_first_msg(channel, &channel->rx_msgs_queue);
4654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (msg) {
4664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (msg->status != HSI_STATUS_ERROR) {
4674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = copy_to_user((void __user *)buf,
4684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			sg_virt(msg->sgt.sgl), hsc_msg_len_get(msg));
4694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			if (ret)
4704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos				ret = -EFAULT;
4714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			else
4724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos				ret = hsc_msg_len_get(msg);
4734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		} else {
4744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = -EIO;
4754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		}
4764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->free_msgs_list);
4774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
4784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout:
4794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	clear_bit(HSC_CH_READ, &channel->flags);
4804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
4824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
4834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic ssize_t hsc_write(struct file *file, const char __user *buf, size_t len,
4854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos						loff_t *ppos __maybe_unused)
4864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
4874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = file->private_data;
4884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_msg *msg;
4894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ssize_t ret;
4904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
4914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((len == 0) || !IS_ALIGNED(len, sizeof(u32)))
4924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
4934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (len > max_data_size)
4944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		len = max_data_size;
4954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (channel->ch >= channel->cl->tx_cfg.channels)
4964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ECHRNG;
4974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_and_set_bit(HSC_CH_WRITE, &channel->flags))
4984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EBUSY;
4994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsc_get_first_msg(channel, &channel->free_msgs_list);
5004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!msg) {
5014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		clear_bit(HSC_CH_WRITE, &channel->flags);
5024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ENOSPC;
5034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
5044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (copy_from_user(sg_virt(msg->sgt.sgl), (void __user *)buf, len)) {
5054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		ret = -EFAULT;
5064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
5074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
5084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_msg_len_set(msg, len);
5094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->complete = hsc_tx_completed;
5104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg->destructor = hsc_tx_msg_destructor;
5114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_async_write(channel->cl, msg);
5124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0)
5134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
5144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = wait_event_interruptible(channel->tx_wait,
5164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos					!list_empty(&channel->tx_msgs_queue));
5174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
5184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		clear_bit(HSC_CH_WRITE, &channel->flags);
5194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_flush(channel->cl);
5204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINTR;
5214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
5224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	msg = hsc_get_first_msg(channel, &channel->tx_msgs_queue);
5244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (msg) {
5254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (msg->status == HSI_STATUS_ERROR)
5264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = -EIO;
5274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		else
5284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = hsc_msg_len_get(msg);
5294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_add_tail(channel, msg, &channel->free_msgs_list);
5314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
5324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout:
5334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	clear_bit(HSC_CH_WRITE, &channel->flags);
5344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
5364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
5374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic long hsc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
5394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
5404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = file->private_data;
5414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned int state;
5424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_rx_config rxc;
5434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_tx_config txc;
5444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	long ret = 0;
5454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	switch (cmd) {
5474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_RESET:
5484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_flush(channel->cl);
5494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		break;
5504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_SET_PM:
5514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
5524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			return -EFAULT;
5534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (state == HSC_PM_DISABLE) {
5544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			if (test_and_set_bit(HSC_CH_WLINE, &channel->flags))
5554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos				return -EINVAL;
5564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = hsi_start_tx(channel->cl);
5574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		} else if (state == HSC_PM_ENABLE) {
5584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			if (!test_and_clear_bit(HSC_CH_WLINE, &channel->flags))
5594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos				return -EINVAL;
5604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = hsi_stop_tx(channel->cl);
5614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		} else {
5624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			ret = -EINVAL;
5634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		}
5644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		break;
5654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_SEND_BREAK:
5664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return hsc_break_send(channel->cl);
5674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_SET_RX:
5684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (copy_from_user(&rxc, (void __user *)arg, sizeof(rxc)))
5694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			return -EFAULT;
5704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return hsc_rx_set(channel->cl, &rxc);
5714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_GET_RX:
5724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_rx_get(channel->cl, &rxc);
5734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (copy_to_user((void __user *)arg, &rxc, sizeof(rxc)))
5744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			return -EFAULT;
5754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		break;
5764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_SET_TX:
5774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (copy_from_user(&txc, (void __user *)arg, sizeof(txc)))
5784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			return -EFAULT;
5794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return hsc_tx_set(channel->cl, &txc);
5804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	case HSC_GET_TX:
5814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_tx_get(channel->cl, &txc);
5824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (copy_to_user((void __user *)arg, &txc, sizeof(txc)))
5834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			return -EFAULT;
5844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		break;
5854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	default:
5864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ENOIOCTLCMD;
5874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
5884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
5904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
5914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic inline void __hsc_port_release(struct hsc_client_data *cl_data)
5934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
5944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	BUG_ON(cl_data->usecnt == 0);
5954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
5964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (--cl_data->usecnt == 0) {
5974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_flush(cl_data->cl);
5984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_release_port(cl_data->cl);
5994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
6004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
6014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int hsc_open(struct inode *inode, struct file *file)
6034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
6044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data;
6054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel;
6064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret = 0;
6074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	pr_debug("open, minor = %d\n", iminor(inode));
6094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl_data = container_of(inode->i_cdev, struct hsc_client_data, cdev);
6114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	mutex_lock(&cl_data->lock);
6124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	channel = cl_data->channels + (iminor(inode) & HSC_CH_MASK);
6134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_and_set_bit(HSC_CH_OPEN, &channel->flags)) {
6154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		ret = -EBUSY;
6164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
6174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
6184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	/*
6194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	 * Check if we have already claimed the port associated to the HSI
6204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	 * client. If not then try to claim it, else increase its refcount
6214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	 */
6224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (cl_data->usecnt == 0) {
6234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		ret = hsi_claim_port(cl_data->cl, 0);
6244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (ret < 0)
6254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			goto out;
6264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_setup(cl_data->cl);
6274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
6284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl_data->usecnt++;
6294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsc_msgs_alloc(channel);
6314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
6324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		__hsc_port_release(cl_data);
6334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out;
6344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
6354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	file->private_data = channel;
6374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	mutex_unlock(&cl_data->lock);
6384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
6404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout:
6414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	mutex_unlock(&cl_data->lock);
6424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
6444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
6454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int hsc_release(struct inode *inode __maybe_unused, struct file *file)
6474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
6484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel = file->private_data;
6494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = channel->cl_data;
6504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	mutex_lock(&cl_data->lock);
6524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	file->private_data = NULL;
6534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (test_and_clear_bit(HSC_CH_WLINE, &channel->flags))
6544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsi_stop_tx(channel->cl);
6554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	__hsc_port_release(cl_data);
6564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_reset_list(channel, &channel->rx_msgs_queue);
6574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_reset_list(channel, &channel->tx_msgs_queue);
6584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_reset_list(channel, &channel->free_msgs_list);
6594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	clear_bit(HSC_CH_READ, &channel->flags);
6604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	clear_bit(HSC_CH_WRITE, &channel->flags);
6614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	clear_bit(HSC_CH_OPEN, &channel->flags);
6624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	wake_up(&channel->rx_wait);
6634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	wake_up(&channel->tx_wait);
6644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	mutex_unlock(&cl_data->lock);
6654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return 0;
6674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
6684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic const struct file_operations hsc_fops = {
6704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.owner		= THIS_MODULE,
6714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.read		= hsc_read,
6724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.write		= hsc_write,
6734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.unlocked_ioctl	= hsc_ioctl,
6744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.open		= hsc_open,
6754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.release	= hsc_release,
6764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos};
6774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void __devinit hsc_channel_init(struct hsc_channel *channel)
6794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
6804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	init_waitqueue_head(&channel->rx_wait);
6814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	init_waitqueue_head(&channel->tx_wait);
6824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	spin_lock_init(&channel->lock);
6834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	INIT_LIST_HEAD(&channel->free_msgs_list);
6844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	INIT_LIST_HEAD(&channel->rx_msgs_queue);
6854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	INIT_LIST_HEAD(&channel->tx_msgs_queue);
6864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
6874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int __devinit hsc_probe(struct device *dev)
6894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
6904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	const char devname[] = "hsi_char";
6914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data;
6924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_channel *channel;
6934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_client *cl = to_hsi_client(dev);
6944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unsigned int hsc_baseminor;
6954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	dev_t hsc_dev;
6964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret;
6974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int i;
6984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
6994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl_data = kzalloc(sizeof(*cl_data), GFP_KERNEL);
7004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!cl_data) {
7014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		dev_err(dev, "Could not allocate hsc_client_data\n");
7024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -ENOMEM;
7034e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7044e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsc_baseminor = HSC_BASEMINOR(hsi_id(cl), hsi_port_id(cl));
7054e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (!hsc_major) {
7064e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		ret = alloc_chrdev_region(&hsc_dev, hsc_baseminor,
7074e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos						HSC_DEVS, devname);
7084e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		if (ret > 0)
7094e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos			hsc_major = MAJOR(hsc_dev);
7104e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	} else {
7114e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_dev = MKDEV(hsc_major, hsc_baseminor);
7124e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		ret = register_chrdev_region(hsc_dev, HSC_DEVS, devname);
7134e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7144e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret < 0) {
7154e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		dev_err(dev, "Device %s allocation failed %d\n",
7164e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos					hsc_major ? "minor" : "major", ret);
7174e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out1;
7184e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7194e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	mutex_init(&cl_data->lock);
7204e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsi_client_set_drvdata(cl, cl_data);
7214e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cdev_init(&cl_data->cdev, &hsc_fops);
7224e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl_data->cdev.owner = THIS_MODULE;
7234e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cl_data->cl = cl;
7244e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	for (i = 0, channel = cl_data->channels; i < HSC_DEVS; i++, channel++) {
7254e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		hsc_channel_init(channel);
7264e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		channel->ch = i;
7274e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		channel->cl = cl;
7284e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		channel->cl_data = cl_data;
7294e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7304e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7314e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	/* 1 hsi client -> N char devices (one for each channel) */
7324e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = cdev_add(&cl_data->cdev, hsc_dev, HSC_DEVS);
7334e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret) {
7344e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		dev_err(dev, "Could not add char device %d\n", ret);
7354e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		goto out2;
7364e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7374e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7384e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return 0;
7394e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout2:
7404e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unregister_chrdev_region(hsc_dev, HSC_DEVS);
7414e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosout1:
7424e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	kfree(cl_data);
7434e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7444e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return ret;
7454e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
7464e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7474e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int __devexit hsc_remove(struct device *dev)
7484e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
7494e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsi_client *cl = to_hsi_client(dev);
7504e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	struct hsc_client_data *cl_data = hsi_client_drvdata(cl);
7514e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	dev_t hsc_dev = cl_data->cdev.dev;
7524e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7534e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	cdev_del(&cl_data->cdev);
7544e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	unregister_chrdev_region(hsc_dev, HSC_DEVS);
7554e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsi_client_set_drvdata(cl, NULL);
7564e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	kfree(cl_data);
7574e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7584e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return 0;
7594e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
7604e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7614e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic struct hsi_client_driver hsc_driver = {
7624e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	.driver = {
7634e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		.name	= "hsi_char",
7644e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		.owner	= THIS_MODULE,
7654e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		.probe	= hsc_probe,
7664e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		.remove	= __devexit_p(hsc_remove),
7674e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	},
7684e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos};
7694e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7704e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic int __init hsc_init(void)
7714e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
7724e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	int ret;
7734e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7744e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if ((max_data_size < 4) || (max_data_size > 0x10000) ||
7754e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		(max_data_size & (max_data_size - 1))) {
7764e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		pr_err("Invalid max read/write data size");
7774e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return -EINVAL;
7784e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7794e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7804e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	ret = hsi_register_client_driver(&hsc_driver);
7814e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	if (ret) {
7824e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		pr_err("Error while registering HSI/SSI driver %d", ret);
7834e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos		return ret;
7844e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	}
7854e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7864e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	pr_info("HSI/SSI char device loaded\n");
7874e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7884e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	return 0;
7894e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
7904e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosmodule_init(hsc_init);
7914e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7924e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosstatic void __exit hsc_exit(void)
7934e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos{
7944e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	hsi_unregister_client_driver(&hsc_driver);
7954e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos	pr_info("HSI char device removed\n");
7964e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos}
7974e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokosmodule_exit(hsc_exit);
7984e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras Domokos
7994e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras DomokosMODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>");
8004e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras DomokosMODULE_ALIAS("hsi:hsi_char");
8014e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras DomokosMODULE_DESCRIPTION("HSI character device");
8024e69fc22753fcce1d9275b5517ef3646ffeffcf4Andras DomokosMODULE_LICENSE("GPL v2");
803