diff mbox

[RFC,7/9] HSI: character driver low level interface

Message ID 1256896808-20152-8-git-send-email-s-jan@ti.com (mailing list archive)
State Awaiting Upstream, archived
Delegated to: Tony Lindgren
Headers show

Commit Message

jan sebastien Oct. 30, 2009, 10 a.m. UTC
None
diff mbox

Patch

diff --git a/drivers/hsi/hsi-if.c b/drivers/hsi/hsi-if.c
new file mode 100644
index 0000000..2c9ba0c
--- /dev/null
+++ b/drivers/hsi/hsi-if.c
@@ -0,0 +1,642 @@ 
+/*
+ * hsi-if.c
+ *
+ * Part of the HSI character driver, implements the HSI interface.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.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.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <asm/mach-types.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/bitmap.h>
+
+#include <linux/hsi_driver_if.h>
+#include <linux/hsi_char.h>
+
+#include "hsi-char.h"
+#include "hsi-if.h"
+
+#define HSI_CHANNEL_STATE_UNAVAIL	(1 << 0)
+#define HSI_CHANNEL_STATE_READING	(1 << 1)
+#define HSI_CHANNEL_STATE_WRITING	(1 << 2)
+
+#define PORT1	0
+#define PORT2	1
+
+#define RXCONV(dst, src) \
+	do { \
+		(dst)->mode = (src)->mode; \
+		(dst)->flow = (src)->flow; \
+		(dst)->frame_size = (src)->frame_size; \
+		(dst)->channels = (src)->channels; \
+		(dst)->divisor = (src)->divisor; \
+	} while (0)
+
+#define TXCONV(dst, src) \
+	do { \
+		(dst)->mode = (src)->mode; \
+		(dst)->flow = (src)->flow; \
+		(dst)->frame_size = (src)->frame_size; \
+		(dst)->channels = (src)->channels; \
+		(dst)->divisor = (src)->divisor; \
+		(dst)->arb_mode = (src)->arb_mode; \
+	} while (0)
+
+struct if_hsi_channel {
+	struct hsi_device *dev;
+	unsigned int channel_id;
+	u32 *tx_data;
+	unsigned int tx_count;
+	u32 *rx_data;
+	unsigned int rx_count;
+	unsigned int opened;
+	unsigned int state;
+	spinlock_t lock; /* Serializes access to channel data */
+};
+
+struct if_hsi_iface {
+	struct if_hsi_channel channels[HSI_MAX_CHAR_DEVS];
+	int bootstrap;
+	unsigned long init_chan_map;
+	spinlock_t lock; /* Serializes access to HSI functional interface */
+};
+
+static void if_hsi_port_event(struct hsi_device *dev, unsigned int event,
+				void *arg);
+static int __devinit if_hsi_probe(struct hsi_device *dev);
+static int __devexit if_hsi_remove(struct hsi_device *dev);
+
+static struct hsi_device_driver if_hsi_char_driver = {
+	.ctrl_mask = ANY_HSI_CONTROLLER,
+	.probe = if_hsi_probe,
+	.remove = __devexit_p(if_hsi_remove),
+	.driver = {
+		.name = "hsi_char"
+	},
+};
+
+static struct if_hsi_iface hsi_iface;
+
+static int if_hsi_read_on(int ch, u32 *data, unsigned int count)
+{
+	struct if_hsi_channel *channel;
+	int ret;
+
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+
+	spin_lock(&channel->lock);
+	if (channel->state & HSI_CHANNEL_STATE_READING) {
+		pr_err("Read still pending on channel %d\n", ch);
+		spin_unlock(&channel->lock);
+		return -EBUSY;
+	}
+	channel->state |= HSI_CHANNEL_STATE_READING;
+	channel->rx_data = data;
+	channel->rx_count = count;
+	spin_unlock(&channel->lock);
+
+	ret = hsi_read(channel->dev, data, count/4);
+	dev_dbg(&channel->dev->device, "%s, ch = %d, ret = %d\n", __func__, ch,
+									ret);
+
+	return ret;
+}
+
+static void if_hsi_read_done(struct hsi_device *dev, unsigned int size)
+{
+	struct if_hsi_channel *channel;
+	struct hsi_event ev;
+
+	channel = &hsi_iface.channels[dev->n_ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, dev->n_ch);
+	spin_lock(&channel->lock);
+	channel->state &= ~HSI_CHANNEL_STATE_READING;
+	ev.event = HSI_EV_IN;
+	ev.data = channel->rx_data;
+	ev.count = size;
+	spin_unlock(&channel->lock);
+	if_notify(dev->n_ch, &ev);
+}
+
+int if_hsi_read(int ch, u32 *data, unsigned int count)
+{
+	int ret = 0;
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	ret = if_hsi_read_on(ch, data, count);
+	spin_unlock_bh(&hsi_iface.lock);
+	return ret;
+}
+
+int if_hsi_poll(int ch)
+{
+	struct if_hsi_channel *channel;
+	int ret = 0;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	ret = hsi_poll(channel->dev);
+	spin_unlock_bh(&hsi_iface.lock);
+	return ret;
+}
+
+static int if_hsi_write_on(int ch, u32 *address, unsigned int count)
+{
+	struct if_hsi_channel *channel;
+	int ret;
+
+	channel = &hsi_iface.channels[ch];
+
+	spin_lock(&channel->lock);
+	if (channel->state & HSI_CHANNEL_STATE_WRITING) {
+		pr_err("Write still pending on channel %d\n", ch);
+		spin_unlock(&channel->lock);
+		return -EBUSY;
+	}
+
+	channel->tx_data = address;
+	channel->tx_count = count;
+	channel->state |= HSI_CHANNEL_STATE_WRITING;
+	spin_unlock(&channel->lock);
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	ret = hsi_write(channel->dev, address, count/4);
+	return ret;
+}
+
+static void if_hsi_write_done(struct hsi_device *dev, unsigned int size)
+{
+	struct if_hsi_channel *channel;
+	struct hsi_event ev;
+
+	channel = &hsi_iface.channels[dev->n_ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, dev->n_ch);
+
+	spin_lock(&channel->lock);
+	channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+	ev.event = HSI_EV_OUT;
+	ev.data = channel->tx_data;
+	ev.count = size;
+	spin_unlock(&channel->lock);
+	if_notify(dev->n_ch, &ev);
+}
+
+int if_hsi_write(int ch, u32 *data, unsigned int count)
+{
+	int ret = 0;
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	ret = if_hsi_write_on(ch, data, count);
+	spin_unlock_bh(&hsi_iface.lock);
+	return ret;
+}
+
+void if_hsi_send_break(int ch)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, HSI_IOCTL_SEND_BREAK, NULL);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_flush_rx(int ch)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, HSI_IOCTL_FLUSH_RX, NULL);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_flush_ch(int ch)
+{
+	/* FIXME - Check the purpose of this function */
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	spin_lock(&channel->lock);
+	spin_unlock(&channel->lock);
+}
+
+void if_hsi_flush_tx(int ch)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, HSI_IOCTL_FLUSH_TX, NULL);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_get_wakeline(int ch, unsigned int *state)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, HSI_IOCTL_WAKE, state);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_set_wakeline(int ch, unsigned int state)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, state, NULL);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+int if_hsi_set_rx(int ch, struct hsi_rx_config *cfg)
+{
+	int ret;
+	struct if_hsi_channel *channel;
+	struct hsr_ctx ctx;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	RXCONV(&ctx, cfg);
+	spin_lock_bh(&hsi_iface.lock);
+	ret = hsi_ioctl(channel->dev, HSI_IOCTL_SET_RX, &ctx);
+	spin_unlock_bh(&hsi_iface.lock);
+	return ret;
+}
+
+void if_hsi_get_rx(int ch, struct hsi_rx_config *cfg)
+{
+	struct if_hsi_channel *channel;
+	struct hsr_ctx ctx;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, HSI_IOCTL_GET_RX, &ctx);
+	RXCONV(cfg, &ctx);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+int if_hsi_set_tx(int ch, struct hsi_tx_config *cfg)
+{
+	int ret;
+	struct if_hsi_channel *channel;
+	struct hst_ctx ctx;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	TXCONV(&ctx, cfg);
+	spin_lock_bh(&hsi_iface.lock);
+	ret = hsi_ioctl(channel->dev, HSI_IOCTL_SET_TX, &ctx);
+	spin_unlock_bh(&hsi_iface.lock);
+	return ret;
+}
+
+void if_hsi_get_tx(int ch, struct hsi_tx_config *cfg)
+{
+	struct if_hsi_channel *channel;
+	struct hst_ctx ctx;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	hsi_ioctl(channel->dev, HSI_IOCTL_GET_TX, &ctx);
+	TXCONV(cfg, &ctx);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_cancel_read(int ch)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock(&channel->lock);
+	if (channel->state & HSI_CHANNEL_STATE_READING)
+		hsi_read_cancel(channel->dev);
+	channel->state &= ~HSI_CHANNEL_STATE_READING;
+	spin_unlock(&channel->lock);
+}
+
+void if_hsi_cancel_write(int ch)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock(&channel->lock);
+	if (channel->state & HSI_CHANNEL_STATE_WRITING)
+		hsi_write_cancel(channel->dev);
+	channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+	spin_unlock(&channel->lock);
+}
+
+static int if_hsi_openchannel(struct if_hsi_channel *channel)
+{
+	int ret = 0;
+
+	spin_lock(&channel->lock);
+
+	if (channel->state == HSI_CHANNEL_STATE_UNAVAIL)
+		return -ENODEV;
+
+	if (channel->opened) {
+		ret = -EBUSY;
+		goto leave;
+	}
+
+	if (!channel->dev) {
+		pr_err("Channel %d is not ready??\n",
+				channel->channel_id);
+		ret = -ENODEV;
+		goto leave;
+	}
+
+	ret = hsi_open(channel->dev);
+	if (ret < 0) {
+		pr_err("Could not open channel %d\n",
+				channel->channel_id);
+		goto leave;
+	}
+
+	channel->opened = 1;
+
+leave:
+	spin_unlock(&channel->lock);
+	return ret;
+}
+
+
+static int if_hsi_closechannel(struct if_hsi_channel *channel)
+{
+	int ret = 0;
+
+	spin_lock(&channel->lock);
+
+	if (!channel->opened)
+		goto leave;
+
+	if (!channel->dev) {
+		pr_err("Channel %d is not ready??\n",
+				channel->channel_id);
+		ret = -ENODEV;
+		goto leave;
+	}
+
+	/* Stop any pending read/write */
+	if (channel->state & HSI_CHANNEL_STATE_READING) {
+		hsi_read_cancel(channel->dev);
+		channel->state &= ~HSI_CHANNEL_STATE_READING;
+	}
+	if (channel->state & HSI_CHANNEL_STATE_WRITING) {
+		hsi_write_cancel(channel->dev);
+		channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+	}
+
+	hsi_close(channel->dev);
+
+	channel->opened = 0;
+leave:
+	spin_unlock(&channel->lock);
+	return ret;
+}
+
+
+int if_hsi_start(int ch)
+{
+	struct if_hsi_channel *channel;
+	int ret = 0;
+
+	channel = &hsi_iface.channels[ch];
+	dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+	spin_lock_bh(&hsi_iface.lock);
+	channel->state = 0;
+	ret = if_hsi_openchannel(channel);
+	if (ret < 0) {
+		pr_err("Could not open channel %d\n", ch);
+		spin_unlock_bh(&hsi_iface.lock);
+		goto error;
+	}
+	spin_unlock_bh(&hsi_iface.lock);
+	if_hsi_poll(ch);
+
+error:
+	return ret;
+}
+
+void if_hsi_stop(int ch)
+{
+	struct if_hsi_channel *channel;
+	channel = &hsi_iface.channels[ch];
+	if_hsi_set_wakeline(ch, 1);
+	spin_lock_bh(&hsi_iface.lock);
+	if_hsi_closechannel(channel);
+	spin_unlock_bh(&hsi_iface.lock);
+}
+
+static int __devinit if_hsi_probe(struct hsi_device *dev)
+{
+	struct if_hsi_channel *channel;
+	unsigned long *address;
+	int ret = -ENXIO, port;
+
+	for (port = 0; port < HSI_MAX_PORTS; port++) {
+		if (if_hsi_char_driver.ch_mask[port])
+			break;
+	}
+
+	if (port == HSI_MAX_PORTS)
+		return -ENXIO;
+
+	if (dev->n_ch >= HSI_MAX_CHAR_DEVS) {
+		pr_err("HSI char driver cannot handle channel %d\n", dev->n_ch);
+		return -ENXIO;
+	}
+
+	address = &if_hsi_char_driver.ch_mask[port];
+
+	spin_lock_bh(&hsi_iface.lock);
+	if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+		hsi_set_read_cb(dev, if_hsi_read_done);
+		hsi_set_write_cb(dev, if_hsi_write_done);
+		hsi_set_port_event_cb(dev, if_hsi_port_event);
+		channel = &hsi_iface.channels[dev->n_ch];
+		channel->dev = dev;
+		channel->state = 0;
+		ret = 0;
+		hsi_iface.init_chan_map ^= (1 << dev->n_ch);
+	}
+	spin_unlock_bh(&hsi_iface.lock);
+
+	return ret;
+}
+
+static int __devexit if_hsi_remove(struct hsi_device *dev)
+{
+	struct if_hsi_channel *channel;
+	unsigned long *address;
+	int ret = -ENXIO, port;
+
+	for (port = 0; port < HSI_MAX_PORTS; port++) {
+		if (if_hsi_char_driver.ch_mask[port])
+			break;
+	}
+
+	if (port == HSI_MAX_PORTS)
+		return -ENXIO;
+
+	address = &if_hsi_char_driver.ch_mask[port];
+
+	spin_lock_bh(&hsi_iface.lock);
+	if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+		hsi_set_read_cb(dev, NULL);
+		hsi_set_write_cb(dev, NULL);
+		channel = &hsi_iface.channels[dev->n_ch];
+		channel->dev = NULL;
+		channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+		ret = 0;
+	}
+	spin_unlock_bh(&hsi_iface.lock);
+
+	return ret;
+}
+
+static void if_hsi_port_event(struct hsi_device *dev, unsigned int event,
+				void *arg)
+{
+	struct hsi_event ev;
+	int i;
+
+	ev.event = HSI_EV_EXCEP;
+	ev.data = (u32 *)0;
+	ev.count = 0;
+
+	switch (event) {
+	case HSI_EVENT_BREAK_DETECTED:
+		pr_debug("%s, HWBREAK detected\n", __func__);
+		ev.data = (u32 *)HSI_HWBREAK;
+		spin_lock_bh(&hsi_iface.lock);
+		for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+			if (hsi_iface.channels[i].opened)
+				if_notify(i, &ev);
+		}
+		spin_unlock_bh(&hsi_iface.lock);
+		break;
+	case HSI_EVENT_HSR_DATAAVAILABLE:
+		i = (int)arg;
+		pr_debug("%s, HSI_EVENT_HSR_DATAAVAILABLE channel = %d\n",
+								__func__, i);
+		ev.event = HSI_EV_AVAIL;
+		spin_lock_bh(&hsi_iface.lock);
+		if (hsi_iface.channels[i].opened)
+			if_notify(i, &ev);
+		spin_unlock_bh(&hsi_iface.lock);
+		break;
+	case HSI_EVENT_CAWAKE_UP:
+		pr_debug("%s, CAWAKE up\n", __func__);
+		break;
+	case HSI_EVENT_CAWAKE_DOWN:
+		pr_debug("%s, CAWAKE down\n", __func__);
+		break;
+	case HSI_EVENT_ERROR:
+		pr_debug("%s, HSI ERROR occured\n", __func__);
+		break;
+	default:
+		pr_warning("%s, Unknown event(%d)\n", __func__, event);
+		break;
+	}
+}
+
+int __init if_hsi_init(unsigned int port, unsigned int *channels_map)
+{
+	struct if_hsi_channel *channel;
+	int	i, ret = 0;
+
+	port -= 1;
+	if (port >= HSI_MAX_PORTS)
+		return -EINVAL;
+
+	hsi_iface.bootstrap = 1;
+	spin_lock_init(&hsi_iface.lock);
+
+	for (i = 0; i < HSI_MAX_PORTS; i++)
+		if_hsi_char_driver.ch_mask[i] = 0;
+
+	for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+		channel = &hsi_iface.channels[i];
+		channel->dev = NULL;
+		channel->opened = 0;
+		channel->state = 0;
+		channel->channel_id = i;
+		spin_lock_init(&channel->lock);
+		channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+	}
+
+	for (i = 0; (i < HSI_MAX_CHAR_DEVS) && channels_map[i]; i++) {
+		pr_debug("%s, port = %d, channels_map[i] = %d\n", __func__,
+							port, channels_map[i]);
+		if ((channels_map[i] - 1) < HSI_MAX_CHAR_DEVS)
+			if_hsi_char_driver.ch_mask[port] |=
+						(1 << ((channels_map[i] - 1)));
+		else {
+			pr_err("Channel %d cannot be handled by the HSI "
+						"driver.\n", channels_map[i]);
+			return -EINVAL;
+		}
+
+	}
+	hsi_iface.init_chan_map = if_hsi_char_driver.ch_mask[port];
+
+	ret = hsi_register_driver(&if_hsi_char_driver);
+	if (ret)
+		pr_err("Error while registering HSI driver %d", ret);
+
+	if (hsi_iface.init_chan_map) {
+		ret = -ENXIO;
+		pr_err("HSI: Some channels could not be registered (out of "
+					"range or already registered?)\n");
+	}
+	return ret;
+}
+
+int __exit if_hsi_exit(void)
+{
+	struct if_hsi_channel *channel;
+	unsigned long *address;
+	int i, port;
+
+	for (port = 0; port < HSI_MAX_PORTS; port++) {
+		if (if_hsi_char_driver.ch_mask[port])
+			break;
+	}
+
+	if (port == HSI_MAX_PORTS)
+		return -ENXIO;
+
+	address = &if_hsi_char_driver.ch_mask[port];
+
+	for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+		channel = &hsi_iface.channels[i];
+		if (channel->opened) {
+			if_hsi_set_wakeline(i, 1);
+			if_hsi_closechannel(channel);
+		}
+	}
+	hsi_unregister_driver(&if_hsi_char_driver);
+	return 0;
+}
diff --git a/drivers/hsi/hsi-if.h b/drivers/hsi/hsi-if.h
new file mode 100644
index 0000000..d64610d
--- /dev/null
+++ b/drivers/hsi/hsi-if.h
@@ -0,0 +1,65 @@ 
+/*
+ * hsi-if.h
+ *
+ * Part of the HSI character driver, private headers.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.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.
+ */
+
+#ifndef _HSI_IF_H
+#define _HSI_IF_H
+
+#define HSI_EV_MASK		(0xffff << 0)
+#define HSI_EV_TYPE_MASK	(0x0f << 16)
+#define HSI_EV_IN		(0x01 << 16)
+#define HSI_EV_OUT		(0x02 << 16)
+#define HSI_EV_EXCEP		(0x03 << 16)
+#define HSI_EV_AVAIL		(0x04 << 16)
+#define HSI_EV_TYPE(event)	((event) & HSI_EV_TYPE_MASK)
+
+#define HSI_HWBREAK		1
+#define HSI_ERROR		2
+
+struct hsi_event {
+    unsigned int event;
+    u32 *data;
+    unsigned int count;
+};
+
+int if_hsi_init(unsigned int port, unsigned int *channels_map);
+int if_hsi_exit(void);
+
+int if_hsi_start(int ch);
+void if_hsi_stop(int ch);
+
+void if_hsi_send_break(int ch);
+void if_hsi_flush_rx(int ch);
+void if_hsi_flush_tx(int ch);
+void if_hsi_bootstrap(int ch);
+void if_hsi_set_wakeline(int ch, unsigned int state);
+void if_hsi_get_wakeline(int ch, unsigned int *state);
+int if_hsi_set_rx(int ch, struct hsi_rx_config *cfg);
+void if_hsi_get_rx(int ch, struct hsi_rx_config *cfg);
+int if_hsi_set_tx(int ch, struct hsi_tx_config *cfg);
+void if_hsi_get_tx(int ch, struct hsi_tx_config *cfg);
+
+int if_hsi_read(int ch, u32 *data, unsigned int count);
+int if_hsi_poll(int ch);
+int if_hsi_write(int ch, u32 *data, unsigned int count);
+
+void if_hsi_cancel_read(int ch);
+void if_hsi_cancel_write(int ch);
+
+#endif /* _HSI_IF_H */