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