[RFC PATCH 1/9] HSI: Low Level Driver interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Driver kernel interface headers and implementation
Driver Makefile and configuration integration

Signed-off-by: Sebastien Jan <s-jan@xxxxxx>
Signed-off-by: Carlos Chinea <carlos.chinea@xxxxxxxxx>
---
 drivers/Makefile              |    1 +
 drivers/hsi/Kconfig           |   44 +++
 drivers/hsi/Makefile          |   14 +
 drivers/hsi/hsi_driver_if.c   |  657 +++++++++++++++++++++++++++++++++++++++++
 include/linux/hsi_driver_if.h |  180 +++++++++++
 5 files changed, 896 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hsi/Kconfig
 create mode 100644 drivers/hsi/Makefile
 create mode 100644 drivers/hsi/hsi_driver_if.c
 create mode 100644 include/linux/hsi_driver_if.h

diff --git a/drivers/Makefile b/drivers/Makefile
index 086857c..05b4190 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -111,3 +111,4 @@ obj-$(CONFIG_VLYNQ)		+= vlynq/
 obj-$(CONFIG_STAGING)		+= staging/
 obj-y				+= platform/
 obj-y				+= ieee802154/
+obj-$(CONFIG_OMAP_HSI)		+= hsi/
diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
new file mode 100644
index 0000000..e10b522
--- /dev/null
+++ b/drivers/hsi/Kconfig
@@ -0,0 +1,44 @@
+#
+# OMAP HSI driver configuration
+#
+menuconfig HSI
+	bool "HSI support"
+	default n
+	help
+	  The MIPI HSI is a High Speed Synchronous Serial Interface and is
+	  defined for communication between two Integrated Circuits (the
+	  typical scenario is an application IC and cellular modem IC
+	  communication). Data transaction model is peer-to-peer. MIPI HSI
+	  physical layer provides logical channeling and several modes of
+	  operation.
+
+if HSI
+
+config OMAP_HSI
+	tristate "OMAP HSI hardware driver"
+	depends on (ARCH_OMAP34XX || ARCH_OMAP4)
+	default n
+	---help---
+	  If you say Y here, you will enable the OMAP HSI hardware driver.
+
+	  If unsure, say N.
+
+	  Note that the HSI driver supports either:
+	    - the OMAP MIPI HSI device
+	    - the OMAP SSI device
+
+choice
+	prompt "Selected device support file"
+	depends on OMAP_HSI && y
+	default OMAP_HSI_DEVICE
+	---help---
+	  Adds the device support for one of the devices handled by the HSI
+	  driver.
+
+config OMAP_HSI_DEVICE
+	bool "HSI (OMAP MIPI HSI)"
+
+config OMAP_SSI_DEVICE
+	bool "SSI (OMAP SSI)"
+
+endchoice
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
new file mode 100644
index 0000000..a7f579b
--- /dev/null
+++ b/drivers/hsi/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for HSI drivers
+#
+EXTRA_CFLAGS :=
+
+omap_hsi-objs	:=	hsi_driver.o hsi_driver_dma.o hsi_driver_int.o \
+			hsi_driver_if.o hsi_driver_bus.o hsi_driver_gpio.o \
+			hsi_driver_fifo.o
+
+ifeq ($(CONFIG_DEBUG_FS), y)
+	omap_hsi-objs += hsi_driver_debugfs.o
+endif
+
+obj-$(CONFIG_OMAP_HSI)	+= omap_hsi.o
diff --git a/drivers/hsi/hsi_driver_if.c b/drivers/hsi/hsi_driver_if.c
new file mode 100644
index 0000000..fb0035d
--- /dev/null
+++ b/drivers/hsi/hsi_driver_if.c
@@ -0,0 +1,657 @@
+/*
+ * hsi_driver_if.c
+ *
+ * Implements HSI hardware driver interfaces for the upper layers.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@xxxxxxxxx>
+ * Author: Sebastien JAN <s-jan@xxxxxx>
+ *
+ * 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 "hsi_driver.h"
+
+#define NOT_SET		(-1)
+
+/* Manage HSR divisor update
+ * A special divisor value allows switching to auto-divisor mode in Rx
+ * (but with error counters deactivated). This function implements the
+ * the transitions to/from this mode.
+ */
+int hsi_set_rx_divisor(struct hsi_port *sport, u32 divisor)
+{
+	struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+	void __iomem *base = hsi_ctrl->base;
+	int port = sport->port_number;
+	struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+	if (divisor == NOT_SET)
+		return 0;
+
+	if (hsi_driver_device_is_hsi(pdev)) {
+		if (divisor == HSI_HSR_DIVISOR_AUTO && sport->counters_on) {
+			/* auto mode: deactivate counters + set divisor = 0 */
+			sport->reg_counters = hsi_inl(base,
+						HSI_HSR_COUNTERS_REG(port));
+			sport->counters_on = 0;
+			hsi_outl(0, base, HSI_HSR_COUNTERS_REG(port));
+			hsi_outl(0, base, HSI_HSR_DIVISOR_REG(port));
+			dev_dbg(hsi_ctrl->dev, "Switched to HSR auto mode\n");
+		} else if (divisor != HSI_HSR_DIVISOR_AUTO) {
+			/* Divisor set mode: use counters */
+			if (!sport->counters_on) {
+				/* Leave auto mode: restore counters */
+				hsi_outl(sport->reg_counters, base,
+						HSI_HSR_COUNTERS_REG(port));
+				sport->counters_on = 1;
+				dev_dbg(hsi_ctrl->dev, "Left HSR auto mode. "
+				"Counters=0x%lx\n", sport->reg_counters);
+			}
+			hsi_outl(divisor, base, HSI_HSR_DIVISOR_REG(port));
+		}
+	} else {
+		if (divisor == HSI_HSR_DIVISOR_AUTO && sport->counters_on) {
+			/* auto mode: deactivate timeout */
+			sport->reg_counters = hsi_inl(base,
+						HSI_HSR_COUNTERS_REG(port));
+			sport->counters_on = 0;
+			hsi_outl(0, base, HSI_HSR_COUNTERS_REG(port));
+			dev_dbg(hsi_ctrl->dev, "Deactivated SSR timeout\n");
+		} else if (divisor == HSI_SSR_DIVISOR_USE_TIMEOUT &&
+							!sport->counters_on){
+			/* Leave auto mode: restore timeout */
+			hsi_outl(sport->reg_counters, base,
+					HSI_HSR_COUNTERS_REG(port));
+			sport->counters_on = 1;
+			dev_dbg(hsi_ctrl->dev, "Re-activated SSR timeout; "
+					"timeout=0x%lx\n", sport->reg_counters);
+		}
+	}
+
+	return 0;
+}
+
+int hsi_set_rx(struct hsi_port *sport, struct hsr_ctx *cfg)
+{
+	struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+	void __iomem *base = hsi_ctrl->base;
+	int port = sport->port_number;
+	struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+	if (((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_STREAM) &&
+		((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_FRAME) &&
+		((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_SLEEP) &&
+		(cfg->mode != NOT_SET))
+		return -EINVAL;
+
+	if (hsi_driver_device_is_hsi(pdev)) {
+		if (
+		((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED) &&
+		((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_PIPELINED) &&
+			(cfg->flow != NOT_SET))
+			return -EINVAL;
+	} else {
+		if (
+		((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED) &&
+			(cfg->flow != NOT_SET))
+			return -EINVAL;
+	}
+
+	if ((cfg->frame_size > HSI_FRAMESIZE_MAX) &&
+		(cfg->frame_size != NOT_SET))
+		return -EINVAL;
+
+	if ((cfg->channels == 0) ||
+		((cfg->channels > sport->max_ch) &&
+			(cfg->channels != NOT_SET)))
+		return -EINVAL;
+
+	if (hsi_driver_device_is_hsi(pdev)) {
+		if ((cfg->divisor > HSI_MAX_RX_DIVISOR) &&
+			(cfg->divisor != HSI_HSR_DIVISOR_AUTO) &&
+			(cfg->divisor != NOT_SET))
+			return -EINVAL;
+	}
+
+	if ((cfg->mode != NOT_SET) &&
+		(cfg->flow != NOT_SET))
+		hsi_outl(cfg->mode | cfg->flow, base, HSI_HSR_MODE_REG(port));
+
+	if (cfg->frame_size != NOT_SET)
+		hsi_outl(cfg->frame_size, base, HSI_HSR_FRAMESIZE_REG(port));
+
+	if (cfg->channels != NOT_SET) {
+		if ((cfg->channels & (-cfg->channels)) ^ cfg->channels)
+			return -EINVAL;
+		else
+			hsi_outl(cfg->channels, base,
+						HSI_HSR_CHANNELS_REG(port));
+	}
+
+	return hsi_set_rx_divisor(sport, cfg->divisor);
+}
+
+void hsi_get_rx(struct hsi_port *sport, struct hsr_ctx *cfg)
+{
+	struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+	void __iomem *base = hsi_ctrl->base;
+	int port = sport->port_number;
+	struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+	cfg->mode = hsi_inl(base, HSI_HSR_MODE_REG(port)) & HSI_MODE_VAL_MASK;
+	cfg->flow = hsi_inl(base, HSI_HSR_MODE_REG(port)) & HSI_FLOW_VAL_MASK;
+	cfg->frame_size = hsi_inl(base, HSI_HSR_FRAMESIZE_REG(port));
+	cfg->channels = hsi_inl(base, HSI_HSR_CHANNELS_REG(port));
+	if (hsi_driver_device_is_hsi(pdev))
+		cfg->divisor = hsi_inl(base, HSI_HSR_DIVISOR_REG(port));
+}
+
+int hsi_set_tx(struct hsi_port *sport, struct hst_ctx *cfg)
+{
+	struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+	void __iomem *base = hsi_ctrl->base;
+	int port = sport->port_number;
+	struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+	unsigned int max_divisor = hsi_driver_device_is_hsi(pdev) ?
+				HSI_MAX_TX_DIVISOR : HSI_SSI_MAX_TX_DIVISOR;
+
+	if (((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_STREAM) &&
+		((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_FRAME) &&
+		(cfg->mode != NOT_SET))
+		return -EINVAL;
+
+	if (hsi_driver_device_is_hsi(pdev)) {
+		if (
+		((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED) &&
+		((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_PIPELINED) &&
+			(cfg->flow != NOT_SET))
+			return -EINVAL;
+	} else {
+		if (
+		((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED) &&
+			(cfg->flow != NOT_SET))
+			return -EINVAL;
+	}
+
+	if ((cfg->frame_size > HSI_FRAMESIZE_MAX) &&
+		(cfg->frame_size != NOT_SET))
+		return -EINVAL;
+
+	if ((cfg->channels == 0) ||
+		((cfg->channels > sport->max_ch) &&
+			(cfg->channels != NOT_SET)))
+		return -EINVAL;
+
+	if ((cfg->divisor > max_divisor) && (cfg->divisor != NOT_SET))
+		return -EINVAL;
+
+	if ((cfg->arb_mode != HSI_ARBMODE_ROUNDROBIN) &&
+		(cfg->arb_mode != HSI_ARBMODE_PRIORITY) &&
+		(cfg->mode != NOT_SET))
+		return -EINVAL;
+
+	if ((cfg->mode != NOT_SET) &&
+		(cfg->flow != NOT_SET))
+		hsi_outl(cfg->mode | cfg->flow | HSI_MODE_WAKE_CTRL_SW,
+				base, HSI_HST_MODE_REG(port));
+
+	if (cfg->frame_size != NOT_SET)
+		hsi_outl(cfg->frame_size, base, HSI_HST_FRAMESIZE_REG(port));
+
+	if (cfg->channels != NOT_SET) {
+		if ((cfg->channels & (-cfg->channels)) ^ cfg->channels)
+			return -EINVAL;
+		else
+			hsi_outl(cfg->channels, base,
+						HSI_HST_CHANNELS_REG(port));
+	}
+
+	if (cfg->divisor != NOT_SET)
+		hsi_outl(cfg->divisor, base, HSI_HST_DIVISOR_REG(port));
+
+	if (cfg->arb_mode != NOT_SET)
+		hsi_outl(cfg->arb_mode, base, HSI_HST_ARBMODE_REG(port));
+
+	return 0;
+}
+
+void hsi_get_tx(struct hsi_port *sport, struct hst_ctx *cfg)
+{
+    struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+    void __iomem *base = hsi_ctrl->base;
+    int port = sport->port_number;
+
+    cfg->mode = hsi_inl(base, HSI_HST_MODE_REG(port)) & HSI_MODE_VAL_MASK;
+    cfg->flow = hsi_inl(base, HSI_HST_MODE_REG(port)) & HSI_FLOW_VAL_MASK;
+    cfg->frame_size = hsi_inl(base, HSI_HST_FRAMESIZE_REG(port));
+    cfg->channels = hsi_inl(base, HSI_HST_CHANNELS_REG(port));
+    cfg->divisor = hsi_inl(base, HSI_HST_DIVISOR_REG(port));
+    cfg->arb_mode = hsi_inl(base, HSI_HST_ARBMODE_REG(port));
+}
+
+/**
+ * hsi_open - open a hsi device channel.
+ * @dev - Reference to the hsi device channel to be openned.
+ *
+ * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
+ */
+int hsi_open(struct hsi_device *dev)
+{
+	struct hsi_channel *ch;
+	struct hsi_port *port;
+	struct hsi_dev *hsi_ctrl;
+
+	if (!dev || !dev->ch) {
+		pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	if (!ch->read_done || !ch->write_done) {
+		dev_err(&dev->device, "Trying to open with no (read/write) "
+						"callbacks registered\n");
+		return -EINVAL;
+	}
+	port = ch->hsi_port;
+	hsi_ctrl = port->hsi_controller;
+	spin_lock_bh(&hsi_ctrl->lock);
+	if (ch->flags & HSI_CH_OPEN) {
+		dev_err(&dev->device, "Port %d Channel %d already OPENED\n",
+							dev->n_p, dev->n_ch);
+		spin_unlock_bh(&hsi_ctrl->lock);
+		return -EBUSY;
+	}
+	clk_enable(hsi_ctrl->hsi_clk);
+	ch->flags |= HSI_CH_OPEN;
+
+	hsi_outl_or(HSI_ERROROCCURED | HSI_BREAKDETECTED, hsi_ctrl->base,
+		HSI_SYS_MPU_ENABLE_REG(port->port_number, port->n_irq));
+	/* NOTE: error and break are port events and do not need to be
+	 * enabled for HSI extended enable register */
+
+	clk_disable(hsi_ctrl->hsi_clk);
+	spin_unlock_bh(&hsi_ctrl->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(hsi_open);
+
+/**
+ * hsi_write - write data into the hsi device channel
+ * @dev - reference to the hsi device channel to write into.
+ * @addr - pointer to a 32-bit word data to be written.
+ * @size - number of 32-bit word to be written.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ * A success value only indicates that the request has been accepted.
+ * Transfer is only completed when the write_done callback is called.
+ *
+ */
+int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
+{
+	struct hsi_channel *ch;
+	int err;
+
+	if (unlikely(!dev || !dev->ch || !addr || (size <= 0))) {
+		dev_err(&dev->device, "Wrong paramenters "
+			"hsi_device %p data %p count %d", dev, addr, size);
+		return -EINVAL;
+	}
+	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+		dev_err(&dev->device, "HSI device NOT open\n");
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
+	ch->write_data.addr = addr;
+	ch->write_data.size = size;
+
+	if (size == 1)
+		err = hsi_driver_write_interrupt(ch, addr);
+	else
+		err = hsi_driver_write_dma(ch, addr, size);
+
+	if (unlikely(err < 0)) {
+		ch->write_data.addr = NULL;
+		ch->write_data.size = 0;
+	}
+	spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
+
+	return err;
+
+}
+EXPORT_SYMBOL(hsi_write);
+
+/**
+ * hsi_read - read data from the hsi device channel
+ * @dev - hsi device channel reference to read data from.
+ * @addr - pointer to a 32-bit word data to store the data.
+ * @size - number of 32-bit word to be stored.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ * A success value only indicates that the request has been accepted.
+ * Data is only available in the buffer when the read_done callback is called.
+ *
+ */
+int hsi_read(struct hsi_device *dev, u32 *addr, unsigned int size)
+{
+	struct hsi_channel *ch;
+	int err;
+
+	if (unlikely(!dev || !dev->ch || !addr || (size <= 0))) {
+		dev_err(&dev->device, "Wrong paramenters "
+			"hsi_device %p data %p count %d", dev, addr, size);
+		return -EINVAL;
+	}
+	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+		dev_err(&dev->device, "HSI device NOT open\n");
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
+	ch->read_data.addr = addr;
+	ch->read_data.size = size;
+
+	if (size == 1)
+		err = hsi_driver_read_interrupt(ch, addr);
+	else
+		err = hsi_driver_read_dma(ch, addr, size);
+
+	if (unlikely(err < 0)) {
+		ch->read_data.addr = NULL;
+		ch->read_data.size = 0;
+	}
+	spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
+
+	return err;
+}
+EXPORT_SYMBOL(hsi_read);
+
+void __hsi_write_cancel(struct hsi_channel *ch)
+{
+	if (ch->write_data.size == 1)
+		hsi_driver_cancel_write_interrupt(ch);
+	else if (ch->write_data.size > 1)
+		hsi_driver_cancel_write_dma(ch);
+
+}
+/**
+ * hsi_write_cancel - Cancel pending write request.
+ * @dev - hsi device channel where to cancel the pending write.
+ *
+ * write_done() callback will not be called after sucess of this function.
+ */
+void hsi_write_cancel(struct hsi_device *dev)
+{
+	if (unlikely(!dev || !dev->ch)) {
+		pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
+		return;
+	}
+	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+		dev_err(&dev->device, "HSI device NOT open\n");
+		return;
+	}
+
+	spin_lock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+	__hsi_write_cancel(dev->ch);
+	spin_unlock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+}
+EXPORT_SYMBOL(hsi_write_cancel);
+
+void __hsi_read_cancel(struct hsi_channel *ch)
+{
+	if (ch->read_data.size == 1)
+		hsi_driver_cancel_read_interrupt(ch);
+	else if (ch->read_data.size > 1)
+		hsi_driver_cancel_read_dma(ch);
+}
+
+/**
+ * hsi_read_cancel - Cancel pending read request.
+ * @dev - hsi device channel where to cancel the pending read.
+ *
+ * read_done() callback will not be called after sucess of this function.
+ */
+void hsi_read_cancel(struct hsi_device *dev)
+{
+	if (unlikely(!dev || !dev->ch)) {
+		pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
+		return;
+	}
+
+	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+		dev_err(&dev->device, "HSI device NOT open\n");
+		return;
+	}
+
+	spin_lock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+	__hsi_read_cancel(dev->ch);
+	spin_unlock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+
+}
+EXPORT_SYMBOL(hsi_read_cancel);
+
+/**
+ * hsi_poll - HSI poll
+ * @dev - hsi device channel reference to apply the I/O control
+ * 						(or port associated to it)
+ *
+ * Return 0 on sucess, a negative value on failure.
+ *
+ */
+int hsi_poll(struct hsi_device *dev)
+{
+	struct hsi_channel *ch;
+	int err;
+
+	if (unlikely(!dev || !dev->ch))
+		return -EINVAL;
+
+	if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+		dev_err(&dev->device, "HSI device NOT open\n");
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
+	ch->flags |= HSI_CH_RX_POLL;
+	err = hsi_driver_read_interrupt(ch, NULL);
+	spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
+
+	return err;
+}
+EXPORT_SYMBOL(hsi_poll);
+
+
+/**
+ * hsi_ioctl - HSI I/O control
+ * @dev - hsi device channel reference to apply the I/O control
+ * 						(or port associated to it)
+ * @command - HSI I/O control command
+ * @arg - parameter associated to the control command. NULL, if no parameter.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ *
+ */
+int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
+{
+	struct hsi_channel *ch;
+	struct hsi_dev *hsi_ctrl;
+	void __iomem *base;
+	unsigned int port, channel;
+	u32 wake;
+	int err = 0;
+
+	if (unlikely((!dev) ||
+		(!dev->ch) ||
+		(!dev->ch->hsi_port) ||
+		(!dev->ch->hsi_port->hsi_controller)) ||
+		(!(dev->ch->flags & HSI_CH_OPEN))) {
+		pr_err(LOG_NAME "HSI IOCTL Invalid parameter\n");
+		return -EINVAL;
+	}
+
+	ch = dev->ch;
+	hsi_ctrl = ch->hsi_port->hsi_controller;
+	port = ch->hsi_port->port_number;
+	channel = ch->channel_number;
+	base = hsi_ctrl->base;
+	clk_enable(hsi_ctrl->hsi_clk);
+
+	switch (command) {
+	case HSI_IOCTL_WAKE_UP:
+		/* We only claim once the wake line per channel */
+		wake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
+		if (!(wake & HSI_WAKE(channel))) {
+			clk_enable(hsi_ctrl->hsi_clk);
+			hsi_outl(HSI_WAKE(channel), base,
+					HSI_SYS_SET_WAKE_REG(port));
+		}
+		break;
+	case HSI_IOCTL_WAKE_DOWN:
+		wake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
+		if ((wake & HSI_WAKE(channel))) {
+			hsi_outl(HSI_WAKE(channel), base,
+						HSI_SYS_CLEAR_WAKE_REG(port));
+			clk_disable(hsi_ctrl->hsi_clk);
+		}
+		break;
+	case HSI_IOCTL_SEND_BREAK:
+		hsi_outl(1, base, HSI_HST_BREAK_REG(port));
+		break;
+	case HSI_IOCTL_WAKE:
+		if (arg == NULL)
+			err = -EINVAL;
+		else
+			*(u32 *)arg = hsi_inl(base, HSI_SYS_WAKE_REG(port));
+		break;
+	case HSI_IOCTL_FLUSH_RX:
+		hsi_outl(0, base, HSI_HSR_RXSTATE_REG(port));
+		break;
+	case HSI_IOCTL_FLUSH_TX:
+		hsi_outl(0, base, HSI_HST_TXSTATE_REG(port));
+		break;
+	case HSI_IOCTL_CAWAKE:
+		if (!arg) {
+			err = -EINVAL;
+			goto out;
+		}
+		if (dev->ch->hsi_port->cawake_gpio < 0) {
+			err = -ENODEV;
+			goto out;
+		}
+		*(unsigned int *)arg = hsi_cawake(dev->ch->hsi_port);
+		break;
+	case HSI_IOCTL_SET_RX:
+		if (!arg) {
+			err = -EINVAL;
+			goto out;
+		}
+		err = hsi_set_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
+		break;
+	case HSI_IOCTL_GET_RX:
+		if (!arg) {
+			err = -EINVAL;
+			goto out;
+		}
+		hsi_get_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
+		break;
+	case HSI_IOCTL_SET_TX:
+		if (!arg) {
+			err = -EINVAL;
+			goto out;
+		}
+		err = hsi_set_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
+		break;
+	case HSI_IOCTL_GET_TX:
+		if (!arg) {
+			err = -EINVAL;
+			goto out;
+		}
+		hsi_get_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
+		break;
+	default:
+		err = -ENOIOCTLCMD;
+		break;
+	}
+out:
+	clk_disable(hsi_ctrl->hsi_clk);
+
+	return err;
+}
+EXPORT_SYMBOL(hsi_ioctl);
+
+/**
+ * hsi_close - close given hsi device channel
+ * @dev - reference to hsi device channel.
+ */
+void hsi_close(struct hsi_device *dev)
+{
+	if (!dev || !dev->ch) {
+		pr_err(LOG_NAME "Trying to close wrong HSI device %p\n", dev);
+		return;
+	}
+
+	spin_lock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+	if (dev->ch->flags & HSI_CH_OPEN) {
+		dev->ch->flags &= ~HSI_CH_OPEN;
+		__hsi_write_cancel(dev->ch);
+		__hsi_read_cancel(dev->ch);
+	}
+	spin_unlock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+}
+EXPORT_SYMBOL(hsi_close);
+
+/**
+ * hsi_set_read_cb - register read_done() callback.
+ * @dev - reference to hsi device channel where the callback is associated to.
+ * @read_cb - callback to signal read transfer completed.
+ *
+ * NOTE: Write callback must be only set when channel is not open !
+ */
+void hsi_set_read_cb(struct hsi_device *dev,
+		void (*read_cb)(struct hsi_device *dev, unsigned int size))
+{
+	dev->ch->read_done = read_cb;
+}
+EXPORT_SYMBOL(hsi_set_read_cb);
+
+/**
+ * hsi_set_read_cb - register write_done() callback.
+ * @dev - reference to hsi device channel where the callback is associated to.
+ * @write_cb - callback to signal read transfer completed.
+ *
+ * NOTE: Read callback must be only set when channel is not open !
+ */
+void hsi_set_write_cb(struct hsi_device *dev,
+		void (*write_cb)(struct hsi_device *dev, unsigned int size))
+{
+	dev->ch->write_done = write_cb;
+}
+EXPORT_SYMBOL(hsi_set_write_cb);
+
+/**
+ * hsi_set_port_event_cb - register port_event callback.
+ * @dev - reference to hsi device channel where the callback is associated to.
+ * @port_event_cb - callback to signal events from the channel port.
+ */
+void hsi_set_port_event_cb(struct hsi_device *dev,
+				void (*port_event_cb)(struct hsi_device *dev,
+						unsigned int event, void *arg))
+{
+	write_lock_bh(&dev->ch->rw_lock);
+	dev->ch->port_event = port_event_cb;
+	write_unlock_bh(&dev->ch->rw_lock);
+}
+EXPORT_SYMBOL(hsi_set_port_event_cb);
diff --git a/include/linux/hsi_driver_if.h b/include/linux/hsi_driver_if.h
new file mode 100644
index 0000000..9d9ae69
--- /dev/null
+++ b/include/linux/hsi_driver_if.h
@@ -0,0 +1,180 @@
+/*
+ * hsi_driver_if.h
+ *
+ * Header for the HSI driver low level interface.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@xxxxxxxxx>
+ * Author: Sebastien JAN <s-jan@xxxxxx>
+ *
+ * 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_DRIVER_IF_H__
+#define __HSI_DRIVER_IF_H__
+
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/notifier.h>
+
+/* The number of ports handled by the driver (MAX:2). Reducing this value
+ * optimizes the driver memory footprint.
+ */
+#define HSI_MAX_PORTS		1
+
+/* bit-field definition for allowed controller IDs and channels */
+#define ANY_HSI_CONTROLLER	-1
+
+/* HSR special divisor values set to control the auto-divisor Rx mode */
+#define HSI_HSR_DIVISOR_AUTO		0x1000 /* Activate auto Rx */
+#define HSI_SSR_DIVISOR_USE_TIMEOUT	0x1001 /* De-activate auto-Rx for SSI */
+
+enum {
+	HSI_EVENT_BREAK_DETECTED = 0,
+	HSI_EVENT_ERROR,
+	HSI_EVENT_PRE_SPEED_CHANGE,
+	HSI_EVENT_POST_SPEED_CHANGE,
+	HSI_EVENT_CAWAKE_UP,
+	HSI_EVENT_CAWAKE_DOWN,
+	HSI_EVENT_HSR_DATAAVAILABLE,
+};
+
+enum {
+	HSI_IOCTL_WAKE_UP,
+	HSI_IOCTL_WAKE_DOWN,
+	HSI_IOCTL_SEND_BREAK,
+	HSI_IOCTL_WAKE,
+	HSI_IOCTL_FLUSH_RX,
+	HSI_IOCTL_FLUSH_TX,
+	HSI_IOCTL_CAWAKE,
+	HSI_IOCTL_SET_RX,
+	HSI_IOCTL_GET_RX,
+	HSI_IOCTL_SET_TX,
+	HSI_IOCTL_GET_TX,
+};
+
+/* Forward references */
+struct hsi_device;
+struct hsi_channel;
+
+/* DPS */
+struct hst_ctx {
+	u32 mode;
+	u32 flow;
+	u32 frame_size;
+	u32 divisor;
+	u32 arb_mode;
+	u32 channels;
+};
+
+struct hsr_ctx {
+	u32 mode;
+	u32 flow;
+	u32 frame_size;
+	u32 divisor;
+	u32 timeout;
+	u32 channels;
+};
+
+struct port_ctx {
+	u32 sys_mpu_enable[2];
+	struct hst_ctx hst;
+	struct hsr_ctx hsr;
+};
+
+/**
+ * struct ctrl_ctx - hsi controller regs context
+ * @loss_count: hsi last loss count
+ * @sysconfig: keeps sysconfig reg state
+ * @gdd_gcr: keeps gcr reg state
+ * @pctx: array of port context
+ */
+struct ctrl_ctx {
+	int loss_count;
+	u32 sysconfig;
+	u32 gdd_gcr;
+	struct port_ctx *pctx;
+};
+/* END DPS */
+
+struct hsi_platform_data {
+	void (*set_min_bus_tput)(struct device *dev, u8 agent_id,
+							unsigned long r);
+	int (*clk_notifier_register)(struct clk *clk,
+						struct notifier_block *nb);
+	int (*clk_notifier_unregister)(struct clk *clk,
+						struct notifier_block *nb);
+	u8 num_ports;
+	struct ctrl_ctx ctx;
+};
+
+/**
+ * struct hsi_device - HSI device object
+ * @n_ctrl: associated HSI controller platform id number
+ * @n_p: port number
+ * @n_ch: channel number
+ * @modalias: [to be removed]
+ * @ch: channel descriptor
+ * @device: associated device
+*/
+struct hsi_device {
+	int n_ctrl;
+	unsigned int n_p;
+	unsigned int n_ch;
+	struct hsi_channel *ch;
+	struct device device;
+};
+
+#define to_hsi_device(dev)	container_of(dev, struct hsi_device, device)
+
+/**
+ * struct hsi_device_driver - HSI driver instance container
+ * @ctrl_mask: bit-field indicating the supported HSI device ids
+ * @ch_mask: bit-field indicating enabled channels for this port
+ * @probe: probe callback (driver registering)
+ * @remove: remove callback (driver un-registering)
+ * @suspend: suspend callback
+ * @resume: resume callback
+ * @driver: associated device_driver object
+*/
+struct hsi_device_driver {
+	unsigned long		ctrl_mask;
+	unsigned long		ch_mask[HSI_MAX_PORTS];
+	int			(*probe)(struct hsi_device *dev);
+	int			(*remove)(struct hsi_device *dev);
+	int			(*suspend)(struct hsi_device *dev,
+						pm_message_t mesg);
+	int			(*resume)(struct hsi_device *dev);
+	struct device_driver	driver;
+};
+
+#define to_hsi_device_driver(drv) container_of(drv, \
+						struct hsi_device_driver, \
+						driver)
+
+int hsi_register_driver(struct hsi_device_driver *driver);
+void hsi_unregister_driver(struct hsi_device_driver *driver);
+int hsi_open(struct hsi_device *dev);
+int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size);
+void hsi_write_cancel(struct hsi_device *dev);
+int hsi_read(struct hsi_device *dev, u32 *addr, unsigned int size);
+void hsi_read_cancel(struct hsi_device *dev);
+int hsi_poll(struct hsi_device *dev);
+int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg);
+void hsi_close(struct hsi_device *dev);
+void hsi_set_read_cb(struct hsi_device *dev,
+		void (*read_cb)(struct hsi_device *dev, unsigned int size));
+void hsi_set_write_cb(struct hsi_device *dev,
+		void (*write_cb)(struct hsi_device *dev, unsigned int size));
+void hsi_set_port_event_cb(struct hsi_device *dev,
+				void (*port_event_cb)(struct hsi_device *dev,
+					unsigned int event, void *arg));
+#endif /* __HSI_DRIVER_IF_H__ */
-- 
1.6.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux