aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/omap_hsi/hsi_protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/omap_hsi/hsi_protocol.c')
-rw-r--r--drivers/omap_hsi/hsi_protocol.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/drivers/omap_hsi/hsi_protocol.c b/drivers/omap_hsi/hsi_protocol.c
new file mode 100644
index 0000000..e1451e7
--- /dev/null
+++ b/drivers/omap_hsi/hsi_protocol.c
@@ -0,0 +1,308 @@
+/*
+ * File - hsi_protocol.c
+ *
+ * Implements HSI protocol for Infineon Modem.
+ *
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * Author: Rupesh Gujare <rupesh.g@samsung.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if 0
+#define DEBUG 1
+#endif
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+
+#include "hsi-protocol-if.h"
+#include <linux/hsi_driver_if.h>
+
+#define DRIVER_VERSION "1.0"
+
+char test_data[10] = "abcdefghij";
+
+dev_t hsi_protocol_dev;
+
+struct protocol_queue {
+ struct list_head list;
+ u32 *data;
+ unsigned int count;
+};
+
+struct hsi_protocol {
+ unsigned int opened;
+ int poll_event;
+ struct list_head rx_queue;
+ struct list_head tx_queue;
+ spinlock_t lock; /* Serialize access to driver data and API */
+ struct fasync_struct *async_queue;
+ wait_queue_head_t rx_wait;
+ wait_queue_head_t tx_wait;
+ wait_queue_head_t poll_wait;
+};
+
+static struct hsi_protocol hsi_protocol_data[HSI_MAX_CHANNELS];
+
+void if_notify(int ch, struct hsi_event *ev)
+{
+ struct protocol_queue *entry;
+
+ pr_debug("%s, ev = {0x%x, 0x%p, %u}\n",
+ __func__, ev->event, ev->data, ev->count);
+
+ spin_lock(&hsi_protocol_data[ch].lock);
+
+/* Not Required */
+ /*if (!hsi_protocol_data[ch].opened) {
+ pr_debug("%s, device not opened\n!", __func__);
+ printk(KERN_INFO "%s, device not opened\n!", __func__);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ return;
+ }*/
+
+ switch (HSI_EV_TYPE(ev->event)) {
+ case HSI_EV_IN:
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("HSI-CHAR: entry allocation failed.\n");
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ list_add_tail(&entry->list, &hsi_protocol_data[ch].rx_queue);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_IN\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].rx_wait);
+ break;
+ case HSI_EV_OUT:
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("HSI-CHAR: entry allocation failed.\n");
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ hsi_protocol_data[ch].poll_event |= (POLLOUT | POLLWRNORM);
+ list_add_tail(&entry->list, &hsi_protocol_data[ch].tx_queue);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_OUT\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].tx_wait);
+ break;
+ case HSI_EV_EXCEP:
+ hsi_protocol_data[ch].poll_event |= POLLPRI;
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_EXCEP\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].poll_wait);
+ break;
+ case HSI_EV_AVAIL:
+ hsi_protocol_data[ch].poll_event |= (POLLIN | POLLRDNORM);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_AVAIL\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].poll_wait);
+ break;
+ default:
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ break;
+ }
+}
+
+int hsi_proto_read(int ch, u32 *buffer, int count)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len = 0;
+ struct protocol_queue *entry;
+ int ret, recv_data = 0;
+
+ /*if (count > MAX_HSI_IPC_BUFFER)
+ count = MAX_HSI_IPC_BUFFER;
+
+ data = kmalloc(count, GFP_ATOMIC);*/
+
+ ret = if_hsi_read(ch, buffer, count);
+ if (ret < 0) {
+ pr_err("Can not submit read. READ Error\n");
+ goto out2;
+ }
+
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ add_wait_queue(&hsi_protocol_data[ch].rx_wait, &wait);
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ for (;;) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ if (!list_empty(&hsi_protocol_data[ch].rx_queue)) {
+ entry = list_entry(hsi_protocol_data[ch].rx_queue.next,
+ struct protocol_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ pr_debug("%s, data = 0x%p, data_len = %d\n",
+ __func__, data, data_len);
+
+ if (data_len) {
+ pr_debug("%s, RX finished, ch-> %d, length = %d\n",
+ __func__, ch, count);
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ hsi_protocol_data[ch].poll_event &=
+ ~(POLLIN | POLLRDNORM);
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+ if_hsi_poll(ch);
+#if 0
+ memcpy(buffer, data, count);
+#endif
+ recv_data += data_len;
+#if 0
+ buffer += data_len;
+ if ((recv_data == count) || (recv_data >= MAX_HSI_IPC_BUFFER))
+#endif
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ recv_data = -EAGAIN;
+ if_hsi_cancel_read(ch);
+ /* goto out; */
+ break;
+ }
+
+ /*printk(KERN_DEBUG "%s, going to sleep...\n", __func__); */
+ schedule();
+ /*printk(KERN_DEBUG "%s, woke up\n", __func__); */
+ }
+
+/*out:*/
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_protocol_data[ch].rx_wait, &wait);
+
+out2:
+ /*To Do- Set bit if data to be received is
+ * greater than 512K Bytes and return to IPC call
+ */
+
+ return recv_data;
+}
+
+int hsi_proto_write(int ch, u32 *buffer, int length)
+{
+
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len = 0, ret = -1;
+ struct protocol_queue *entry;
+
+ ret = if_hsi_write(ch, buffer, length);
+ if (ret < 0) {
+ pr_err("HSI Write ERROR %s\n", __func__);
+ goto out2;
+ } else
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ hsi_protocol_data[ch].poll_event &= ~(POLLOUT | POLLWRNORM);
+ add_wait_queue(&hsi_protocol_data[ch].tx_wait, &wait);
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ for (;;) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ if (!list_empty(&hsi_protocol_data[ch].tx_queue)) {
+ entry = list_entry(hsi_protocol_data[ch].tx_queue.next,
+ struct protocol_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ if (data_len) {
+ pr_debug("%s, TX finished, data_len = %d, ch-> %d\n",
+ __func__, length, ch);
+ ret = data_len;
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
+ schedule();
+ }
+
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_protocol_data[ch].tx_wait, &wait);
+
+out2:
+ return ret;
+}
+EXPORT_SYMBOL(hsi_proto_write);
+
+static int __init hsi_protocol_init(void)
+{
+ int i, ret = 0;
+
+ pr_info("HSI Infineon Protocol driver version " DRIVER_VERSION "\n");
+
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ init_waitqueue_head(&hsi_protocol_data[i].rx_wait);
+ init_waitqueue_head(&hsi_protocol_data[i].tx_wait);
+ init_waitqueue_head(&hsi_protocol_data[i].poll_wait);
+ spin_lock_init(&hsi_protocol_data[i].lock);
+ hsi_protocol_data[i].opened = 0;
+ INIT_LIST_HEAD(&hsi_protocol_data[i].rx_queue);
+ INIT_LIST_HEAD(&hsi_protocol_data[i].tx_queue);
+ }
+
+ printk(KERN_INFO "hsi_protocol_init : hsi_mux_setting Done.\n");
+
+ ret = if_hsi_init();
+
+ return ret;
+}
+
+
+static void __exit hsi_protocol_exit(void)
+{
+ if_hsi_exit();
+}
+
+
+MODULE_AUTHOR("Rupesh Gujare <rupesh.g@samsung.com> / Samsung Electronics");
+MODULE_DESCRIPTION("HSI Protocol for Infineon Modem");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(hsi_protocol_init);
+module_exit(hsi_protocol_exit);