* [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms
@ 2009-10-30 9:59 Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 1/9] HSI: Low Level Driver interface Sebastien Jan
` (9 more replies)
0 siblings, 10 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 9:59 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan
This is an RFC for a series of patch to add the support of MIPI HSI and SSI
devices to OMAP platforms.
The patch includes the HSI device driver and the device files for MIPI HSI and
SSI.
The driver is made of 2 distinct modules:
- omap_hsi: is the Low-Level Driver (or hardware driver). It provides a
kernel functional interface, to be used by other kernel modules and
abstracts the HW.
This part of the patch is based on Nokia SSI driver, already submitted as
RFC by Carlos Chinea [1].
- hsi-char: is a char device driver proposing a user-space interface for
using the HSI and relies on omap_hsi.
This part of the patch is based on a development from Andras Domokos.
The intent is to propose a single HSI driver that can support MIPI HSI 1.1 as
well as previous implementations (SSI) for OMAP platforms (OMAP3 / OMAP4).
The driver core functionalities are there and ready for review. The following
updates will come in the following weeks / months:
- Clocks and Power Management support for OMAP4 platform (HSI device file)
- HSI-char interface documentation
Validation: The driver is validated on 3430 SDP (with a HW loopback on the SSI
device), and is being validated on 4430 Virtio platform (MIPI HSI device).
[1] http://lwn.net/Articles/301918/
Thanks for your comments!
Sebastien JAN
Sebastien Jan (9):
HSI: Low Level Driver interface
HSI: Low Level Driver core
HSI: Low Level Driver device management
HSI: Low Level Driver debugfs support
HSI: Low Level Driver documentation
HSI: character driver interface
HSI: character driver low level interface
HSI: HSI device support
HSI: SSI device support and integration on 3430SDP platform
Documentation/hsi/hsi.txt | 319 ++++++++++++++++
arch/arm/mach-omap2/Makefile | 2 +
arch/arm/mach-omap2/board-3430sdp.c | 12 +
arch/arm/mach-omap2/hsi.c | 497 +++++++++++++++++++++++++
arch/arm/mach-omap2/mux.c | 17 +
arch/arm/mach-omap2/ssi.c | 530 ++++++++++++++++++++++++++
arch/arm/plat-omap/include/mach/hsi.h | 432 ++++++++++++++++++++++
arch/arm/plat-omap/include/mach/mux.h | 10 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/hsi/Kconfig | 58 +++
drivers/hsi/Makefile | 17 +
drivers/hsi/hsi-char.c | 526 ++++++++++++++++++++++++++
drivers/hsi/hsi-char.h | 31 ++
drivers/hsi/hsi-if.c | 642 ++++++++++++++++++++++++++++++++
drivers/hsi/hsi-if.h | 65 ++++
drivers/hsi/hsi_driver.c | 644 ++++++++++++++++++++++++++++++++
drivers/hsi/hsi_driver.h | 308 +++++++++++++++
drivers/hsi/hsi_driver_bus.c | 182 +++++++++
drivers/hsi/hsi_driver_debugfs.c | 486 ++++++++++++++++++++++++
drivers/hsi/hsi_driver_dma.c | 469 +++++++++++++++++++++++
drivers/hsi/hsi_driver_fifo.c | 289 +++++++++++++++
drivers/hsi/hsi_driver_gpio.c | 79 ++++
drivers/hsi/hsi_driver_if.c | 657 +++++++++++++++++++++++++++++++++
drivers/hsi/hsi_driver_int.c | 339 +++++++++++++++++
include/linux/hsi_char.h | 71 ++++
include/linux/hsi_driver_if.h | 180 +++++++++
27 files changed, 6865 insertions(+), 0 deletions(-)
create mode 100644 Documentation/hsi/hsi.txt
create mode 100644 arch/arm/mach-omap2/hsi.c
create mode 100644 arch/arm/mach-omap2/ssi.c
create mode 100644 arch/arm/plat-omap/include/mach/hsi.h
create mode 100644 drivers/hsi/Kconfig
create mode 100644 drivers/hsi/Makefile
create mode 100644 drivers/hsi/hsi-char.c
create mode 100644 drivers/hsi/hsi-char.h
create mode 100644 drivers/hsi/hsi-if.c
create mode 100644 drivers/hsi/hsi-if.h
create mode 100644 drivers/hsi/hsi_driver.c
create mode 100644 drivers/hsi/hsi_driver.h
create mode 100644 drivers/hsi/hsi_driver_bus.c
create mode 100644 drivers/hsi/hsi_driver_debugfs.c
create mode 100644 drivers/hsi/hsi_driver_dma.c
create mode 100644 drivers/hsi/hsi_driver_fifo.c
create mode 100644 drivers/hsi/hsi_driver_gpio.c
create mode 100644 drivers/hsi/hsi_driver_if.c
create mode 100644 drivers/hsi/hsi_driver_int.c
create mode 100644 include/linux/hsi_char.h
create mode 100644 include/linux/hsi_driver_if.h
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC PATCH 1/9] HSI: Low Level Driver interface
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 12:49 ` Felipe Balbi
2009-10-30 10:00 ` [RFC PATCH 2/9] HSI: Low Level Driver core Sebastien Jan
` (8 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
Driver kernel interface headers and implementation
Driver Makefile and configuration integration
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
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@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 "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@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_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
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 2/9] HSI: Low Level Driver core
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 1/9] HSI: Low Level Driver interface Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 12:56 ` Felipe Balbi
2009-10-30 10:00 ` [RFC PATCH 3/9] HSI: Low Level Driver device management Sebastien Jan
` (7 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
Driver core implementation (driver registration, ...)
Driver types definitions
HSI bus implementation
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
drivers/hsi/hsi_driver.c | 644 ++++++++++++++++++++++++++++++++++++++++++
drivers/hsi/hsi_driver.h | 308 ++++++++++++++++++++
drivers/hsi/hsi_driver_bus.c | 182 ++++++++++++
3 files changed, 1134 insertions(+), 0 deletions(-)
create mode 100644 drivers/hsi/hsi_driver.c
create mode 100644 drivers/hsi/hsi_driver.h
create mode 100644 drivers/hsi/hsi_driver_bus.c
diff --git a/drivers/hsi/hsi_driver.c b/drivers/hsi/hsi_driver.c
new file mode 100644
index 0000000..4abaf08
--- /dev/null
+++ b/drivers/hsi/hsi_driver.c
@@ -0,0 +1,644 @@
+/*
+ * hsi_driver.c
+ *
+ * Implements HSI module interface, initialization, and PM related functions.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+#include <mach/clock.h>
+#include "hsi_driver.h"
+
+#define HSI_DRIVER_VERSION "0.1"
+#define HSI_RESETDONE_TIMEOUT 10 /* 10 ms */
+#define HSI_RESETDONE_RETRIES 20 /* => max 200 ms waiting for reset */
+
+/* NOTE: Function called in interrupt context */
+int hsi_port_event_handler(struct hsi_port *p, unsigned int event, void *arg)
+{
+ struct hsi_channel *hsi_channel;
+ int ch;
+
+ if (event == HSI_EVENT_HSR_DATAAVAILABLE) {
+ /* The data-available event is channel-specific and must not be
+ * broadcasted
+ */
+ hsi_channel = p->hsi_channel + (int)arg;
+ read_lock(&hsi_channel->rw_lock);
+ if ((hsi_channel->dev) && (hsi_channel->port_event))
+ hsi_channel->port_event(hsi_channel->dev, event, arg);
+ read_unlock(&hsi_channel->rw_lock);
+ } else {
+ for (ch = 0; ch < p->max_ch; ch++) {
+ hsi_channel = p->hsi_channel + ch;
+ read_lock(&hsi_channel->rw_lock);
+ if ((hsi_channel->dev) && (hsi_channel->port_event))
+ hsi_channel->port_event(hsi_channel->dev,
+ event, arg);
+ read_unlock(&hsi_channel->rw_lock);
+ }
+ }
+ return 0;
+}
+
+static int hsi_clk_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+/* TODO - implement support for clocks changes
+ switch (event) {
+ case CLK_PRE_RATE_CHANGE:
+ break;
+ case CLK_ABORT_RATE_CHANGE:
+ break;
+ case CLK_POST_RATE_CHANGE:
+ break;
+ default:
+ break;
+ }
+*/
+
+ /*
+ * TODO: At this point we may emit a port event warning about the
+ * clk frequency change to the upper layers.
+ */
+ return NOTIFY_DONE;
+}
+
+static void hsi_dev_release(struct device *dev)
+{
+ /* struct device kfree is already made in unregister_hsi_devices().
+ * Registering this function is necessary to avoid an error from
+ * the device_release() function.
+ */
+}
+
+static int __init reg_hsi_dev_ch(struct hsi_dev *hsi_ctrl, unsigned int p,
+ unsigned int ch)
+{
+ struct hsi_device *dev;
+ struct hsi_port *port = &hsi_ctrl->hsi_port[p];
+ int err;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->n_ctrl = hsi_ctrl->id;
+ dev->n_p = p;
+ dev->n_ch = ch;
+ dev->ch = &port->hsi_channel[ch];
+ dev->device.bus = &hsi_bus_type;
+ dev->device.parent = hsi_ctrl->dev;
+ dev->device.release = hsi_dev_release;
+ if (dev->n_ctrl < 0)
+ dev_set_name(&dev->device, "omap_hsi-p%u.c%u", p, ch);
+ else
+ dev_set_name(&dev->device, "omap_hsi%d-p%u.c%u", dev->n_ctrl, p,
+ ch);
+
+ err = device_register(&dev->device);
+ if (err >= 0) {
+ write_lock_bh(&port->hsi_channel[ch].rw_lock);
+ port->hsi_channel[ch].dev = dev;
+ write_unlock_bh(&port->hsi_channel[ch].rw_lock);
+ } else {
+ kfree(dev);
+ }
+ return err;
+}
+
+static int __init register_hsi_devices(struct hsi_dev *hsi_ctrl)
+{
+ int port;
+ int ch;
+ int err;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++)
+ for (ch = 0; ch < hsi_ctrl->hsi_port[port].max_ch; ch++) {
+ err = reg_hsi_dev_ch(hsi_ctrl, port, ch);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int __init hsi_softreset(struct hsi_dev *hsi_ctrl)
+{
+ int ind = 0;
+ void __iomem *base = hsi_ctrl->base;
+ u32 status;
+
+ hsi_outl_or(HSI_SOFTRESET, base, HSI_SYS_SYSCONFIG_REG);
+
+ status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
+ while ((!(status & HSI_RESETDONE)) && (ind < HSI_RESETDONE_RETRIES)) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(HSI_RESETDONE_TIMEOUT));
+ status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
+ ind++;
+ }
+
+ if (ind >= HSI_RESETDONE_RETRIES)
+ return -EIO;
+
+ /* Reseting GDD */
+ hsi_outl_or(HSI_SWRESET, base, HSI_GDD_GRST_REG);
+
+ return 0;
+}
+
+static void __init set_hsi_ports_default(struct hsi_dev *hsi_ctrl,
+ struct platform_device *pd)
+{
+ struct port_ctx *cfg;
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ unsigned int port = 0;
+ void __iomem *base = hsi_ctrl->base;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ cfg = &pdata->ctx.pctx[port - 1];
+ hsi_outl(cfg->hst.mode | cfg->hst.flow | HSI_MODE_WAKE_CTRL_SW,
+ base, HSI_HST_MODE_REG(port));
+ if (!hsi_driver_device_is_hsi(pdev))
+ hsi_outl(cfg->hst.frame_size, base,
+ HSI_HST_FRAMESIZE_REG(port));
+ hsi_outl(cfg->hst.divisor, base, HSI_HST_DIVISOR_REG(port));
+ hsi_outl(cfg->hst.channels, base, HSI_HST_CHANNELS_REG(port));
+ hsi_outl(cfg->hst.arb_mode, base, HSI_HST_ARBMODE_REG(port));
+
+ hsi_outl(cfg->hsr.mode | cfg->hsr.flow, base,
+ HSI_HSR_MODE_REG(port));
+ hsi_outl(cfg->hsr.frame_size, base,
+ HSI_HSR_FRAMESIZE_REG(port));
+ hsi_outl(cfg->hsr.channels, base, HSI_HSR_CHANNELS_REG(port));
+ if (hsi_driver_device_is_hsi(pdev))
+ hsi_outl(cfg->hsr.divisor, base,
+ HSI_HSR_DIVISOR_REG(port));
+ hsi_outl(cfg->hsr.timeout, base, HSI_HSR_COUNTERS_REG(port));
+ }
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ /* SW strategy for HSI fifo management can be changed here */
+ hsi_fifo_mapping(hsi_ctrl, HSI_FIFO_MAPPING_DEFAULT);
+ }
+}
+
+static int __init hsi_port_channels_init(struct hsi_port *port)
+{
+ struct hsi_channel *ch;
+ unsigned int ch_i;
+
+ for (ch_i = 0; ch_i < port->max_ch; ch_i++) {
+ ch = &port->hsi_channel[ch_i];
+ ch->channel_number = ch_i;
+ rwlock_init(&ch->rw_lock);
+ ch->flags = 0;
+ ch->hsi_port = port;
+ ch->read_data.addr = NULL;
+ ch->read_data.size = 0;
+ ch->read_data.lch = -1;
+ ch->write_data.addr = NULL;
+ ch->write_data.size = 0;
+ ch->write_data.lch = -1;
+ ch->dev = NULL;
+ ch->read_done = NULL;
+ ch->write_done = NULL;
+ ch->port_event = NULL;
+ }
+
+ return 0;
+}
+
+static void hsi_ports_exit(struct hsi_dev *hsi_ctrl, unsigned int max_ports)
+{
+ struct hsi_port *hsi_p;
+ unsigned int port;
+
+ for (port = 0; port < max_ports; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ hsi_mpu_exit(hsi_p);
+ hsi_cawake_exit(hsi_p);
+ }
+}
+
+static int __init hsi_request_mpu_irq(struct hsi_port *hsi_p)
+{
+ struct hsi_dev *hsi_ctrl = hsi_p->hsi_controller;
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *mpu_irq;
+
+ if (hsi_driver_device_is_hsi(pd))
+ mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ,
+ hsi_p->port_number - 1);
+ else /* SSI support 2 IRQs per port */
+ mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ,
+ (hsi_p->port_number - 1) * 2);
+
+ if (!mpu_irq) {
+ dev_err(hsi_ctrl->dev, "HSI misses info for MPU IRQ on"
+ " port %d\n", hsi_p->port_number);
+ return -ENXIO;
+ }
+ hsi_p->n_irq = 0; /* We only use one irq line */
+ hsi_p->irq = mpu_irq->start;
+ return hsi_mpu_init(hsi_p, mpu_irq->name);
+}
+
+static int __init hsi_request_cawake_irq(struct hsi_port *hsi_p)
+{
+ struct hsi_dev *hsi_ctrl = hsi_p->hsi_controller;
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *cawake_irq;
+
+ if (hsi_driver_device_is_hsi(pd)) {
+ hsi_p->cawake_gpio = -1;
+ return 0;
+ } else {
+ cawake_irq = platform_get_resource(pd, IORESOURCE_IRQ,
+ 4 + hsi_p->port_number);
+ }
+
+ if (!cawake_irq) {
+ dev_err(hsi_ctrl->dev, "SSI device misses info for CAWAKE"
+ "IRQ on port %d\n", hsi_p->port_number);
+ return -ENXIO;
+ }
+
+ if (cawake_irq->flags & IORESOURCE_UNSET) {
+ dev_info(hsi_ctrl->dev, "No CAWAKE GPIO support\n");
+ hsi_p->cawake_gpio = -1;
+ return 0;
+ }
+
+ hsi_p->cawake_gpio_irq = cawake_irq->start;
+ hsi_p->cawake_gpio = irq_to_gpio(cawake_irq->start);
+ return hsi_cawake_init(hsi_p, cawake_irq->name);
+}
+
+static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct hsi_port *hsi_p;
+ unsigned int port;
+ int err;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ hsi_p->port_number = port + 1;
+ hsi_p->hsi_controller = hsi_ctrl;
+ hsi_p->max_ch = hsi_driver_device_is_hsi(pd) ?
+ HSI_CHANNELS_MAX : HSI_SSI_CHANNELS_MAX;
+ hsi_p->max_ch = min(hsi_p->max_ch, (u8) HSI_PORT_MAX_CH);
+ hsi_p->irq = 0;
+ hsi_p->counters_on = 1;
+ hsi_p->reg_counters = pdata->ctx.pctx[port].hsr.timeout;
+ spin_lock_init(&hsi_p->lock);
+ err = hsi_port_channels_init(&hsi_ctrl->hsi_port[port]);
+ if (err < 0)
+ goto rback1;
+ err = hsi_request_mpu_irq(hsi_p);
+ if (err < 0)
+ goto rback2;
+ err = hsi_request_cawake_irq(hsi_p);
+ if (err < 0)
+ goto rback3;
+ }
+ return 0;
+rback3:
+ hsi_mpu_exit(hsi_p);
+rback2:
+ hsi_ports_exit(hsi_ctrl, port + 1);
+rback1:
+ return err;
+}
+
+static int __init hsi_request_gdd_irq(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *gdd_irq;
+
+ if (hsi_driver_device_is_hsi(pd))
+ gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 2);
+ else
+ gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 4);
+
+ if (!gdd_irq) {
+ dev_err(hsi_ctrl->dev, "HSI has no GDD IRQ resource\n");
+ return -ENXIO;
+ }
+
+ hsi_ctrl->gdd_irq = gdd_irq->start;
+ return hsi_gdd_init(hsi_ctrl, gdd_irq->name);
+}
+
+static void __init hsi_init_gdd_chan_count(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *gdd_chan_count;
+ int i;
+
+ gdd_chan_count = platform_get_resource(pd, IORESOURCE_DMA, 0);
+
+ if (!gdd_chan_count) {
+ dev_warn(hsi_ctrl->dev, "HSI device has no GDD channel count "
+ "resource (use 8 as default)\n");
+ hsi_ctrl->gdd_chan_count = 8;
+ } else {
+ hsi_ctrl->gdd_chan_count = gdd_chan_count->start;
+ /* Check that the number of channels is power of 2 */
+ for (i = 0; i < 16; i++) {
+ if (hsi_ctrl->gdd_chan_count == (1 << i))
+ break;
+ }
+ if (i >= 16)
+ dev_err(hsi_ctrl->dev, "The Number of DMA channels "
+ "shall be a power of 2! (=%d)\n",
+ hsi_ctrl->gdd_chan_count);
+ }
+}
+
+static int __init hsi_controller_init(struct hsi_dev *hsi_ctrl,
+ struct platform_device *pd)
+{
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct resource *mem, *ioarea;
+ int err;
+
+ mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pd->dev, "HSI device does not have "
+ "HSI IO memory region information\n");
+ return -ENXIO;
+ }
+
+ ioarea = devm_request_mem_region(&pd->dev, mem->start,
+ (mem->end - mem->start) + 1,
+ dev_name(&pd->dev));
+ if (!ioarea) {
+ dev_err(&pd->dev, "Unable to request HSI IO mem region\n");
+ return -EBUSY;
+ }
+
+ hsi_ctrl->phy_base = mem->start;
+ hsi_ctrl->base = devm_ioremap(&pd->dev, mem->start,
+ (mem->end - mem->start) + 1);
+ if (!hsi_ctrl->base) {
+ dev_err(&pd->dev, "Unable to ioremap HSI base IO address\n");
+ return -ENXIO;
+ }
+
+ hsi_ctrl->id = pd->id;
+ if (pdata->num_ports > HSI_MAX_PORTS) {
+ dev_err(&pd->dev, "The HSI driver does not support enough "
+ "ports!\n");
+ return -ENXIO;
+ }
+ hsi_ctrl->max_p = pdata->num_ports;
+ hsi_ctrl->dev = &pd->dev;
+ spin_lock_init(&hsi_ctrl->lock);
+ hsi_ctrl->hsi_clk = clk_get(&pd->dev, "hsi_clk");
+ hsi_init_gdd_chan_count(hsi_ctrl);
+
+ if (IS_ERR(hsi_ctrl->hsi_clk)) {
+ dev_err(hsi_ctrl->dev, "Unable to get HSI clocks\n");
+ return PTR_ERR(hsi_ctrl->hsi_clk);
+ }
+
+ if (pdata->clk_notifier_register) {
+ hsi_ctrl->hsi_nb.notifier_call = hsi_clk_event;
+ hsi_ctrl->hsi_nb.priority = INT_MAX; /* Let's try to be first */
+ err = pdata->clk_notifier_register(hsi_ctrl->hsi_clk,
+ &hsi_ctrl->hsi_nb);
+ if (err < 0)
+ goto rback1;
+ }
+
+ err = hsi_ports_init(hsi_ctrl);
+ if (err < 0)
+ goto rback2;
+
+ err = hsi_request_gdd_irq(hsi_ctrl);
+ if (err < 0)
+ goto rback3;
+
+ return 0;
+rback3:
+ hsi_ports_exit(hsi_ctrl, hsi_ctrl->max_p);
+rback2:
+ if (pdata->clk_notifier_unregister)
+ pdata->clk_notifier_unregister(hsi_ctrl->hsi_clk,
+ &hsi_ctrl->hsi_nb);
+rback1:
+ clk_put(hsi_ctrl->hsi_clk);
+ dev_err(&pd->dev, "Error on hsi_controller initialization\n");
+ return err;
+}
+
+static void hsi_controller_exit(struct hsi_dev *hsi_ctrl)
+{
+ struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data;
+
+ hsi_gdd_exit(hsi_ctrl);
+ hsi_ports_exit(hsi_ctrl, hsi_ctrl->max_p);
+ if (pdata->clk_notifier_unregister)
+ pdata->clk_notifier_unregister(hsi_ctrl->hsi_clk,
+ &hsi_ctrl->hsi_nb);
+ clk_put(hsi_ctrl->hsi_clk);
+}
+
+static int __init hsi_probe(struct platform_device *pd)
+{
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct hsi_dev *hsi_ctrl;
+ u32 revision;
+ int err;
+
+ dev_dbg(&pd->dev, "The platform device probed is an %s\n",
+ hsi_driver_device_is_hsi(pd) ? "HSI" : "SSI");
+
+ if (!pdata) {
+ pr_err(LOG_NAME "No platform_data found on hsi device\n");
+ return -ENXIO;
+ }
+
+ hsi_ctrl = kzalloc(sizeof(*hsi_ctrl), GFP_KERNEL);
+ if (hsi_ctrl == NULL) {
+ dev_err(&pd->dev, "Could not allocate memory for"
+ " struct hsi_dev\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pd, hsi_ctrl);
+ err = hsi_controller_init(hsi_ctrl, pd);
+ if (err < 0) {
+ dev_err(&pd->dev, "Could not initialize hsi controller:"
+ " %d\n", err);
+ goto rollback1;
+ }
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ err = hsi_softreset(hsi_ctrl);
+ if (err < 0)
+ goto rollback2;
+
+ /* Set default PM settings */
+ hsi_outl((HSI_AUTOIDLE | HSI_SIDLEMODE_SMART | HSI_MIDLEMODE_SMART),
+ hsi_ctrl->base, HSI_SYS_SYSCONFIG_REG);
+ hsi_outl(HSI_CLK_AUTOGATING_ON, hsi_ctrl->base, HSI_GDD_GCR_REG);
+
+ /* Configure HSI ports */
+ set_hsi_ports_default(hsi_ctrl, pd);
+
+ /* Gather info from registers for the driver.(REVISION) */
+ revision = hsi_inl(hsi_ctrl->base, HSI_SYS_REVISION_REG);
+ if (hsi_driver_device_is_hsi(pd))
+ dev_info(hsi_ctrl->dev, "HSI Hardware REVISION 0x%x\n",
+ revision);
+ else
+ dev_info(hsi_ctrl->dev, "SSI Hardware REVISION %d.%d\n",
+ (revision & HSI_SSI_REV_MAJOR) >> 4,
+ (revision & HSI_SSI_REV_MINOR));
+
+
+ err = hsi_debug_add_ctrl(hsi_ctrl);
+ if (err < 0)
+ goto rollback2;
+
+ err = register_hsi_devices(hsi_ctrl);
+ if (err < 0)
+ goto rollback3;
+
+ clk_disable(hsi_ctrl->hsi_clk);
+
+ return err;
+
+rollback3:
+ hsi_debug_remove_ctrl(hsi_ctrl);
+rollback2:
+ clk_disable(hsi_ctrl->hsi_clk);
+ hsi_controller_exit(hsi_ctrl);
+rollback1:
+ kfree(hsi_ctrl);
+ return err;
+}
+
+static void __exit unregister_hsi_devices(struct hsi_dev *hsi_ctrl)
+{
+ struct hsi_port *hsi_p;
+ struct hsi_device *device;
+ unsigned int port;
+ unsigned int ch;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ for (ch = 0; ch < hsi_p->max_ch; ch++) {
+ device = hsi_p->hsi_channel[ch].dev;
+ hsi_close(device);
+ device_unregister(&device->device);
+ kfree(device);
+ }
+ }
+}
+
+static int __exit hsi_remove(struct platform_device *pd)
+{
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+
+ if (!hsi_ctrl)
+ return 0;
+
+ unregister_hsi_devices(hsi_ctrl);
+ hsi_debug_remove_ctrl(hsi_ctrl);
+ hsi_controller_exit(hsi_ctrl);
+ kfree(hsi_ctrl);
+
+ return 0;
+}
+
+int hsi_driver_device_is_hsi(struct platform_device *dev)
+{
+ struct platform_device_id *id = platform_get_device_id(dev);
+ return (id->driver_data == HSI_DRV_DEVICE_HSI);
+}
+
+/* List of devices supported by this driver */
+static struct platform_device_id hsi_id_table[] = {
+ { "omap_hsi", HSI_DRV_DEVICE_HSI },
+ { "omap_ssi", HSI_DRV_DEVICE_SSI },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, hsi_id_table);
+
+static struct platform_driver hsi_pdriver = {
+ .probe = hsi_probe,
+ .remove = __exit_p(hsi_remove),
+ .driver = {
+ .name = "omap_hsi",
+ .owner = THIS_MODULE,
+ },
+ .id_table = hsi_id_table
+};
+
+static int __init hsi_driver_init(void)
+{
+ int err = 0;
+
+ pr_info("HSI DRIVER Version " HSI_DRIVER_VERSION "\n");
+
+ hsi_bus_init();
+ err = hsi_debug_init();
+ if (err < 0) {
+ pr_err(LOG_NAME "HSI Debugfs failed %d\n", err);
+ goto rback1;
+ }
+ err = platform_driver_probe(&hsi_pdriver, hsi_probe);
+ if (err < 0) {
+ pr_err(LOG_NAME "Platform DRIVER register FAILED: %d\n", err);
+ goto rback2;
+ }
+
+ return 0;
+rback2:
+ hsi_debug_exit();
+rback1:
+ hsi_bus_exit();
+ return err;
+}
+
+static void __exit hsi_driver_exit(void)
+{
+ platform_driver_unregister(&hsi_pdriver);
+ hsi_debug_exit();
+ hsi_bus_exit();
+
+ pr_info("HSI DRIVER removed\n");
+}
+
+module_init(hsi_driver_init);
+module_exit(hsi_driver_exit);
+
+MODULE_ALIAS("platform:omap_hsi");
+MODULE_AUTHOR("Carlos Chinea / Nokia");
+MODULE_AUTHOR("Sebastien JAN / Texas Instruments");
+MODULE_DESCRIPTION("MIPI High-speed Synchronous Serial Interface (HSI) Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hsi/hsi_driver.h b/drivers/hsi/hsi_driver.h
new file mode 100644
index 0000000..711297a
--- /dev/null
+++ b/drivers/hsi/hsi_driver.h
@@ -0,0 +1,308 @@
+/*
+ * hsi_driver.h
+ *
+ * Header file 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@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_DRIVER_H__
+#define __HSI_DRIVER_H__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/notifier.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <mach/hsi.h>
+#include <linux/hsi_driver_if.h>
+
+/* Channel states */
+#define HSI_CH_OPEN 0x01
+#define HSI_CH_RX_POLL 0x10
+
+/*
+ * The number of channels handled by the driver in the ports, or the highest
+ * port channel number (+1) used. (MAX:8 for SSI; 16 for HSI)
+ * Reducing this value optimizes the driver memory footprint.
+ */
+#define HSI_PORT_MAX_CH 4
+
+#define LOG_NAME "OMAP HSI: "
+
+/* SW strategies for FIFO mapping */
+enum {
+ HSI_FIFO_MAPPING_UNDEF = 0,
+ HSI_FIFO_MAPPING_SSI, /* 8 FIFOs per port (SSI compatible mode) */
+ HSI_FIFO_MAPPING_ALL_PORT1, /* ALL FIFOs mapped on 1st port */
+};
+#define HSI_FIFO_MAPPING_DEFAULT HSI_FIFO_MAPPING_SSI
+
+/* Device identifying constants */
+enum {
+ HSI_DRV_DEVICE_HSI,
+ HSI_DRV_DEVICE_SSI
+};
+
+/**
+ * struct hsi_data - HSI buffer descriptor
+ * @addr: pointer to the buffer where to send or receive data
+ * @size: size in words (32 bits) of the buffer
+ * @lch: associated GDD (DMA) logical channel number, if any
+ */
+struct hsi_data {
+ u32 *addr;
+ unsigned int size;
+ int lch;
+};
+
+/**
+ * struct hsi_channel - HSI channel data
+ * @read_data: Incoming HSI buffer descriptor
+ * @write_data: Outgoing HSI buffer descriptor
+ * @hsi_port: Reference to port where the channel belongs to
+ * @flags: Tracks if channel has been open
+ * @channel_number: HSI channel number
+ * @rw_lock: Read/Write lock to serialize access to callback and hsi_device
+ * @dev: Reference to the associated hsi_device channel
+ * @write_done: Callback to signal TX completed.
+ * @read_done: Callback to signal RX completed.
+ * @port_event: Callback to signal port events (RX Error, HWBREAK, CAWAKE ...)
+ */
+struct hsi_channel {
+ struct hsi_data read_data;
+ struct hsi_data write_data;
+ struct hsi_port *hsi_port;
+ u8 flags;
+ u8 channel_number;
+ rwlock_t rw_lock;
+ struct hsi_device *dev;
+ void (*write_done)(struct hsi_device *dev, unsigned int size);
+ void (*read_done)(struct hsi_device *dev, unsigned int size);
+ void (*port_event)(struct hsi_device *dev, unsigned int event,
+ void *arg);
+};
+
+/**
+ * struct hsi_port - hsi port driver data
+ * @hsi_channel: Array of channels in the port
+ * @hsi_controller: Reference to the HSI controller
+ * @port_number: port number
+ * @max_ch: maximum number of channels supported on the port
+ * @n_irq: HSI irq line use to handle interrupts (0 or 1)
+ * @irq: IRQ number
+ * @cawake_gpio: GPIO number for cawake line (-1 if none)
+ * @cawake_gpio_irq: IRQ number for cawake gpio events
+ * @counters_on: indicates if the HSR counters are in use or not
+ * @reg_counters: stores the previous counters values when deactivated
+ * @lock: Serialize access to the port registers and internal data
+ * @hsi_tasklet: Bottom half for interrupts
+ * @cawake_tasklet: Bottom half for cawake events
+ */
+struct hsi_port {
+ struct hsi_channel hsi_channel[HSI_PORT_MAX_CH];
+ struct hsi_dev *hsi_controller;
+ u8 flags;
+ u8 port_number;
+ u8 max_ch;
+ u8 n_irq;
+ int irq;
+ int cawake_gpio;
+ int cawake_gpio_irq;
+ int counters_on;
+ unsigned long reg_counters;
+ spinlock_t lock; /* access to the port registers and internal data */
+ struct tasklet_struct hsi_tasklet;
+ struct tasklet_struct cawake_tasklet;
+};
+
+/**
+ * struct hsi_dev - hsi controller driver data
+ * @hsi_port: Array of hsi ports enabled in the controller
+ * @id: HSI controller platform id number
+ * @max_p: Number of ports enabled in the controller
+ * @hsi_clk: Reference to the HSI custom clock
+ * @base: HSI registers base virtual address
+ * @phy_base: HSI registers base physical address
+ * @lock: Serializes access to internal data and regs
+ * @cawake_clk_enable: Tracks if a cawake event has enable the clocks
+ * @gdd_irq: GDD (DMA) irq number
+ * @fifo_mapping_strategy: Selected strategy for fifo to ports/channels mapping
+ * @gdd_usecount: Holds the number of ongoning DMA transfers
+ * @last_gdd_lch: Last used GDD logical channel
+ * @gdd_chan-count: Number of available DMA channels on the device (must be ^2)
+ * @set_min_bus_tput: (PM) callback to set minimun bus throuput
+ * @clk_notifier_register: (PM) callabck for DVFS support
+ * @clk_notifier_unregister: (PM) callabck for DVFS support
+ * @hsi_nb: (PM) Notification block for DVFS notification chain
+ * @hsi_gdd_tasklet: Bottom half for DMA transfers
+ * @dir: debugfs base directory
+ * @dev: Reference to the HSI platform device
+ */
+struct hsi_dev {
+ struct hsi_port hsi_port[HSI_MAX_PORTS];
+ int id;
+ u8 max_p;
+ struct clk *hsi_clk;
+ void __iomem *base;
+ unsigned long phy_base;
+ spinlock_t lock; /* Serializes access to internal data and regs */
+ unsigned int cawake_clk_enable:1;
+ int gdd_irq;
+ unsigned int fifo_mapping_strategy;
+ unsigned int gdd_usecount;
+ unsigned int last_gdd_lch;
+ unsigned int gdd_chan_count;
+ 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);
+ struct notifier_block hsi_nb;
+ struct tasklet_struct hsi_gdd_tasklet;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+#endif
+ struct device *dev;
+};
+
+/* HSI Bus */
+extern struct bus_type hsi_bus_type;
+
+int hsi_port_event_handler(struct hsi_port *p, unsigned int event, void *arg);
+int hsi_bus_init(void);
+void hsi_bus_exit(void);
+/* End HSI Bus */
+
+void hsi_reset_ch_read(struct hsi_channel *ch);
+void hsi_reset_ch_write(struct hsi_channel *ch);
+
+int hsi_driver_read_interrupt(struct hsi_channel *hsi_channel, u32 *data);
+int hsi_driver_write_interrupt(struct hsi_channel *hsi_channel, u32 *data);
+int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 *data,
+ unsigned int count);
+int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 *data,
+ unsigned int count);
+
+void hsi_driver_cancel_write_interrupt(struct hsi_channel *ch);
+void hsi_driver_disable_read_interrupt(struct hsi_channel *ch);
+void hsi_driver_cancel_read_interrupt(struct hsi_channel *ch);
+void hsi_driver_cancel_write_dma(struct hsi_channel *ch);
+void hsi_driver_cancel_read_dma(struct hsi_channel *ch);
+
+int hsi_driver_device_is_hsi(struct platform_device *dev);
+
+int hsi_mpu_init(struct hsi_port *hsi_p, const char *irq_name);
+void hsi_mpu_exit(struct hsi_port *hsi_p);
+
+int hsi_gdd_init(struct hsi_dev *hsi_ctrl, const char *irq_name);
+void hsi_gdd_exit(struct hsi_dev *hsi_ctrl);
+
+int hsi_cawake_init(struct hsi_port *port, const char *irq_name);
+void hsi_cawake_exit(struct hsi_port *port);
+
+int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel,
+ unsigned int port);
+int hsi_fifo_get_chan(struct hsi_dev *hsi_ctrl, unsigned int fifo,
+ unsigned int *channel, unsigned int *port);
+int __init hsi_fifo_mapping(struct hsi_dev *hsi_ctrl, unsigned int mtype);
+long hsi_hst_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+long hsi_hsr_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+long hsi_hst_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+long hsi_hsr_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+
+#ifdef CONFIG_DEBUG_FS
+int hsi_debug_init(void);
+void hsi_debug_exit(void);
+int hsi_debug_add_ctrl(struct hsi_dev *hsi_ctrl);
+void hsi_debug_remove_ctrl(struct hsi_dev *hsi_ctrl);
+#else
+#define hsi_debug_add_ctrl(hsi_ctrl) 0
+#define hsi_debug_remove_ctrl(hsi_ctrl)
+#define hsi_debug_init() 0
+#define hsi_debug_exit()
+#endif /* CONFIG_DEBUG_FS */
+
+static inline unsigned int hsi_cawake(struct hsi_port *port)
+{
+ return gpio_get_value(port->cawake_gpio);
+}
+
+static inline struct hsi_channel *ctrl_get_ch(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel)
+{
+ return &hsi_ctrl->hsi_port[port - 1].hsi_channel[channel];
+}
+
+/* HSI IO access */
+static inline u32 hsi_inl(void __iomem *base, u32 offset)
+{
+ return inl((unsigned int) base + offset);
+}
+
+static inline void hsi_outl(u32 data, void __iomem *base, u32 offset)
+{
+ outl(data, (unsigned int) base + offset);
+}
+
+static inline void hsi_outl_or(u32 data, void __iomem *base, u32 offset)
+{
+ u32 tmp = hsi_inl(base, offset);
+ hsi_outl((tmp | data), base, offset);
+}
+
+static inline void hsi_outl_and(u32 data, void __iomem *base, u32 offset)
+{
+ u32 tmp = hsi_inl(base, offset);
+ hsi_outl((tmp & data), base, offset);
+}
+
+static inline u16 hsi_inw(void __iomem *base, u32 offset)
+{
+ return inw((unsigned int) base + offset);
+}
+
+static inline void hsi_outw(u16 data, void __iomem *base, u32 offset)
+{
+ outw(data, (unsigned int) base + offset);
+}
+
+static inline void hsi_outw_or(u16 data, void __iomem *base, u32 offset)
+{
+ u16 tmp = hsi_inw(base, offset);
+ hsi_outw((tmp | data), base, offset);
+}
+
+static inline void hsi_outw_and(u16 data, void __iomem *base, u32 offset)
+{
+ u16 tmp = hsi_inw(base, offset);
+ hsi_outw((tmp & data), base, offset);
+}
+
+#endif /* __HSI_DRIVER_H__ */
diff --git a/drivers/hsi/hsi_driver_bus.c b/drivers/hsi/hsi_driver_bus.c
new file mode 100644
index 0000000..3fb9d6e
--- /dev/null
+++ b/drivers/hsi/hsi_driver_bus.c
@@ -0,0 +1,182 @@
+/*
+ * hsi_driver_bus.c
+ *
+ * Implements an HSI bus, device and driver interface.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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/device.h>
+#include "hsi_driver.h"
+
+#define HSI_PREFIX "hsi:"
+
+struct bus_type hsi_bus_type;
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE + 1, "%s%s\n", HSI_PREFIX,
+ dev_name(dev));
+}
+
+static struct device_attribute hsi_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ add_uevent_var(env, "MODALIAS=%s%s", HSI_PREFIX, dev_name(dev));
+ return 0;
+}
+
+static int hsi_bus_match(struct device *device, struct device_driver *driver)
+{
+ struct hsi_device *dev = to_hsi_device(device);
+ struct hsi_device_driver *drv = to_hsi_device_driver(driver);
+
+ if (!test_bit(dev->n_ctrl, &drv->ctrl_mask))
+ return 0;
+
+ if (!test_bit(dev->n_ch, &drv->ch_mask[dev->n_p]))
+ return 0;
+
+ return 1;
+}
+
+int hsi_bus_unreg_dev(struct device *device, void *p)
+{
+ device->release(device);
+ device_unregister(device);
+
+ return 0;
+}
+
+int __init hsi_bus_init(void)
+{
+ return bus_register(&hsi_bus_type);
+}
+
+void hsi_bus_exit(void)
+{
+ bus_for_each_dev(&hsi_bus_type, NULL, NULL, hsi_bus_unreg_dev);
+ bus_unregister(&hsi_bus_type);
+}
+
+static int hsi_driver_probe(struct device *dev)
+{
+ struct hsi_device_driver *drv = to_hsi_device_driver(dev->driver);
+
+ if (!drv->probe)
+ return -ENODEV;
+
+ return drv->probe(to_hsi_device(dev));
+}
+
+static int hsi_driver_remove(struct device *dev)
+{
+ struct hsi_device_driver *drv;
+ int ret;
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+ if (drv->remove) {
+ ret = drv->remove(to_hsi_device(dev));
+ } else {
+ dev->driver = NULL;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int hsi_driver_suspend(struct device *dev, pm_message_t mesg)
+{
+ struct hsi_device_driver *drv = to_hsi_device_driver(dev->driver);
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+ if (!drv->suspend)
+ return 0;
+
+ return drv->suspend(to_hsi_device(dev), mesg);
+}
+
+static int hsi_driver_resume(struct device *dev)
+{
+ struct hsi_device_driver *drv;
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+ if (!drv->resume)
+ return 0;
+
+ return drv->resume(to_hsi_device(dev));
+}
+
+struct bus_type hsi_bus_type = {
+ .name = "hsi",
+ .dev_attrs = hsi_dev_attrs,
+ .match = hsi_bus_match,
+ .uevent = hsi_bus_uevent,
+ .probe = hsi_driver_probe,
+ .remove = hsi_driver_remove,
+ .suspend = hsi_driver_suspend,
+ .resume = hsi_driver_resume,
+};
+
+/**
+ * hsi_register_driver - Register HSI device driver
+ * @driver - reference to the HSI device driver.
+ */
+int hsi_register_driver(struct hsi_device_driver *driver)
+{
+ int ret = 0;
+
+ if (driver == NULL)
+ return -EINVAL;
+
+ driver->driver.bus = &hsi_bus_type;
+
+ ret = driver_register(&driver->driver);
+
+ if (ret == 0)
+ pr_debug("hsi: driver %s registered\n", driver->driver.name);
+
+ return ret;
+}
+EXPORT_SYMBOL(hsi_register_driver);
+
+/**
+ * hsi_unregister_driver - Unregister HSI device driver
+ * @driver - reference to the HSI device driver.
+ */
+void hsi_unregister_driver(struct hsi_device_driver *driver)
+{
+ if (driver == NULL)
+ return;
+
+ driver_unregister(&driver->driver);
+
+ pr_debug("hsi: driver %s unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL(hsi_unregister_driver);
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 3/9] HSI: Low Level Driver device management
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 1/9] HSI: Low Level Driver interface Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 2/9] HSI: Low Level Driver core Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 4/9] HSI: Low Level Driver debugfs support Sebastien Jan
` (6 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
Low-level managment of interrupts, DMA, GPIO (SSI) and FIFO (HSI).
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
drivers/hsi/hsi_driver_dma.c | 469 +++++++++++++++++++++++++++++++++++++++++
drivers/hsi/hsi_driver_fifo.c | 289 +++++++++++++++++++++++++
drivers/hsi/hsi_driver_gpio.c | 79 +++++++
drivers/hsi/hsi_driver_int.c | 339 +++++++++++++++++++++++++++++
4 files changed, 1176 insertions(+), 0 deletions(-)
create mode 100644 drivers/hsi/hsi_driver_dma.c
create mode 100644 drivers/hsi/hsi_driver_fifo.c
create mode 100644 drivers/hsi/hsi_driver_gpio.c
create mode 100644 drivers/hsi/hsi_driver_int.c
diff --git a/drivers/hsi/hsi_driver_dma.c b/drivers/hsi/hsi_driver_dma.c
new file mode 100644
index 0000000..4514e98
--- /dev/null
+++ b/drivers/hsi/hsi_driver_dma.c
@@ -0,0 +1,469 @@
+/*
+ * hsi_driver_dma.c
+ *
+ * Implements HSI low level interface driver functionality with DMA support.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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/dma-mapping.h>
+#include "hsi_driver.h"
+
+#define HSI_SYNC_WRITE 0
+#define HSI_SYNC_READ 1
+#define HSI_L3_TPUT 13428 /* 13428 KiB/s => ~110 Mbit/s*/
+
+static unsigned char hsi_sync_table[2][2][8] = {
+ {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
+ {0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}
+ }, {
+ {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
+ {0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
+ }
+};
+
+/**
+ * hsi_get_free_lch - Get a free GDD(DMA)logical channel
+ * @hsi_ctrl- HSI controller of the GDD.
+ *
+ * Needs to be called holding the hsi_controller lock
+ *
+ * Return a free logical channel number. If there is no free lch
+ * then returns an out of range value
+ */
+static unsigned int hsi_get_free_lch(struct hsi_dev *hsi_ctrl)
+{
+ unsigned int enable_reg;
+ unsigned int i;
+ unsigned int lch = hsi_ctrl->last_gdd_lch;
+
+ enable_reg = hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ for (i = 1; i <= hsi_ctrl->gdd_chan_count; i++) {
+ lch = (lch + i) & (hsi_ctrl->gdd_chan_count - 1);
+ if (!(enable_reg & HSI_GDD_LCH(lch))) {
+ hsi_ctrl->last_gdd_lch = lch;
+ return lch;
+ }
+ }
+
+ return lch;
+}
+
+/**
+ * hsi_driver_write_dma - Program GDD [DMA] to write data from memory to
+ * the hsi channel buffer.
+ * @hsi_channel - pointer to the hsi_channel to write data to.
+ * @data - 32-bit word pointer to the data.
+ * @size - Number of 32bit words to be transfered.
+ *
+ * hsi_controller lock must be held before calling this function.
+ *
+ * Return 0 on success and < 0 on error.
+ */
+int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 *data,
+ unsigned int size)
+{
+ struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_channel->hsi_port->port_number;
+ unsigned int channel = hsi_channel->channel_number;
+ unsigned int sync;
+ long buff_offset;
+ int lch;
+ dma_addr_t dma_data;
+ dma_addr_t s_addr;
+ u16 tmp;
+
+ if ((size < 1) || (data == NULL))
+ return -EINVAL;
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ lch = hsi_get_free_lch(hsi_ctrl);
+ if (lch >= hsi_ctrl->gdd_chan_count) {
+ dev_err(hsi_ctrl->dev, "No free GDD logical channels.\n");
+ clk_disable(hsi_ctrl->hsi_clk);
+ return -EBUSY; /* No free GDD logical channels. */
+ }
+
+ /* NOTE: Getting a free gdd logical channel and
+ * reserve it must be done atomicaly. */
+ hsi_channel->write_data.lch = lch;
+
+ /* Sync is required for SSI but not for HSI */
+ sync = hsi_sync_table[HSI_SYNC_WRITE][port - 1][channel];
+
+ dma_data = dma_map_single(hsi_ctrl->dev, data, size * 4,
+ DMA_TO_DEVICE);
+
+ tmp = HSI_SRC_SINGLE_ACCESS0 |
+ HSI_SRC_MEMORY_PORT |
+ HSI_DST_SINGLE_ACCESS0 |
+ HSI_DST_PERIPHERAL_PORT |
+ HSI_DATA_TYPE_S32;
+ hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));
+
+ tmp = HSI_SRC_AMODE_POSTINC | HSI_DST_AMODE_CONST | sync;
+ hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch));
+
+ hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CICR_REG(lch));
+
+ buff_offset = hsi_hst_buffer_reg(hsi_ctrl, port, channel);
+ if (buff_offset < 0)
+ return buff_offset;
+ s_addr = hsi_ctrl->phy_base + buff_offset;
+
+ hsi_outl(s_addr, base, HSI_GDD_CDSA_REG(lch));
+
+ hsi_outl(dma_data, base, HSI_GDD_CSSA_REG(lch));
+ hsi_outw(size, base, HSI_GDD_CEN_REG(lch));
+
+ hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch));
+
+ return 0;
+}
+
+/**
+ * hsi_driver_read_dma - Program GDD [DMA] to write data to memory from
+ * the hsi channel buffer.
+ * @hsi_channel - pointer to the hsi_channel to read data from.
+ * @data - 32-bit word pointer where to store the incoming data.
+ * @size - Number of 32bit words to be transfered to the buffer.
+ *
+ * hsi_controller lock must be held before calling this function.
+ *
+ * Return 0 on success and < 0 on error.
+ */
+int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 *data,
+ unsigned int count)
+{
+ struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_channel->hsi_port->port_number;
+ unsigned int channel = hsi_channel->channel_number;
+ unsigned int sync;
+ unsigned int lch;
+ long buff_offset;
+ dma_addr_t dma_data;
+ dma_addr_t d_addr;
+ u16 tmp;
+
+ clk_enable(hsi_ctrl->hsi_clk);
+ lch = hsi_get_free_lch(hsi_ctrl);
+ if (lch >= hsi_ctrl->gdd_chan_count) {
+ dev_err(hsi_ctrl->dev, "No free GDD logical channels.\n");
+ clk_disable(hsi_ctrl->hsi_clk);
+ return -EBUSY; /* No free GDD logical channels. */
+ }
+
+ /* When DMA is used for Rx, disable the Rx Interrupt.
+ * (else DATAAVAILLABLE event would get triggered on first
+ * received data word)
+ * (By default, Rx interrupt is active for polling feature)
+ */
+ hsi_driver_disable_read_interrupt(hsi_channel);
+
+ /*
+ * NOTE: Gettting a free gdd logical channel and
+ * reserve it must be done atomicaly.
+ */
+ hsi_channel->read_data.lch = lch;
+
+ /* Sync is required for SSI but not for HSI */
+ sync = hsi_sync_table[HSI_SYNC_READ][port - 1][channel];
+
+ dma_data = dma_map_single(hsi_ctrl->dev, data, count * 4,
+ DMA_FROM_DEVICE);
+
+ tmp = HSI_DST_SINGLE_ACCESS0 |
+ HSI_DST_MEMORY_PORT |
+ HSI_SRC_SINGLE_ACCESS0 |
+ HSI_SRC_PERIPHERAL_PORT |
+ HSI_DATA_TYPE_S32;
+ hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));
+
+ tmp = HSI_DST_AMODE_POSTINC | HSI_SRC_AMODE_CONST | sync;
+ hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch));
+
+ hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CICR_REG(lch));
+
+ buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, port, channel);
+ if (buff_offset < 0)
+ return buff_offset;
+ d_addr = hsi_ctrl->phy_base + buff_offset;
+
+ hsi_outl(d_addr, base, HSI_GDD_CSSA_REG(lch));
+
+ hsi_outl(dma_data, base, HSI_GDD_CDSA_REG(lch));
+ hsi_outw(count, base, HSI_GDD_CEN_REG(lch));
+
+ hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch));
+
+ return 0;
+}
+
+void hsi_driver_cancel_write_dma(struct hsi_channel *hsi_ch)
+{
+ int lch = hsi_ch->write_data.lch;
+ unsigned int port = hsi_ch->hsi_port->port_number;
+ unsigned int channel = hsi_ch->channel_number;
+ struct hsi_dev *hsi_ctrl = hsi_ch->hsi_port->hsi_controller;
+ u32 ccr;
+ long buff_offset;
+
+ if (lch < 0)
+ return;
+
+ clk_enable(hsi_ctrl->hsi_clk);
+ ccr = hsi_inw(hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+ if (!(ccr & HSI_CCR_ENABLE)) {
+ dev_dbg(&hsi_ch->dev->device, LOG_NAME "Write cancel on not "
+ "enabled logical channel %d CCR REG 0x%08X\n", lch, ccr);
+ clk_disable(hsi_ctrl->hsi_clk);
+ return;
+ }
+
+ hsi_outw_and(~HSI_CCR_ENABLE, hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+ hsi_outl_and(~HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outl(HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+ buff_offset = hsi_hst_bufstate_f_reg(hsi_ctrl, port, channel);
+ if (buff_offset >= 0)
+ hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), hsi_ctrl->base,
+ buff_offset);
+
+ hsi_reset_ch_write(hsi_ch);
+ clk_disable(hsi_ctrl->hsi_clk);
+ clk_disable(hsi_ctrl->hsi_clk); /* FIXME - check if can be removed */
+}
+
+void hsi_driver_cancel_read_dma(struct hsi_channel *hsi_ch)
+{
+ int lch = hsi_ch->read_data.lch;
+ struct hsi_dev *hsi_ctrl = hsi_ch->hsi_port->hsi_controller;
+ unsigned int port = hsi_ch->hsi_port->port_number;
+ unsigned int channel = hsi_ch->channel_number;
+ u32 reg;
+ long buff_offset;
+
+ if (lch < 0)
+ return;
+
+ /* DMA transfer is over, re-enable default mode
+ * (Interrupts for polling feature)
+ */
+ hsi_driver_read_interrupt(hsi_ch, NULL);
+
+ clk_enable(hsi_ctrl->hsi_clk);
+ reg = hsi_inw(hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+ if (!(reg & HSI_CCR_ENABLE)) {
+ dev_dbg(&hsi_ch->dev->device, LOG_NAME "Read cancel on not "
+ "enable logical channel %d CCR REG 0x%08X\n", lch, reg);
+ clk_disable(hsi_ctrl->hsi_clk);
+ return;
+ }
+
+ hsi_outw_and(~HSI_CCR_ENABLE, hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+ hsi_outl_and(~HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outl(HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+ buff_offset = hsi_hsr_bufstate_f_reg(hsi_ctrl, port, channel);
+ if (buff_offset >= 0)
+ hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), hsi_ctrl->base,
+ buff_offset);
+
+ hsi_reset_ch_read(hsi_ch);
+ clk_disable(hsi_ctrl->hsi_clk);
+ clk_disable(hsi_ctrl->hsi_clk); /* FIXME - check if can be removed */
+}
+
+/**
+ * hsi_get_info_from_gdd_lch - Retrieve channels information from DMA channel
+ * @hsi_ctrl - HSI device control structure
+ * @lch - DMA logical channel
+ * @port - HSI port
+ * @channel - HSI channel
+ * @is_read_path - channel is used for reading
+ *
+ * Updates the port, channel and is_read_path parameters depending on the
+ * lch DMA channel status.
+ *
+ * Return 0 on success and < 0 on error.
+ */
+int hsi_get_info_from_gdd_lch(struct hsi_dev *hsi_ctrl, unsigned int lch,
+ unsigned int *port, unsigned int *channel, unsigned int *is_read_path)
+{
+ int i_ports;
+ int i_chans;
+ int err = -1;
+
+ for (i_ports = 0; i_ports < HSI_MAX_PORTS; i_ports++)
+ for (i_chans = 0; i_chans < HSI_PORT_MAX_CH; i_chans++)
+ if (hsi_ctrl->hsi_port[i_ports].
+ hsi_channel[i_chans].read_data.lch == lch) {
+ *is_read_path = 1;
+ *port = i_ports + 1;
+ *channel = i_chans;
+ err = 0;
+ goto get_info_bk;
+ } else if (hsi_ctrl->hsi_port[i_ports].
+ hsi_channel[i_chans].write_data.lch == lch) {
+ *is_read_path = 0;
+ *port = i_ports + 1;
+ *channel = i_chans;
+ err = 0;
+ goto get_info_bk;
+ }
+get_info_bk:
+ return err;
+}
+
+static void do_hsi_gdd_lch(struct hsi_dev *hsi_ctrl, unsigned int gdd_lch)
+{
+ void __iomem *base = hsi_ctrl->base;
+ struct hsi_channel *ch;
+ unsigned int port;
+ unsigned int channel;
+ unsigned int is_read_path;
+ u32 gdd_csr;
+ dma_addr_t dma_h;
+ size_t size;
+
+ if (hsi_get_info_from_gdd_lch(hsi_ctrl, gdd_lch, &port, &channel,
+ &is_read_path) < 0) {
+ dev_err(hsi_ctrl->dev, "Unable to match the DMA channel %d with"
+ " an HSI channel\n", gdd_lch);
+ return;
+ }
+/* FIXME: to remove when validated: */
+ else {
+ dev_dbg(hsi_ctrl->dev, "DMA event on gdd_lch=%d => port=%d, "
+ "channel=%d, read=%d\n", gdd_lch, port, channel,
+ is_read_path);
+ }
+
+ spin_lock(&hsi_ctrl->lock);
+
+ hsi_outl_and(~HSI_GDD_LCH(gdd_lch), base,
+ HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ gdd_csr = hsi_inw(base, HSI_GDD_CSR_REG(gdd_lch));
+
+ if (!(gdd_csr & HSI_CSR_TOUT)) {
+ if (is_read_path) { /* Read path */
+ dma_h = hsi_inl(base, HSI_GDD_CDSA_REG(gdd_lch));
+ size = hsi_inw(base, HSI_GDD_CEN_REG(gdd_lch)) * 4;
+ dma_sync_single_for_cpu(hsi_ctrl->dev, dma_h, size,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(hsi_ctrl->dev, dma_h, size,
+ DMA_FROM_DEVICE);
+ ch = ctrl_get_ch(hsi_ctrl, port, channel);
+ hsi_reset_ch_read(ch);
+ /* DMA transfer is over, re-enable default mode
+ * (interrupts for polling feature)
+ */
+ hsi_driver_read_interrupt(ch, NULL);
+ spin_unlock(&hsi_ctrl->lock);
+ ch->read_done(ch->dev, size);
+ } else {
+ dma_h = hsi_inl(base, HSI_GDD_CSSA_REG(gdd_lch));
+ size = hsi_inw(base, HSI_GDD_CEN_REG(gdd_lch)) * 4;
+ dma_unmap_single(hsi_ctrl->dev, dma_h, size,
+ DMA_TO_DEVICE);
+ ch = ctrl_get_ch(hsi_ctrl, port, channel);
+ hsi_reset_ch_write(ch);
+ spin_unlock(&hsi_ctrl->lock);
+ ch->write_done(ch->dev, size);
+ }
+ } else {
+ dev_err(hsi_ctrl->dev, "Error on GDD transfer "
+ "on gdd channel %d\n", gdd_lch);
+ spin_unlock(&hsi_ctrl->lock);
+ hsi_port_event_handler(&hsi_ctrl->hsi_port[port - 1],
+ HSI_EVENT_ERROR, NULL);
+ }
+
+ /* Decrease clk usecount which was increased in
+ * hsi_driver_{read,write}_dma() */
+ clk_disable(hsi_ctrl->hsi_clk);
+}
+
+static void do_hsi_gdd_tasklet(unsigned long device)
+{
+ struct hsi_dev *hsi_ctrl = (struct hsi_dev *)device;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int gdd_lch = 0;
+ u32 status_reg = 0;
+ u32 lch_served = 0;
+ unsigned int gdd_max_count = hsi_ctrl->gdd_chan_count;
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ status_reg = hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+ for (gdd_lch = 0; gdd_lch < gdd_max_count; gdd_lch++) {
+ if (status_reg & HSI_GDD_LCH(gdd_lch)) {
+ do_hsi_gdd_lch(hsi_ctrl, gdd_lch);
+ lch_served |= HSI_GDD_LCH(gdd_lch);
+ }
+ }
+
+ hsi_outl(lch_served, base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+ status_reg = hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+ status_reg &= hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ clk_disable(hsi_ctrl->hsi_clk);
+
+ if (status_reg)
+ tasklet_hi_schedule(&hsi_ctrl->hsi_gdd_tasklet);
+ else
+ enable_irq(hsi_ctrl->gdd_irq);
+}
+
+static irqreturn_t hsi_gdd_mpu_handler(int irq, void *hsi_controller)
+{
+ struct hsi_dev *hsi_ctrl = hsi_controller;
+
+ tasklet_hi_schedule(&hsi_ctrl->hsi_gdd_tasklet);
+ disable_irq_nosync(hsi_ctrl->gdd_irq);
+
+ return IRQ_HANDLED;
+}
+
+int __init hsi_gdd_init(struct hsi_dev *hsi_ctrl, const char *irq_name)
+{
+ tasklet_init(&hsi_ctrl->hsi_gdd_tasklet, do_hsi_gdd_tasklet,
+ (unsigned long)hsi_ctrl);
+ if (request_irq(hsi_ctrl->gdd_irq, hsi_gdd_mpu_handler, IRQF_DISABLED,
+ irq_name, hsi_ctrl) < 0) {
+ dev_err(hsi_ctrl->dev, "FAILED to request GDD IRQ %d\n",
+ hsi_ctrl->gdd_irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void hsi_gdd_exit(struct hsi_dev *hsi_ctrl)
+{
+ tasklet_disable(&hsi_ctrl->hsi_gdd_tasklet);
+ free_irq(hsi_ctrl->gdd_irq, hsi_ctrl);
+}
diff --git a/drivers/hsi/hsi_driver_fifo.c b/drivers/hsi/hsi_driver_fifo.c
new file mode 100644
index 0000000..33b0f7c
--- /dev/null
+++ b/drivers/hsi/hsi_driver_fifo.c
@@ -0,0 +1,289 @@
+/*
+ * hsi_driver_fifo.c
+ *
+ * Implements HSI module fifo management.
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+#include <mach/clock.h>
+#include "hsi_driver.h"
+
+/**
+ * hsi_fifo_get_id - Get fifo index corresponding to (port, channel)
+ * @hsi_ctrl - HSI controler data
+ * @channel - channel used
+ * @port - HSI port used
+ *
+ * Returns the fifo index associated to the provided (port, channel).
+ * Notes: 1) The fifo <=> (port, channel) correspondance depends on the selected
+ * SW strategy for channels mapping (fifo management).
+ * 2) the mapping is identical for Read and Write path.
+ * This exclusively applies to HSI devices.
+ */
+int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel,
+ unsigned int port)
+{
+ int fifo_index = 0;
+ int err = 0;
+
+ if (unlikely(channel >= HSI_CHANNELS_MAX || port < 1 || port > 2)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ }
+
+ if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT1) {
+ if (unlikely(port != 1)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ } else {
+ fifo_index = channel;
+ }
+ } else if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_SSI) {
+ if (unlikely(channel >= 8)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ } else {
+ fifo_index = channel + 8 * (port - 1);
+ }
+ } else {
+ err = -EPERM;
+ goto fifo_id_bk;
+ }
+
+fifo_id_bk:
+ if (unlikely(err < 0)) {
+ dev_err(hsi_ctrl->dev, "Cannot map a fifo to the requested "
+ "params: channel:%d, port:%d; ERR=%d\n", channel, port,
+ fifo_index);
+ fifo_index = err;
+ }
+
+ return fifo_index;
+}
+
+/**
+ * hsi_fifo_get_chan - Get (port, channel) from a fifo index
+ * @hsi_ctrl - HSI controler data
+ * @fifo - HSI fifo used (0..HSI_HST_FIFO_COUNT)
+ * @channel - related channel if any (0..)
+ * @port - related port if any (1..2)
+ *
+ * Returns 0 in case of success, and errocode (< 0) else
+ * Notes: 1) The fifo <=> (port, channel) correspondance depends on the selected
+ * SW strategy for channels mapping (fifo management).
+ * 2) the mapping is identical for Read and Write path.
+ * This exclusively applies to HSI devices.
+ */
+int hsi_fifo_get_chan(struct hsi_dev *hsi_ctrl, unsigned int fifo,
+ unsigned int *channel, unsigned int *port)
+{
+ int err = 0;
+
+ if (unlikely(fifo >= HSI_HST_FIFO_COUNT)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ }
+
+ if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT1) {
+ *channel = fifo;
+ *port = 1;
+ } else if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_SSI) {
+ if (fifo < 8) {
+ *channel = fifo;
+ *port = 1;
+ } else {
+ *channel = fifo - 8;
+ *port = 2;
+ }
+ } else {
+ err = -EPERM;
+ goto fifo_id_bk;
+ }
+
+fifo_id_bk:
+ if (unlikely(err < 0))
+ dev_err(hsi_ctrl->dev, "Cannot map a channel / port to the "
+ "requested params: fifo:%d; ERR=%d\n", fifo, err);
+
+ return err;
+}
+
+/**
+ * hsi_fifo_mapping - Configures the HSI FIFO mapping registers.
+ * @hsi_ctrl - HSI controler data
+ * @mtype - mapping strategy
+ *
+ * Returns 0 in case of success, and errocode (< 0) else
+ * Configures the HSI FIFO mapping registers. Several mapping strategies are
+ * proposed.
+ * Note: The mapping is identical for Read and Write path.
+ * This exclusively applies to HSI devices.
+ */
+int __init hsi_fifo_mapping(struct hsi_dev *hsi_ctrl,
+ unsigned int mtype)
+{
+ int err = 0;
+ void __iomem *base = hsi_ctrl->base;
+ int i;
+ unsigned int channel, port;
+
+ if (mtype == HSI_FIFO_MAPPING_ALL_PORT1) {
+ channel = 0;
+ for (i = 0; i < HSI_HST_FIFO_COUNT; i++) {
+ hsi_outl(HSI_MAPPING_ENABLE |
+ (channel << HSI_MAPPING_CH_NUMBER_OFFSET) |
+ (0 << HSI_MAPPING_PORT_NUMBER_OFFSET) |
+ HSI_HST_MAPPING_THRESH_VALUE,
+ base, HSI_HST_MAPPING_FIFO_REG(i));
+ hsi_outl(HSI_MAPPING_ENABLE |
+ (channel << HSI_MAPPING_CH_NUMBER_OFFSET) |
+ (0 << HSI_MAPPING_PORT_NUMBER_OFFSET),
+ base, HSI_HSR_MAPPING_FIFO_REG(i));
+ channel++;
+ }
+ hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_ALL_PORT1;
+ } else if (mtype == HSI_FIFO_MAPPING_SSI) {
+ channel = 0;
+ port = 0;
+ for (i = 0; i < HSI_HST_FIFO_COUNT; i++) {
+ hsi_outl(HSI_MAPPING_ENABLE |
+ (channel << HSI_MAPPING_CH_NUMBER_OFFSET) |
+ (port << HSI_MAPPING_PORT_NUMBER_OFFSET) |
+ HSI_HST_MAPPING_THRESH_VALUE,
+ base, HSI_HST_MAPPING_FIFO_REG(i));
+ hsi_outl(HSI_MAPPING_ENABLE |
+ (channel << HSI_MAPPING_CH_NUMBER_OFFSET) |
+ (port << HSI_MAPPING_PORT_NUMBER_OFFSET),
+ base, HSI_HSR_MAPPING_FIFO_REG(i));
+ channel++;
+ if (channel == 8) {
+ channel = 0;
+ port = 1;
+ }
+ }
+
+ hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_DEFAULT;
+ } else {
+ hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_UNDEF;
+ dev_err(hsi_ctrl->dev, "Bad Fifo strategy request\n");
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/**
+ * hsi_hst_bufstate_f_reg - Return the proper HSI_HST_BUFSTATE register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HST_BUFSTATE register offset
+ * Note: indexing of BUFSTATE registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hst_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel) {
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (fifo < 0)
+ return fifo;
+ else
+ return HSI_HST_BUFSTATE_FIFO_REG(fifo);
+ } else {
+ return HSI_HST_BUFSTATE_REG(port);
+ }
+}
+
+/**
+ * hsi_hsr_bufstate_f_reg - Return the proper HSI_HSR_BUFSTATE register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HSR_BUFSTATE register offset
+ * Note: indexing of BUFSTATE registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hsr_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel) {
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (fifo < 0)
+ return fifo;
+ else
+ return HSI_HSR_BUFSTATE_FIFO_REG(fifo);
+ } else {
+ return HSI_HSR_BUFSTATE_REG(port);
+ }
+}
+
+/**
+ * hsi_hst_buffer_f_reg - Return the proper HSI_HST_BUFFER register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HST_BUFFER register offset
+ * Note: indexing of BUFFER registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hst_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel) {
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0))
+ return fifo;
+ else
+ return HSI_HST_BUFFER_FIFO_REG(fifo);
+ } else {
+ return HSI_HST_BUFFER_CH_REG(port, channel);
+ }
+}
+
+/**
+ * hsi_hsr_buffer_f_reg - Return the proper HSI_HSR_BUFFER register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HSR_BUFFER register offset
+ * Note: indexing of BUFFER registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hsr_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel) {
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (fifo < 0)
+ return fifo;
+ else
+ return HSI_HSR_BUFFER_FIFO_REG(fifo);
+ } else {
+ return HSI_HSR_BUFFER_CH_REG(port, channel);
+ }
+}
+
diff --git a/drivers/hsi/hsi_driver_gpio.c b/drivers/hsi/hsi_driver_gpio.c
new file mode 100644
index 0000000..2410973
--- /dev/null
+++ b/drivers/hsi/hsi_driver_gpio.c
@@ -0,0 +1,79 @@
+/*
+ * hsi_driver_gpio.c
+ *
+ * Implements HSI GPIO related functionality. (i.e: wake lines management)
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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/gpio.h>
+#include "hsi_driver.h"
+
+static void do_hsi_cawake_tasklet(unsigned long hsi_p)
+{
+ struct hsi_port *port = (struct hsi_port *)hsi_p;
+ struct hsi_dev *hsi_ctrl = port->hsi_controller;
+
+ if (hsi_cawake(port)) {
+ if (!hsi_ctrl->cawake_clk_enable) {
+ hsi_ctrl->cawake_clk_enable = 1;
+ clk_enable(hsi_ctrl->hsi_clk);
+ }
+ hsi_port_event_handler(port, HSI_EVENT_CAWAKE_UP, NULL);
+ } else {
+ hsi_port_event_handler(port, HSI_EVENT_CAWAKE_DOWN, NULL);
+ if (hsi_ctrl->cawake_clk_enable) {
+ hsi_ctrl->cawake_clk_enable = 0;
+ clk_disable(hsi_ctrl->hsi_clk);
+ }
+ }
+}
+
+static irqreturn_t hsi_cawake_isr(int irq, void *hsi_p)
+{
+ struct hsi_port *port = hsi_p;
+
+ tasklet_hi_schedule(&port->cawake_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+int __init hsi_cawake_init(struct hsi_port *port, const char *irq_name)
+{
+ tasklet_init(&port->cawake_tasklet, do_hsi_cawake_tasklet,
+ (unsigned long)port);
+
+ if (request_irq(port->cawake_gpio_irq, hsi_cawake_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ irq_name, port) < 0) {
+ dev_err(port->hsi_controller->dev,
+ "FAILED to request %s GPIO IRQ %d on port %d\n",
+ irq_name, port->cawake_gpio_irq, port->port_number);
+ return -EBUSY;
+ }
+ enable_irq_wake(port->cawake_gpio_irq);
+
+ return 0;
+}
+
+void hsi_cawake_exit(struct hsi_port *port)
+{
+ if (port->cawake_gpio < 0)
+ return; /* Nothing to do */
+
+ disable_irq_wake(port->cawake_gpio_irq);
+ tasklet_kill(&port->cawake_tasklet);
+ free_irq(port->cawake_gpio_irq, port);
+}
diff --git a/drivers/hsi/hsi_driver_int.c b/drivers/hsi/hsi_driver_int.c
new file mode 100644
index 0000000..d04daf7
--- /dev/null
+++ b/drivers/hsi/hsi_driver_int.c
@@ -0,0 +1,339 @@
+/*
+ * hsi_driver_int.c
+ *
+ * Implements HSI interrupt functionality.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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 "hsi_driver.h"
+
+void hsi_reset_ch_read(struct hsi_channel *ch)
+{
+ ch->read_data.addr = NULL;
+ ch->read_data.size = 0;
+ ch->read_data.lch = -1;
+}
+
+void hsi_reset_ch_write(struct hsi_channel *ch)
+{
+ ch->write_data.addr = NULL;
+ ch->write_data.size = 0;
+ ch->write_data.lch = -1;
+}
+
+int hsi_driver_write_interrupt(struct hsi_channel *ch, u32 *data)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+
+ clk_enable(p->hsi_controller->hsi_clk);
+
+ hsi_outl_or(HSI_HST_DATAACCEPT(channel), p->hsi_controller->base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ return 0;
+}
+
+int hsi_driver_read_interrupt(struct hsi_channel *ch, u32 *data)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+
+ clk_enable(p->hsi_controller->hsi_clk);
+
+ hsi_outl_or(HSI_HSR_DATAAVAILABLE(channel), p->hsi_controller->base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ clk_disable(p->hsi_controller->hsi_clk);
+
+ return 0;
+}
+
+void hsi_driver_cancel_write_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+ u32 enable;
+ long buff_offset;
+
+ clk_enable(p->hsi_controller->hsi_clk);
+
+ enable = hsi_inl(base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ if (!(enable & HSI_HST_DATAACCEPT(channel))) {
+ dev_dbg(&ch->dev->device, LOG_NAME "Write cancel on not "
+ "enabled channel %d ENABLE REG 0x%08X", channel, enable);
+ clk_disable(p->hsi_controller->hsi_clk);
+ return;
+ }
+
+ hsi_outl_and(~HSI_HST_DATAACCEPT(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ buff_offset = hsi_hst_bufstate_f_reg(p->hsi_controller, port, channel);
+ if (buff_offset >= 0)
+ hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), base,
+ buff_offset);
+
+ hsi_reset_ch_write(ch);
+
+ clk_disable(p->hsi_controller->hsi_clk);
+ clk_disable(p->hsi_controller->hsi_clk); /* FIXME - can be removed? */
+}
+
+void hsi_driver_disable_read_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+
+ clk_enable(p->hsi_controller->hsi_clk);
+
+ hsi_outl_and(~HSI_HSR_DATAAVAILABLE(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ clk_disable(p->hsi_controller->hsi_clk);
+}
+
+void hsi_driver_cancel_read_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+
+ clk_enable(p->hsi_controller->hsi_clk);
+
+ hsi_outl_and(~HSI_HSR_DATAAVAILABLE(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ hsi_reset_ch_read(ch);
+
+ clk_disable(p->hsi_controller->hsi_clk);
+}
+
+static void do_channel_tx(struct hsi_channel *ch)
+{
+ struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int n_ch;
+ unsigned int n_p;
+ unsigned int irq;
+ long buff_offset;
+
+ n_ch = ch->channel_number;
+ n_p = ch->hsi_port->port_number;
+ irq = ch->hsi_port->n_irq;
+
+ spin_lock(&hsi_ctrl->lock);
+
+ if (ch->write_data.addr == NULL) {
+ hsi_outl_and(~HSI_HST_DATAACCEPT(n_ch), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(n_p, irq, n_ch));
+ hsi_reset_ch_write(ch);
+ spin_unlock(&hsi_ctrl->lock);
+ clk_disable(hsi_ctrl->hsi_clk);
+ (*ch->write_done)(ch->dev, 4);
+ } else {
+ buff_offset = hsi_hst_buffer_reg(hsi_ctrl, n_p, n_ch);
+ if (buff_offset >= 0) {
+ hsi_outl(*(ch->write_data.addr), base, buff_offset);
+ ch->write_data.addr = NULL;
+ }
+ spin_unlock(&hsi_ctrl->lock);
+ }
+}
+
+static void do_channel_rx(struct hsi_channel *ch)
+{
+ struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller;
+ void __iomem *base = ch->hsi_port->hsi_controller->base;
+ unsigned int n_ch;
+ unsigned int n_p;
+ unsigned int irq;
+ long buff_offset;
+ int rx_poll = 0;
+ int data_read = 0;
+
+ n_ch = ch->channel_number;
+ n_p = ch->hsi_port->port_number;
+ irq = ch->hsi_port->n_irq;
+
+ spin_lock(&hsi_ctrl->lock);
+
+ if (ch->flags & HSI_CH_RX_POLL)
+ rx_poll = 1;
+
+ if (ch->read_data.addr) {
+ buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, n_p, n_ch);
+ if (buff_offset >= 0) {
+ data_read = 1;
+ *(ch->read_data.addr) = hsi_inl(base, buff_offset);
+ }
+ }
+
+ hsi_outl_and(~HSI_HSR_DATAAVAILABLE(n_ch), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(n_p, irq, n_ch));
+ hsi_reset_ch_read(ch);
+
+ spin_unlock(&hsi_ctrl->lock);
+
+ if (rx_poll)
+ hsi_port_event_handler(ch->hsi_port,
+ HSI_EVENT_HSR_DATAAVAILABLE, (void *)n_ch);
+
+ if (data_read)
+ (*ch->read_done)(ch->dev, 4);
+}
+
+/**
+ * hsi_driver_int_proc - check all channels / ports for interrupts events
+ * @hsi_ctrl - HSI controler data
+ * @status_offset: interrupt status register offset
+ * @enable_offset: interrupt enable regiser offset
+ * @start: interrupt index to start on
+ * @stop: interrupt index to stop on
+ *
+ * This function calls the related processing functions and triggered events
+*/
+static void hsi_driver_int_proc(struct hsi_port *pport,
+ unsigned long status_offset, unsigned long enable_offset,
+ unsigned int start, unsigned int stop)
+{
+ struct hsi_dev *hsi_ctrl = pport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = pport->port_number;
+ unsigned int channel;
+ u32 status_reg;
+ u32 hsr_err_reg;
+ u32 channels_served = 0;
+
+ status_reg = hsi_inl(base, status_offset);
+ status_reg &= hsi_inl(base, enable_offset);
+
+ for (channel = start; channel < stop; channel++) {
+ if (status_reg & HSI_HST_DATAACCEPT(channel)) {
+
+ do_channel_tx(&pport->hsi_channel[channel]);
+ channels_served |= HSI_HST_DATAACCEPT(channel);
+ }
+
+ if (status_reg & HSI_HSR_DATAAVAILABLE(channel)) {
+ do_channel_rx(&pport->hsi_channel[channel]);
+ channels_served |= HSI_HSR_DATAAVAILABLE(channel);
+ }
+ }
+
+ if (status_reg & HSI_BREAKDETECTED) {
+ dev_info(hsi_ctrl->dev, "Hardware BREAK on port %d\n", port);
+ hsi_outl(0, base, HSI_HSR_BREAK_REG(port));
+ hsi_port_event_handler(pport, HSI_EVENT_BREAK_DETECTED, NULL);
+ channels_served |= HSI_BREAKDETECTED;
+ }
+
+ if (status_reg & HSI_ERROROCCURED) {
+ hsr_err_reg = hsi_inl(base, HSI_HSR_ERROR_REG(port));
+ dev_err(hsi_ctrl->dev, "HSI ERROR Port %d: 0x%x\n",
+ port, hsr_err_reg);
+ hsi_outl(hsr_err_reg, base, HSI_HSR_ERRORACK_REG(port));
+ if (hsr_err_reg) /* ignore spurious errors */
+ hsi_port_event_handler(pport, HSI_EVENT_ERROR, NULL);
+ else
+ dev_dbg(hsi_ctrl->dev, "Spurious HSI error!\n");
+
+ channels_served |= HSI_ERROROCCURED;
+ }
+
+ hsi_outl(channels_served, base, status_offset);
+}
+
+static void do_hsi_tasklet(unsigned long hsi_port)
+{
+ struct hsi_port *pport = (struct hsi_port *)hsi_port;
+ struct hsi_dev *hsi_ctrl = pport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = pport->port_number;
+ unsigned int irq = pport->n_irq;
+ u32 status_reg;
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ hsi_driver_int_proc(pport,
+ HSI_SYS_MPU_STATUS_REG(port, irq),
+ HSI_SYS_MPU_ENABLE_REG(port, irq),
+ 0, min(pport->max_ch, (u8) HSI_SSI_CHANNELS_MAX));
+
+ if (pport->max_ch > HSI_SSI_CHANNELS_MAX)
+ hsi_driver_int_proc(pport,
+ HSI_SYS_MPU_U_STATUS_REG(port, irq),
+ HSI_SYS_MPU_U_ENABLE_REG(port, irq),
+ HSI_SSI_CHANNELS_MAX, pport->max_ch);
+
+ status_reg = hsi_inl(base, HSI_SYS_MPU_STATUS_REG(port, irq)) &
+ hsi_inl(base, HSI_SYS_MPU_ENABLE_REG(port, irq));
+
+ if (hsi_driver_device_is_hsi(pd))
+ status_reg |=
+ (hsi_inl(base, HSI_SYS_MPU_U_STATUS_REG(port, irq)) &
+ hsi_inl(base, HSI_SYS_MPU_U_ENABLE_REG(port, irq)));
+
+ clk_disable(hsi_ctrl->hsi_clk);
+
+ if (status_reg)
+ tasklet_hi_schedule(&pport->hsi_tasklet);
+ else
+ enable_irq(pport->irq);
+}
+
+static irqreturn_t hsi_mpu_handler(int irq, void *hsi_port)
+{
+ struct hsi_port *p = hsi_port;
+
+ tasklet_hi_schedule(&p->hsi_tasklet);
+ disable_irq_nosync(p->irq);
+
+ return IRQ_HANDLED;
+}
+
+int __init hsi_mpu_init(struct hsi_port *hsi_p, const char *irq_name)
+{
+ int err;
+
+ tasklet_init(&hsi_p->hsi_tasklet, do_hsi_tasklet,
+ (unsigned long)hsi_p);
+ err = request_irq(hsi_p->irq, hsi_mpu_handler, IRQF_DISABLED,
+ irq_name, hsi_p);
+ if (err < 0) {
+ dev_err(hsi_p->hsi_controller->dev, "FAILED to MPU request"
+ " IRQ (%d) on port %d", hsi_p->irq, hsi_p->port_number);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void hsi_mpu_exit(struct hsi_port *hsi_p)
+{
+ tasklet_disable(&hsi_p->hsi_tasklet);
+ free_irq(hsi_p->irq, hsi_p);
+}
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 4/9] HSI: Low Level Driver debugfs support
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (2 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 3/9] HSI: Low Level Driver device management Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 5/9] HSI: Low Level Driver documentation Sebastien Jan
` (5 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
All registers dump, and counters configuration
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
drivers/hsi/hsi_driver_debugfs.c | 486 ++++++++++++++++++++++++++++++++++++++
1 files changed, 486 insertions(+), 0 deletions(-)
create mode 100644 drivers/hsi/hsi_driver_debugfs.c
diff --git a/drivers/hsi/hsi_driver_debugfs.c b/drivers/hsi/hsi_driver_debugfs.c
new file mode 100644
index 0000000..d600a88
--- /dev/null
+++ b/drivers/hsi/hsi_driver_debugfs.c
@@ -0,0 +1,486 @@
+/*
+ * hsi_driver_debugfs.c
+ *
+ * Implements HSI debugfs.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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/err.h>
+#include <linux/list.h>
+#include <mach/clock.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include "hsi_driver.h"
+
+#define HSI_DIR_NAME_SIZE 64
+
+static struct dentry *hsi_dir;
+
+static int hsi_debug_show(struct seq_file *m, void *p)
+{
+ struct hsi_dev *hsi_ctrl = m->private;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ seq_printf(m, "REVISION\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_REVISION_REG));
+ if (hsi_driver_device_is_hsi(pdev))
+ seq_printf(m, "HWINFO\t\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_HWINFO_REG));
+ seq_printf(m, "SYSCONFIG\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_SYSCONFIG_REG));
+ seq_printf(m, "SYSSTATUS\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_SYSSTATUS_REG));
+
+ clk_disable(hsi_ctrl->hsi_clk);
+ return 0;
+}
+
+static int hsi_debug_port_show(struct seq_file *m, void *p)
+{
+ struct hsi_port *hsi_port = m->private;
+ struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_port->port_number;
+ int ch, fifo;
+ long buff_offset;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ if (hsi_port->cawake_gpio >= 0)
+ seq_printf(m, "CAWAKE\t\t: %d\n", hsi_cawake(hsi_port));
+
+ seq_printf(m, "WAKE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_SYS_WAKE_REG(port)));
+ seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
+ hsi_inl(base, HSI_SYS_MPU_ENABLE_REG(port, hsi_port->n_irq)));
+ seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
+ hsi_inl(base, HSI_SYS_MPU_STATUS_REG(port, hsi_port->n_irq)));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "MPU_U_ENABLE_IRQ%d\t: 0x%08x\n",
+ hsi_port->n_irq,
+ hsi_inl(base, HSI_SYS_MPU_U_ENABLE_REG(port,
+ hsi_port->n_irq)));
+ seq_printf(m, "MPU_U_STATUS_IRQ%d\t: 0x%08x\n",
+ hsi_port->n_irq,
+ hsi_inl(base, HSI_SYS_MPU_U_STATUS_REG(port,
+ hsi_port->n_irq)));
+ }
+ /* HST */
+ seq_printf(m, "\nHST\n===\n");
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_MODE_REG(port)));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_FRAMESIZE_REG(port)));
+ seq_printf(m, "DIVISOR\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_DIVISOR_REG(port)));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_CHANNELS_REG(port)));
+ seq_printf(m, "ARBMODE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_ARBMODE_REG(port)));
+ seq_printf(m, "TXSTATE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_TXSTATE_REG(port)));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "BUFSTATE P1\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BUFSTATE_REG(1)));
+ seq_printf(m, "BUFSTATE P2\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BUFSTATE_REG(2)));
+ } else {
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BUFSTATE_REG(port)));
+ }
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BREAK_REG(port)));
+ for (ch = 0; ch < hsi_port->max_ch; ch++) {
+ buff_offset = hsi_hst_buffer_reg(hsi_ctrl, port, ch);
+ if (buff_offset >= 0)
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ hsi_inl(base, buff_offset));
+ }
+ if (hsi_driver_device_is_hsi(pdev)) {
+ for (fifo = 0; fifo < HSI_HST_FIFO_COUNT; fifo++) {
+ seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo,
+ hsi_inl(base, HSI_HST_MAPPING_FIFO_REG(fifo)));
+ }
+ }
+ /* HSR */
+ seq_printf(m, "\nHSR\n===\n");
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_MODE_REG(port)));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_FRAMESIZE_REG(port)));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_CHANNELS_REG(port)));
+ seq_printf(m, "COUNTERS\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_COUNTERS_REG(port)));
+ seq_printf(m, "RXSTATE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_RXSTATE_REG(port)));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "BUFSTATE P1\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BUFSTATE_REG(1)));
+ seq_printf(m, "BUFSTATE P2\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BUFSTATE_REG(2)));
+ } else {
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BUFSTATE_REG(port)));
+ }
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BREAK_REG(port)));
+ seq_printf(m, "ERROR\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_ERROR_REG(port)));
+ seq_printf(m, "ERRORACK\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_ERRORACK_REG(port)));
+ for (ch = 0; ch < hsi_port->max_ch; ch++) {
+ buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, port, ch);
+ if (buff_offset >= 0)
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ hsi_inl(base, buff_offset));
+ }
+ if (hsi_driver_device_is_hsi(pdev)) {
+ for (fifo = 0; fifo < HSI_HSR_FIFO_COUNT; fifo++) {
+ seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo,
+ hsi_inl(base,
+ HSI_HSR_MAPPING_FIFO_REG(fifo)));
+ }
+ seq_printf(m, "DLL\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_DLL_REG(port)));
+ seq_printf(m, "DIVISOR\t: 0x%08x\n",
+ hsi_inl(base,
+ HSI_HSR_DIVISOR_REG(port)));
+ }
+ clk_disable(hsi_ctrl->hsi_clk);
+ return 0;
+}
+
+static int hsi_debug_gdd_show(struct seq_file *m, void *p)
+{
+ struct hsi_dev *hsi_ctrl = m->private;
+ void __iomem *base = hsi_ctrl->base;
+ int lch;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ clk_enable(hsi_ctrl->hsi_clk);
+
+ seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n",
+ hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG));
+ seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n",
+ hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG));
+
+ if (!hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "HW_ID\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_HW_ID_REG));
+ seq_printf(m, "PPORT_ID\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_PPORT_ID_REG));
+ seq_printf(m, "MPORT_ID\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_MPORT_ID_REG));
+ seq_printf(m, "TEST\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_TEST_REG));
+ }
+
+ seq_printf(m, "GCR\t\t: 0x%08x\n", hsi_inl(base, HSI_GDD_GCR_REG));
+
+ for (lch = 0; lch < hsi_ctrl->gdd_chan_count; lch++) {
+ seq_printf(m, "\nGDD LCH %d\n=========\n", lch);
+ seq_printf(m, "CSDP\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CSDP_REG(lch)));
+ seq_printf(m, "CCR\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CCR_REG(lch)));
+ seq_printf(m, "CICR\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CICR_REG(lch)));
+ seq_printf(m, "CSR\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CSR_REG(lch)));
+ seq_printf(m, "CSSA\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_GDD_CSSA_REG(lch)));
+ seq_printf(m, "CDSA\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_GDD_CDSA_REG(lch)));
+ seq_printf(m, "CEN\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CEN_REG(lch)));
+ seq_printf(m, "CSAC\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CSAC_REG(lch)));
+ seq_printf(m, "CDAC\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CDAC_REG(lch)));
+ if (!hsi_driver_device_is_hsi(pdev))
+ seq_printf(m, "CLNK_CTRL\t: 0x%04x\n",
+ hsi_inw(base, HSI_SSI_GDD_CLNK_CTRL_REG(lch)));
+ }
+
+ clk_disable(hsi_ctrl->hsi_clk);
+ return 0;
+}
+
+
+static int hsi_port_counters_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int hsi_port_counters_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static loff_t hsi_port_counters_seek(struct file *file, loff_t off, int whence)
+{
+ return 0;
+}
+
+static ssize_t hsi_port_counters_read(struct file *filep, char __user *buff,
+ size_t count, loff_t *offp)
+{
+ ssize_t ret;
+ struct hsi_port *hsi_port = filep->private_data;
+ struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_port->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+ char str[50];
+ unsigned long reg;
+
+ if (*offp > 0) {
+ ret = 0;
+ goto hsi_cnt_rd_bk;
+ }
+
+ clk_enable(hsi_ctrl->hsi_clk);
+ reg = hsi_inl(base, HSI_HSR_COUNTERS_REG(port));
+ clk_disable(hsi_ctrl->hsi_clk);
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ sprintf(str, "FT:%d, TB:%d, FB:%d\n",
+ (int) (reg & HSI_COUNTERS_FT_MASK) >> HSI_COUNTERS_FT_OFFSET,
+ (int) (reg & HSI_COUNTERS_TB_MASK) >> HSI_COUNTERS_TB_OFFSET,
+ (int) (reg & HSI_COUNTERS_FB_MASK) >> HSI_COUNTERS_FB_OFFSET);
+ } else {
+ sprintf(str, "timeout:%d\n", (int) reg);
+ }
+
+ ret = strlen(str);
+ if (copy_to_user((void __user *)buff, str, ret)) {
+ dev_err(hsi_ctrl->dev, "copy_to_user failed\n");
+ ret = 0;
+ } else {
+ *offp = ret;
+ }
+
+hsi_cnt_rd_bk:
+ return ret;
+}
+
+/*
+ * Split the buffer `buf' into space-separated words.
+ * Return the number of words or <0 on error.
+ */
+static int hsi_debug_tokenize(char *buf, char *words[], int maxwords)
+{
+ int nwords = 0;
+
+ while (*buf) {
+ char *end;
+
+ /* Skip leading whitespace */
+ while (*buf && isspace(*buf))
+ buf++;
+ if (!*buf)
+ break; /* oh, it was trailing whitespace */
+
+ /* Run `end' over a word */
+ for (end = buf ; *end && !isspace(*end) ; end++)
+ ;
+ /* `buf' is the start of the word, `end' is one past the end */
+
+ if (nwords == maxwords)
+ return -EINVAL; /* ran out of words[] before bytes */
+ if (*end)
+ *end++ = '\0'; /* terminate the word */
+ words[nwords++] = buf;
+ buf = end;
+ }
+ return nwords;
+}
+
+static ssize_t hsi_port_counters_write(struct file *filep,
+ const char __user *buff, size_t count, loff_t *offp)
+{
+ ssize_t ret;
+ struct hsi_port *hsi_port = filep->private_data;
+ struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_port->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+#define MAXWORDS 4
+ int nwords;
+ char *words[MAXWORDS];
+ char tmpbuf[256];
+ unsigned long reg, ft, tb, fb;
+
+ if (count == 0)
+ return 0;
+ if (count > sizeof(tmpbuf)-1)
+ return -E2BIG;
+ if (copy_from_user(tmpbuf, buff, count))
+ return -EFAULT;
+ tmpbuf[count] = '\0';
+ dev_dbg(hsi_ctrl->dev, "%s: read %d bytes from userspace\n",
+ __func__, (int)count);
+
+ nwords = hsi_debug_tokenize(tmpbuf, words, MAXWORDS);
+ if (nwords < 0) {
+ dev_warn(hsi_ctrl->dev,
+ "HSI counters write usage: echo <values> > counters\n");
+ return -EINVAL;
+ }
+
+ clk_enable(hsi_ctrl->hsi_clk);
+ if (hsi_driver_device_is_hsi(pdev)) {
+ if (nwords != 3) {
+ dev_warn(hsi_ctrl->dev, "HSI counters write usage: "
+ "echo \"FT TB FB\" > counters\n");
+ ret = -EINVAL;
+ goto hsi_cnt_w_bk1;
+ }
+ strict_strtoul(words[0], 0, &ft);
+ strict_strtoul(words[1], 0, &tb);
+ strict_strtoul(words[2], 0, &fb);
+ reg = ((ft << HSI_COUNTERS_FT_OFFSET & HSI_COUNTERS_FT_MASK) |
+ (tb << HSI_COUNTERS_TB_OFFSET & HSI_COUNTERS_TB_MASK) |
+ (fb << HSI_COUNTERS_FB_OFFSET & HSI_COUNTERS_FB_MASK));
+ } else {
+ if (nwords != 1) {
+ dev_warn(hsi_ctrl->dev, "HSI counters write usage: "
+ "echo \"timeout\" > counters\n");
+ ret = -EINVAL;
+ goto hsi_cnt_w_bk1;
+ }
+ strict_strtoul(words[0], 0, ®);
+ }
+ hsi_outl(reg, base, HSI_HSR_COUNTERS_REG(port));
+ ret = count;
+ *offp += count;
+
+hsi_cnt_w_bk1:
+ clk_disable(hsi_ctrl->hsi_clk);
+
+ return ret;
+}
+
+static int hsi_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hsi_debug_show, inode->i_private);
+}
+
+static int hsi_port_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hsi_debug_port_show, inode->i_private);
+}
+
+static int hsi_gdd_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hsi_debug_gdd_show, inode->i_private);
+}
+
+static const struct file_operations hsi_regs_fops = {
+ .open = hsi_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations hsi_port_regs_fops = {
+ .open = hsi_port_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations hsi_port_counters_fops = {
+ .open = hsi_port_counters_open,
+ .read = hsi_port_counters_read,
+ .write = hsi_port_counters_write,
+ .llseek = hsi_port_counters_seek,
+ .release = hsi_port_counters_release,
+};
+
+static const struct file_operations hsi_gdd_regs_fops = {
+ .open = hsi_gdd_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int __init hsi_debug_add_ctrl(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+ unsigned char dir_name[HSI_DIR_NAME_SIZE];
+ struct dentry *dir;
+ unsigned int port;
+
+ if (pdev->id < 0) {
+ hsi_ctrl->dir = debugfs_create_dir(pdev->name, hsi_dir);
+ } else {
+ snprintf(dir_name, sizeof(dir_name), "%s%d", pdev->name,
+ pdev->id);
+ hsi_ctrl->dir = debugfs_create_dir(dir_name, hsi_dir);
+ }
+ if (IS_ERR(hsi_ctrl->dir))
+ return PTR_ERR(hsi_ctrl->dir);
+
+ debugfs_create_file("regs", S_IRUGO, hsi_ctrl->dir, hsi_ctrl,
+ &hsi_regs_fops);
+
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ snprintf(dir_name, sizeof(dir_name), "port%d", port + 1);
+ dir = debugfs_create_dir(dir_name, hsi_ctrl->dir);
+ if (IS_ERR(dir))
+ goto rback;
+ debugfs_create_file("regs", S_IRUGO, dir,
+ &hsi_ctrl->hsi_port[port], &hsi_port_regs_fops);
+ debugfs_create_file("counters", S_IRUGO | S_IWUGO, dir,
+ &hsi_ctrl->hsi_port[port],
+ &hsi_port_counters_fops);
+ }
+
+ dir = debugfs_create_dir("gdd", hsi_ctrl->dir);
+ if (IS_ERR(dir))
+ goto rback;
+ debugfs_create_file("regs", S_IRUGO, dir, hsi_ctrl, &hsi_gdd_regs_fops);
+
+ return 0;
+rback:
+ debugfs_remove_recursive(hsi_ctrl->dir);
+ return PTR_ERR(dir);
+}
+
+void hsi_debug_remove_ctrl(struct hsi_dev *hsi_ctrl)
+{
+ debugfs_remove_recursive(hsi_ctrl->dir);
+}
+
+int __init hsi_debug_init(void)
+{
+ hsi_dir = debugfs_create_dir("hsi", NULL);
+ if (IS_ERR(hsi_dir))
+ return PTR_ERR(hsi_dir);
+
+ return 0;
+}
+
+void hsi_debug_exit(void)
+{
+ debugfs_remove_recursive(hsi_dir);
+}
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 5/9] HSI: Low Level Driver documentation
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (3 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 4/9] HSI: Low Level Driver debugfs support Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 6/9] HSI: character driver interface Sebastien Jan
` (4 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
Documentation/hsi/hsi.txt | 319 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 319 insertions(+), 0 deletions(-)
create mode 100644 Documentation/hsi/hsi.txt
diff --git a/Documentation/hsi/hsi.txt b/Documentation/hsi/hsi.txt
new file mode 100644
index 0000000..0890cff
--- /dev/null
+++ b/Documentation/hsi/hsi.txt
@@ -0,0 +1,319 @@
+OMAP HSI API's How To
+=====================
+
+The MIPI High Speed Synchronous Serial Interface (HSI) is a high speed
+communication interface that is used for connecting OMAP to a cellular modem
+engine.
+It is specified by the MIPI alliance (www.mipi.org). An introduction to the
+MIPI HSI working group can be found here: http://www.mipi.org/wgoverview.shtml
+
+The HSI interface supports full duplex communication over multiple channels and
+is capable of reaching speeds up to 200 Mbit/s.
+
+The OMAP HSI driver supports both OMAP MIPI HSI (as defined in
+MIPI documentation mipi_HSI-PL_specification_v01-01-00a.pdf) and OMAP SSI
+devices through different device files, and a generic SW driver.
+
+Please refer to the MIPI specifications for more details on this device.
+
+
+I OMAP HSI driver API overview
+-----------------------------
+
+A) HSI Bus, HSI channels and protocol drivers overview.
+
+The OMAP HSI driver implements the low-level support for the HSI device. It
+abstracts device specifics and provides a simple interface inside the kernel
+for data transmission on the HSI channels.
+The OMAP HSI driver does not implement any communication protocol.
+The SW layers using the OMAP HSI driver may implement a communication protocol
+if required, and are commonly called 'protocol drivers' in this document.
+
+The OMAP HSI abstracts the concept of HSI channels by creating an HSI bus and
+attaching HSI channel devices to it. (see Figure 1)
+
+Protocol drivers will then claim one or more HSI channels, after registering
+with the OMAP HSI driver.
+
+ +---------------------+ +----------------+
+ + HSI channel device + + HSI protocol +
+ + (omap_hsi.pX-cY) + <-------+ driver +
+ +---------------------+ +----------------+
+ | |
+(/sys/bus/hsi/devices/omap_hsi.pX-cy) (/sys/bus/hsi/drivers/hsi_protocol)
+ | |
++----------------------------------------------------------------+
++ HSI bus +
++----------------------------------------------------------------+
+
+ Figure 1.
+
+(NOTE: omap_hsi.pX-cY represents the HSI channel Y on port X from the omap_hsi
+device)
+
+B) Data transfers
+
+The OMAP HSI driver exports an asynchronous interface for sending and receiving
+data over the HSI channels. Protocol drivers will register a set of read and
+write completion callbacks for each HSI channel they use.
+
+Protocol drivers call hsi_write/hsi_read functions to signal the OMAP HSI driver
+that is willing to write/read data to/from a channel. Transfers are completed
+only when the OMAP HSI driver calls the completion callback.
+
+An HSI channel can simultaneously have both a read and a write request
+pending, however, requests cannot be queued.
+
+It is safe to call hsi_write/hsi_read functions inside the callback functions.
+In fact, a protocol driver should normally re-issue the read request from within
+the read callback, in order to not miss any incoming messages.
+
+Note on read / write operations:
+A read or write is performed using a HSI internal DMA channel, unless the size
+of data to transmit is one 32bits Word, where the transmission is directly
+managed through interrupts.
+
+C) Error handling
+
+HSI is a multi channel interface but the channels share the same physical wires.
+Therefore, any transmission error potentially affects all the protocol drivers
+that sit on top of the HSI driver. Whenever an error occurs, it is broadcast
+to all protocol drivers.
+
+Errors are signaled to the protocol drivers through the port_event callback.
+
+Completion callbacks functions are only called when a transfer has succeess.
+
+D) Supported modes of operation
+
+The driver supports stream and frame transmission modes and synchronized and
+pipelined data flows.
+The driver implements the HSI support for the core MPU and not for the
+potential co-processors.
+
+
+II OMAP HSI API's
+-----------------
+
+A) Include
+
+#include<linux/hsi_driver_if.h>
+
+B) int hsi_register_driver(struct hsi_device_driver *driver);
+
+ Description: Register an HSI protocol driver
+
+ Parameter: A protocol driver declaration (see struct hsi_device_driver)
+
+C) void hsi_unregister_driver(struct hsi_device_driver *driver);
+
+ Description: Unregister an HSI protocol driver
+
+ Parameter: A protocol driver declaration (see struct hsi_device_driver)
+
+D) int hsi_open(struct hsi_device *dev);
+
+ Description: Open an HSI device channel
+
+ Parameter: The HSI channel
+
+E) int hsi_write(struct hsi_device *dev, u32 *data, unsigned int count);
+
+ Description: Send data through an HSI channel. The transfer is only completed
+ when the write_complete callback is called
+
+ Parameters:
+ - dev: HSI channel
+ - data: pointer to the data to send
+ - count: number of 32-bit words to be sent
+
+F) void hsi_write_cancel(struct hsi_device *dev);
+
+ Description: Cancel current pending write operation
+
+ Parameters: HSI channel
+
+G) int hsi_read(struct hsi_device *dev, u32 *data, unsigned int count);
+
+ Description: Receive data through an HSI channel. The transfer is only
+ completed when the read_complete callback is called
+
+ Parameters:
+ - dev: HSI channel
+ - data: pointer where to store the data
+ - count: number of 32-bit words to be read
+
+H) void hsi_read_cancel(struct hsi_device *dev);
+
+ Description: Cancel current pending read operation
+
+ Parameters: HSI channel
+
+I) int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg);
+
+ Description: Apply some control command to the port associated to the given
+ HSI channel
+
+ Parameters:
+ - dev: HSI channel
+ - command: command to execute
+ - arg: parameter for the control command
+
+ Commands:
+ - HSI_IOCTL_WAKE_UP:
+ Description: Set HSI wakeup line for the channel
+ Parameters: None
+ - HSI_IOCTL_WAKE_DOWN:
+ Description: Unset HSI wakeup line for the channel
+ Parameters: None
+ - HSI_IOCTL_SEND_BREAK:
+ Description: Send a HW BREAK frame in FRAME mode
+ Parameters: None
+ - HSI_IOCTL_WAKE:
+ Description: Get HST WAKE line status
+ Parameters: Pointer to a u32 variable to return result
+ (Result: 0 means wakeline DOWN, other result means wakeline UP)
+ - HSI_IOCTL_FLUSH_RX:
+ Description: Force the HSR to idle state
+ Parameters: None
+ - HSI_IOCTL_FLUSH_TX:
+ Description: Force the HST to idle state
+ Parameters: None
+ - HSI_IOCTL_CAWAKE:
+ Description: Get CAWAKE (HSR) line status
+ Parameters: Pointer to a u32 variable to return result
+ (Result: 0 means wakeline DOWN, other result means wakeline UP)
+ - HSI_IOCTL_SET_RX:
+ Description: Set HSR configuration
+ Parameters: Pointer to a hsr_ctx structure describing
+ configurable HSR parameters (mode, frame size, channels,
+ data flow type, bit-rate divisor)
+ Notes:
+ HSI: A special value (0x1000) can be passed as bit-rate divisor
+ to request the HSR so switch to auto-divisor mode (in this mode,
+ the HSR can receive at any speed, but the error detection is
+ deactivated). To exit this RX auto-divisor mode, a new divisor
+ must be programmed for HSI (can be 0), and the error-detection
+ is re-enabled.
+ SSI: The same special 0x1000 value is used to deactivate the SSR
+ timeout counter. This counter can be re-enabled by programming
+ the 0x1001 value as bit-rate divisor. The SSI does not accept
+ any other SSR bit-rate divisor values.
+ - HSI_IOCTL_GET_RX:
+ Description: Get HSR configuration
+ Parameters: Pointer to a Hsr_ctx structure describing
+ configurable HSR parameters (mode, frame size, channels,
+ data flow type, bit-rate divisor)
+ - HSI_IOCTL_SET_TX:
+ Description: Set HST configuration
+ Parameters: Pointer to a hst_ctx structure describing
+ configurable HST parameters (mode, frame size, divisor,
+ arb_mode, channels, data flow type)
+ - HSI_IOCTL_GET_TX:
+ Description: Get HST configuration
+ Parameters: Pointer to a hst_ctx structure describing
+ configurable SSR parameters (mode, frame size, divisor,
+ arb_mode, channels, data flow type)
+ Note that the Rx and Tx transmission speeds are configured through the
+ bit-rate divisor parameters. The HSI driver user shall take care of the
+ functional clock provided to the HSI and program the divisors
+ accordingly. Depending on the required speed and HSI device, some
+ constraints on OPP may have to be handled. This shall be managed by the
+ HSI driver user.
+
+J) void hsi_close(struct hsi_device *dev);
+
+ Description: Close an HSI channel
+
+ Parameters: The HSI channel to close
+
+K) Callback configuration functions:
+void hsi_set_read_cb(struct hsi_device *dev,
+ void (*read_cb)(struct hsi_device *dev));
+void hsi_set_write_cb(struct hsi_device *dev,
+ void (*write_cb)(struct hsi_device *dev));
+void hsi_set_port_event_cb(struct hsi_device *dev,
+ void (*port_event_cb)(struct hsi_device *dev,
+ unsigned int event, void *arg));
+
+ Description: Set the read, write and port-event callbacks for the HSI channel.
+ These functions are usually called in the probe function of the HSI protocol
+ driver to set completion callbacks for the asynchronous read and write
+ transfer, and manage the other HSI events.
+
+ Parameters:
+ - dev: HSI channel
+ - read_cb: Pointer to a callback function to signal that a read transfer
+ is completed
+ - write_cb: Pointer to a callback function to signal that a write
+ transfer is completed
+ - port_event_cb: Pointer to a callback function to signal that a HSI
+ event has happened (events can be: Break frame detected, error,
+ cawake-up, cawake-down or HSR-data-available).
+
+
+L) struct hsi_device_driver
+
+Description: Protocol drivers pass this struct to the hsi_register_driver
+function in order to register with the OMAP HSI driver. Among other things it
+tells the OMAP HSI driver which channels the protocol driver wants to allocate
+for its use
+
+Declaration:
+struct hsi_device_driver {
+ unsigned long ctrl_mask;
+ unsigned long ch_mask[HSI_MAX_PORTS];
+ void (*port_event) (struct hsi_device *dev,
+ unsigned int event, void *arg);
+ 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;
+};
+
+Fields description:
+ ctrl_mask: HSI block ids to use
+ ch_mask[HSI_MAX_PORTS]: HSI channels to use
+ port_event: Function callback for notifying HSI events
+ (i.e.: error transfer)
+ Parameters:
+ dev: HSI channel
+ event: Event code
+ event: Event argument
+ probe: Probe function
+ Parameters: HSI channel
+ remove: Remove function
+ Parameters: HSI channel
+
+Example:
+
+static struct hsi_device_driver hsi_protocol_driver = {
+ .ctrl_mask = ANY_HSI_CONTROLLER,
+ .ch_mask[0] = CHANNEL(0) | CHANNEL(1),
+ .port_event = port_event_callback,
+ .probe = hsi_proto_probe,
+ .remove = __devexit_p(hsi_proto_remove),
+ .driver = {
+ .name = "hsi_protocol",
+ },
+};
+
+
+III) FUTURE WORK
+----------------
+ - Power management update and validation on OMAP 3430
+ - Clocks and power management implementation on OMAP 4430
+ - Validation on OMAP 4430
+ - hsi-char documentation
+
+
+=================================================
+Acknowledgements: The OMAP HSI driver is based on OMAP SSI driver, written by
+Carlos Chinea <carlos.chinea@nokia.com>.
+
+Contact: Sebastien Jan <s-jan@ti.com>
+
+Copyright (C) 2008 Nokia Corporation.
+Copyright (C) 2009 Texas Instruments, Inc.
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 6/9] HSI: character driver interface
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (4 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 5/9] HSI: Low Level Driver documentation Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 7/9] HSI: character driver low level interface Sebastien Jan
` (3 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Andras Domokos
Character driver header and interface implementation
Integration in the build and configuration
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Andras Domokos <andras.domokos@nokia.com>
---
drivers/Kconfig | 2 +
drivers/hsi/Kconfig | 14 ++
drivers/hsi/Makefile | 3 +
drivers/hsi/hsi-char.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/hsi/hsi-char.h | 31 +++
include/linux/hsi_char.h | 71 +++++++
6 files changed, 647 insertions(+), 0 deletions(-)
create mode 100644 drivers/hsi/hsi-char.c
create mode 100644 drivers/hsi/hsi-char.h
create mode 100644 include/linux/hsi_char.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 48bbdbe..8fc1c36 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
source "drivers/spi/Kconfig"
+source "drivers/hsi/Kconfig"
+
source "drivers/pps/Kconfig"
source "drivers/gpio/Kconfig"
diff --git a/drivers/hsi/Kconfig b/drivers/hsi/Kconfig
index e10b522..79e0e02 100644
--- a/drivers/hsi/Kconfig
+++ b/drivers/hsi/Kconfig
@@ -42,3 +42,17 @@ config OMAP_SSI_DEVICE
bool "SSI (OMAP SSI)"
endchoice
+
+#
+# OMAP HSI char device kernel configuration
+#
+
+config HSI_CHAR
+ tristate "HSI character driver"
+ depends on OMAP_HSI
+ ---help---
+ If you say Y here, you will enable the HSI character driver.
+ This driver provides a simple character device interface for
+ serial communication over the HSI bus.
+
+endif # HSI
diff --git a/drivers/hsi/Makefile b/drivers/hsi/Makefile
index a7f579b..81270ec 100644
--- a/drivers/hsi/Makefile
+++ b/drivers/hsi/Makefile
@@ -11,4 +11,7 @@ ifeq ($(CONFIG_DEBUG_FS), y)
omap_hsi-objs += hsi_driver_debugfs.o
endif
+hsi_char-objs := hsi-char.o hsi-if.o
+
obj-$(CONFIG_OMAP_HSI) += omap_hsi.o
+obj-$(CONFIG_HSI_CHAR) += hsi_char.o
diff --git a/drivers/hsi/hsi-char.c b/drivers/hsi/hsi-char.c
new file mode 100644
index 0000000..29fbd73
--- /dev/null
+++ b/drivers/hsi/hsi-char.c
@@ -0,0 +1,526 @@
+/*
+ * hsi-char.c
+ *
+ * HSI character device driver, implements the character device
+ * 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/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/miscdevice.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/poll.h>
+#include <asm/mach-types.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+
+#include <mach/hsi.h>
+#include <linux/hsi_driver_if.h>
+#include <linux/hsi_char.h>
+
+#include "hsi-char.h"
+
+#define DRIVER_VERSION "0.1.0"
+
+static unsigned int port = 1;
+module_param(port, uint, 1);
+MODULE_PARM_DESC(port, "HSI port to be probed");
+
+static unsigned int channels_map[HSI_MAX_CHAR_DEVS] = {1};
+module_param_array(channels_map, uint, NULL, 0);
+MODULE_PARM_DESC(channels_map, "HSI channels to be probed");
+
+dev_t hsi_char_dev;
+
+struct char_queue {
+ struct list_head list;
+ u32 *data;
+ unsigned int count;
+};
+
+struct hsi_char {
+ 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_char hsi_char_data[HSI_MAX_CHAR_DEVS];
+
+void if_notify(int ch, struct hsi_event *ev)
+{
+ struct char_queue *entry;
+
+ pr_debug("%s, ev = {0x%x, 0x%p, %u}\n", __func__, ev->event, ev->data,
+ ev->count);
+
+ spin_lock(&hsi_char_data[ch].lock);
+
+ if (!hsi_char_data[ch].opened) {
+ pr_debug("%s, device not opened\n!", __func__);
+ spin_unlock(&hsi_char_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_char_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ list_add_tail(&entry->list, &hsi_char_data[ch].rx_queue);
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_IN\n", __func__);
+ wake_up_interruptible(&hsi_char_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_char_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ hsi_char_data[ch].poll_event |= (POLLOUT | POLLWRNORM);
+ list_add_tail(&entry->list, &hsi_char_data[ch].tx_queue);
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_OUT\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].tx_wait);
+ break;
+ case HSI_EV_EXCEP:
+ hsi_char_data[ch].poll_event |= POLLPRI;
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_EXCEP\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].poll_wait);
+ break;
+ case HSI_EV_AVAIL:
+ hsi_char_data[ch].poll_event |= (POLLIN | POLLRDNORM);
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_AVAIL\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].poll_wait);
+ break;
+ default:
+ spin_unlock(&hsi_char_data[ch].lock);
+ break;
+ }
+}
+
+
+static int hsi_char_fasync(int fd, struct file *file, int on)
+{
+ int ch = (int)file->private_data;
+ if (fasync_helper(fd, file, on, &hsi_char_data[ch].async_queue) >= 0)
+ return 0;
+ else
+ return -EIO;
+}
+
+
+static unsigned int hsi_char_poll(struct file *file, poll_table *wait)
+{
+ int ch = (int)file->private_data;
+ unsigned int ret = 0;
+
+ /*printk(KERN_DEBUG "%s\n", __func__);*/
+
+ poll_wait(file, &hsi_char_data[ch].poll_wait, wait);
+ poll_wait(file, &hsi_char_data[ch].tx_wait, wait);
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ ret = hsi_char_data[ch].poll_event;
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ pr_debug("%s, ret = 0x%x\n", __func__, ret);
+ return ret;
+}
+
+
+static ssize_t hsi_char_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ch = (int)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len;
+ struct char_queue *entry;
+ ssize_t ret;
+
+ /*printk(KERN_DEBUG "%s, count = %d\n", __func__, count);*/
+
+ /* only 32bit data is supported for now */
+ if ((count < 4) || (count & 3))
+ return -EINVAL;
+
+ data = kmalloc(count, GFP_ATOMIC);
+
+ ret = if_hsi_read(ch, data, count);
+ if (ret < 0) {
+ kfree(data);
+ goto out2;
+ }
+
+ add_wait_queue(&hsi_char_data[ch].rx_wait, &wait);
+
+ for ( ; ; ) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ if (!list_empty(&hsi_char_data[ch].rx_queue)) {
+ entry = list_entry(hsi_char_data[ch].rx_queue.next,
+ struct char_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_char_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\n", __func__);
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ hsi_char_data[ch].poll_event &= ~(POLLIN | POLLRDNORM);
+ if_hsi_poll(ch);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+ break;
+ } else if (file->f_flags & O_NONBLOCK) {
+ pr_debug("%s, O_NONBLOCK\n", __func__);
+ ret = -EAGAIN;
+ goto out;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ ret = -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__);*/
+ }
+
+ if (data_len) {
+ ret = copy_to_user((void __user *)buf, data, data_len);
+ if (!ret)
+ ret = data_len;
+ }
+
+ kfree(data);
+
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_char_data[ch].rx_wait, &wait);
+
+out2:
+ /*printk(KERN_DEBUG "%s, ret = %d\n", __func__, ret);*/
+ return ret;
+}
+
+static ssize_t hsi_char_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ch = (int)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len = 0;
+ struct char_queue *entry;
+ ssize_t ret;
+
+ /*printk(KERN_DEBUG "%s, count = %d\n", __func__, count);*/
+
+ /* only 32bit data is supported for now */
+ if ((count < 4) || (count & 3))
+ return -EINVAL;
+
+ data = kmalloc(count, GFP_ATOMIC);
+
+ if (copy_from_user(data, (void __user *)buf, count)) {
+ ret = -EFAULT;
+ kfree(data);
+ } else {
+ ret = count;
+ }
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ ret = if_hsi_write(ch, data, count);
+ if (ret < 0) {
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+ kfree(data);
+ goto out2;
+ }
+ hsi_char_data[ch].poll_event &= ~(POLLOUT | POLLWRNORM);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ add_wait_queue(&hsi_char_data[ch].tx_wait, &wait);
+
+ for ( ; ; ) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ if (!list_empty(&hsi_char_data[ch].tx_queue)) {
+ entry = list_entry(hsi_char_data[ch].tx_queue.next,
+ struct char_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ if (data_len) {
+ pr_debug("%s, TX finished\n", __func__);
+ ret = data_len;
+ break;
+ } else if (file->f_flags & O_NONBLOCK) {
+ pr_debug("%s, O_NONBLOCK\n", __func__);
+ ret = -EAGAIN;
+ goto out;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
+ /*printk(KERN_DEBUG "%s, going to sleep...\n", __func__);*/
+ schedule();
+ /*printk(KERN_DEBUG "%s, woke up\n", __func__);*/
+ }
+
+ kfree(data);
+
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_char_data[ch].tx_wait, &wait);
+
+out2:
+ /*printk(KERN_DEBUG "%s, ret = %d\n", __func__, ret);*/
+ return ret;
+}
+
+static int hsi_char_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ch = (int)file->private_data;
+ unsigned int state;
+ struct hsi_rx_config rx_cfg;
+ struct hsi_tx_config tx_cfg;
+ int ret = 0;
+
+ pr_debug("%s, ch = %d, cmd = 0x%08x\n", __func__, ch, cmd);
+
+ switch (cmd) {
+ case CS_SEND_BREAK:
+ if_hsi_send_break(ch);
+ break;
+ case CS_FLUSH_RX:
+ if_hsi_flush_rx(ch);
+ break;
+ case CS_FLUSH_TX:
+ if_hsi_flush_tx(ch);
+ break;
+ case CS_SET_WAKELINE:
+ if (copy_from_user(&state, (void __user *)arg,
+ sizeof(state)))
+ ret = -EFAULT;
+ else
+ if_hsi_set_wakeline(ch, state);
+ break;
+ case CS_GET_WAKELINE:
+ if_hsi_get_wakeline(ch, &state);
+ if (copy_to_user((void __user *)arg, &state, sizeof(state)))
+ ret = -EFAULT;
+ break;
+ case CS_SET_RX:
+ if (copy_from_user(&rx_cfg, (void __user *)arg,
+ sizeof(rx_cfg)))
+ ret = -EFAULT;
+ else
+ ret = if_hsi_set_rx(ch, &rx_cfg);
+ break;
+ case CS_GET_RX:
+ if_hsi_get_rx(ch, &rx_cfg);
+ if (copy_to_user((void __user *)arg, &rx_cfg, sizeof(rx_cfg)))
+ ret = -EFAULT;
+ break;
+ case CS_SET_TX:
+ if (copy_from_user(&tx_cfg, (void __user *)arg,
+ sizeof(tx_cfg)))
+ ret = -EFAULT;
+ else
+ ret = if_hsi_set_tx(ch, &tx_cfg);
+ break;
+ case CS_GET_TX:
+ if_hsi_get_tx(ch, &tx_cfg);
+ if (copy_to_user((void __user *)arg, &tx_cfg, sizeof(tx_cfg)))
+ ret = -EFAULT;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+
+ return ret;
+}
+
+static int hsi_char_open(struct inode *inode, struct file *file)
+{
+ int ret = 0, ch = iminor(inode);
+
+ pr_debug("%s, ch = %d, channels_map[%d] = %d\n", __func__, ch, ch,
+ channels_map[ch]);
+
+ if (!channels_map[ch])
+ return -ENODEV;
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+
+ if (hsi_char_data[ch].opened) {
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+ return -EBUSY;
+ }
+
+ file->private_data = (void *)ch;
+ hsi_char_data[ch].opened++;
+ hsi_char_data[ch].poll_event = (POLLOUT | POLLWRNORM);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ ret = if_hsi_start(ch);
+
+ return ret;
+}
+
+static int hsi_char_release(struct inode *inode, struct file *file)
+{
+ int ch = (int)file->private_data;
+ struct char_queue *entry;
+ struct list_head *cursor, *next;
+
+ pr_debug("%s, ch = %d\n", __func__, ch);
+
+ if_hsi_stop(ch);
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ hsi_char_data[ch].opened--;
+
+ if (!list_empty(&hsi_char_data[ch].rx_queue)) {
+ list_for_each_safe(cursor, next, &hsi_char_data[ch].rx_queue) {
+ entry = list_entry(cursor, struct char_queue, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+
+ if (!list_empty(&hsi_char_data[ch].tx_queue)) {
+ list_for_each_safe(cursor, next, &hsi_char_data[ch].tx_queue) {
+ entry = list_entry(cursor, struct char_queue, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ return 0;
+}
+
+static const struct file_operations hsi_char_fops = {
+ .owner = THIS_MODULE,
+ .read = hsi_char_read,
+ .write = hsi_char_write,
+ .poll = hsi_char_poll,
+ .ioctl = hsi_char_ioctl,
+ .open = hsi_char_open,
+ .release = hsi_char_release,
+ .fasync = hsi_char_fasync,
+};
+
+static struct cdev hsi_char_cdev;
+
+static int __init hsi_char_init(void)
+{
+ char devname[] = "hsi_char";
+ int ret, i;
+
+ pr_info("HSI character device version " DRIVER_VERSION "\n");
+
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+ init_waitqueue_head(&hsi_char_data[i].rx_wait);
+ init_waitqueue_head(&hsi_char_data[i].tx_wait);
+ init_waitqueue_head(&hsi_char_data[i].poll_wait);
+ spin_lock_init(&hsi_char_data[i].lock);
+ hsi_char_data[i].opened = 0;
+ INIT_LIST_HEAD(&hsi_char_data[i].rx_queue);
+ INIT_LIST_HEAD(&hsi_char_data[i].tx_queue);
+ }
+
+ /*printk(KERN_DEBUG "%s, devname = %s\n", __func__, devname);*/
+
+ ret = if_hsi_init(port, channels_map);
+ if (ret)
+ return ret;
+
+ ret = alloc_chrdev_region(&hsi_char_dev, 0, HSI_MAX_CHAR_DEVS, devname);
+ if (ret < 0) {
+ pr_err("HSI character driver: Failed to register\n");
+ return ret;
+ }
+
+ cdev_init(&hsi_char_cdev, &hsi_char_fops);
+ cdev_add(&hsi_char_cdev, hsi_char_dev, HSI_MAX_CHAR_DEVS);
+
+ return 0;
+}
+
+static void __exit hsi_char_exit(void)
+{
+ cdev_del(&hsi_char_cdev);
+ unregister_chrdev_region(hsi_char_dev, HSI_MAX_CHAR_DEVS);
+ if_hsi_exit();
+}
+
+MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>");
+MODULE_AUTHOR("Sebatien Jan <s-jan@ti.com> / Texas Instruments");
+MODULE_DESCRIPTION("HSI character device");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(hsi_char_init);
+module_exit(hsi_char_exit);
diff --git a/drivers/hsi/hsi-char.h b/drivers/hsi/hsi-char.h
new file mode 100644
index 0000000..d37018f
--- /dev/null
+++ b/drivers/hsi/hsi-char.h
@@ -0,0 +1,31 @@
+/*
+ * hsi-char.h
+ *
+ * HSI character driver private declaration header file.
+ *
+ * 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_CHAR_H
+#define _HSI_CHAR_H
+
+#include "hsi-if.h"
+
+/* how many char devices would be created at most */
+#define HSI_MAX_CHAR_DEVS 8
+
+void if_notify(int ch, struct hsi_event *ev);
+
+#endif /* _HSI_CHAR_H */
diff --git a/include/linux/hsi_char.h b/include/linux/hsi_char.h
new file mode 100644
index 0000000..de42c6c
--- /dev/null
+++ b/include/linux/hsi_char.h
@@ -0,0 +1,71 @@
+/*
+ * hsi_char.h
+ *
+ * HSI character driver public declaration header file.
+ *
+ * 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_CHAR_H
+#define HSI_CHAR_H
+
+#define HSI_CHAR_BASE 'S'
+#define CS_IOW(num, dtype) _IOW(HSI_CHAR_BASE, num, dtype)
+#define CS_IOR(num, dtype) _IOR(HSI_CHAR_BASE, num, dtype)
+#define CS_IOWR(num, dtype) _IOWR(HSI_CHAR_BASE, num, dtype)
+#define CS_IO(num) _IO(HSI_CHAR_BASE, num)
+
+#define CS_SEND_BREAK CS_IO(1)
+#define CS_FLUSH_RX CS_IO(2)
+#define CS_FLUSH_TX CS_IO(3)
+#define CS_BOOTSTRAP CS_IO(4)
+#define CS_SET_WAKELINE CS_IOW(5, unsigned int)
+#define CS_GET_WAKELINE CS_IOR(6, unsigned int)
+#define CS_SET_RX CS_IOW(7, struct hsi_rx_config)
+#define CS_GET_RX CS_IOW(8, struct hsi_rx_config)
+#define CS_SET_TX CS_IOW(9, struct hsi_tx_config)
+#define CS_GET_TX CS_IOW(10, struct hsi_tx_config)
+
+#define HSI_MODE_SLEEP 0
+#define HSI_MODE_STREAM 1
+#define HSI_MODE_FRAME 2
+
+#define HSI_FLOW_SYNCHRONIZED (0 << 2)
+#define HSI_FLOW_PIPELINED (1 << 2) /* only for HSI */
+
+#define HSI_ARBMODE_RR 0
+#define HSI_ARBMODE_PRIO 1
+
+#define WAKE_UP 0
+#define WAKE_DOWN 1
+
+struct hsi_tx_config {
+ u32 mode;
+ u32 flow;
+ u32 frame_size;
+ u32 channels;
+ u32 divisor;
+ u32 arb_mode;
+};
+
+struct hsi_rx_config {
+ u32 mode;
+ u32 flow;
+ u32 frame_size;
+ u32 channels;
+ u32 divisor; /* not used for SSI */
+};
+
+#endif /* HSI_CHAR_H */
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 7/9] HSI: character driver low level interface
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (5 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 6/9] HSI: character driver interface Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 8/9] HSI: HSI device support Sebastien Jan
` (2 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Andras Domokos
Character driver interface with omap_hsi driver (LLD)
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Andras Domokos <andras.domokos@nokia.com>
---
drivers/hsi/hsi-if.c | 642 ++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/hsi/hsi-if.h | 65 +++++
2 files changed, 707 insertions(+), 0 deletions(-)
create mode 100644 drivers/hsi/hsi-if.c
create mode 100644 drivers/hsi/hsi-if.h
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 */
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 8/9] HSI: HSI device support
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (6 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 7/9] HSI: character driver low level interface Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 9/9] HSI: SSI device support and integration on 3430SDP platform Sebastien Jan
2009-10-30 12:57 ` [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Felipe Balbi
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
Common registers definition for HSI like devices (common to MIPI HSI and SSI
for OMAP platforms)
HSI device definition
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
arch/arm/mach-omap2/Makefile | 1 +
arch/arm/mach-omap2/hsi.c | 497 +++++++++++++++++++++++++++++++++
arch/arm/plat-omap/include/mach/hsi.h | 432 ++++++++++++++++++++++++++++
3 files changed, 930 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-omap2/hsi.c
create mode 100644 arch/arm/plat-omap/include/mach/hsi.h
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 6b7702f..3de1c20 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -87,3 +87,4 @@ obj-y += $(onenand-m) $(onenand-y)
smc91x-$(CONFIG_SMC91X) := gpmc-smc91x.o
obj-y += $(smc91x-m) $(smc91x-y)
+obj-$(CONFIG_OMAP_HSI_DEVICE) += hsi.o
diff --git a/arch/arm/mach-omap2/hsi.c b/arch/arm/mach-omap2/hsi.c
new file mode 100644
index 0000000..a920934
--- /dev/null
+++ b/arch/arm/mach-omap2/hsi.c
@@ -0,0 +1,497 @@
+/*
+ * arch/arm/mach-omap2/hsi.c
+ *
+ * HSI device definition
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * 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/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/notifier.h>
+#include <mach/clock.h>
+#include <mach/hsi.h>
+#include <linux/hsi_driver_if.h>
+#include "clock.h"
+#include <asm/clkdev.h>
+#include <mach/mux.h>
+
+#define hsi_inl(p) inl((unsigned long) p)
+#define hsi_outl(v, p) outl(v, (unsigned long) p)
+
+/**
+ * struct hsi_internal_clk - Generic virtual hsi clock
+ * @clk: clock data
+ * @nb: notfier block for the DVFS notification chain
+ * @childs: Array of HSI FCK and ICK clocks
+ * @n_childs: Number of clocks in childs array
+ * @rate_change: Tracks if we are in the middle of a clock rate change
+ * @pdev: Reference to the HSI platform device associated to the clock
+ * @drv_nb: Reference to driver nb, use to propagate the DVFS notification
+ */
+struct hsi_internal_clk {
+ struct clk clk;
+ struct notifier_block nb;
+
+ struct clk **childs;
+ int n_childs;
+
+ unsigned int rate_change:1;
+
+ struct platform_device *pdev;
+ struct notifier_block *drv_nb;
+};
+
+static void hsi_set_mode(struct platform_device *pdev, u32 mode)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ int port;
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ /* FIXME - to update: need read/modify/write or something else:
+ * this register now also contains flow and wake ctrl
+ */
+ hsi_outl(mode, base + HSI_HST_MODE_REG(port));
+ hsi_outl(mode, base + HSI_HSR_MODE_REG(port));
+ }
+}
+
+static void hsi_save_mode(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ p->hst.mode = hsi_inl(base + HSI_HST_MODE_REG(port));
+ p->hsr.mode = hsi_inl(base + HSI_HSR_MODE_REG(port));
+ }
+}
+
+static void hsi_restore_mode(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ hsi_outl(p->hst.mode, base + HSI_HST_MODE_REG(port));
+ hsi_outl(p->hsr.mode, base + HSI_HSR_MODE_REG(port));
+ }
+}
+
+static int hsi_clk_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+/* FIXME
+ struct hsi_internal_clk *hsi_clk =
+ container_of(nb, struct hsi_internal_clk, nb);
+ switch (event) {
+ case CLK_PRE_RATE_CHANGE:
+ hsi_clk->drv_nb->notifier_call(hsi_clk->drv_nb, event, data);
+ hsi_clk->rate_change = 1;
+ if (hsi_clk->clk.usecount > 0) {
+ hsi_save_mode(hsi_clk->pdev);
+ hsi_set_mode(hsi_clk->pdev, HSI_MODE_SLEEP);
+ }
+ break;
+ case CLK_ABORT_RATE_CHANGE:
+ case CLK_POST_RATE_CHANGE:
+ if ((hsi_clk->clk.usecount > 0) && (hsi_clk->rate_change))
+ hsi_restore_mode(hsi_clk->pdev);
+
+ hsi_clk->rate_change = 0;
+ hsi_clk->drv_nb->notifier_call(hsi_clk->drv_nb, event, data);
+ break;
+ default:
+ break;
+ }
+*/
+ return NOTIFY_DONE;
+}
+
+static int hsi_clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+/* FIXME : clock functions not handled yet on OMAP4
+ struct hsi_internal_clk *hsi_clk;
+ if (!clk || !nb) {
+ return -EINVAL;
+ }
+ hsi_clk = container_of(clk, struct hsi_internal_clk, clk);
+ hsi_clk->drv_nb = nb;
+ hsi_clk->nb.priority = nb->priority;
+*/
+ /* NOTE: We only want notifications from the functional clock */
+/* FIXME
+ return clk_notifier_register(hsi_clk->childs[1], &hsi_clk->nb);
+*/
+ pr_debug("%s called\n", __func__);
+ return 0;
+}
+
+static int hsi_clk_notifier_unregister(struct clk *clk,
+ struct notifier_block *nb)
+{
+/* FIXME : clock functions not handled yet on OMAP4
+ struct hsi_internal_clk *hsi_clk;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ hsi_clk = container_of(clk, struct hsi_internal_clk, clk);
+ hsi_clk->drv_nb = NULL;
+
+ return clk_notifier_unregister(hsi_clk->childs[1], &hsi_clk->nb);
+*/
+ pr_debug(KERN_DEBUG "%s called", __func__);
+ return 0;
+}
+
+static void hsi_save_ctx(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+
+/* FIXME - implement PM support
+ pdata->ctx.loss_count =
+ omap_pm_get_dev_context_loss_count(&pdev->dev);
+*/
+ pdata->ctx.sysconfig = hsi_inl(base + HSI_SYS_SYSCONFIG_REG);
+ pdata->ctx.gdd_gcr = hsi_inl(base + HSI_GDD_GCR_REG);
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ p->sys_mpu_enable[0] = hsi_inl(base +
+ HSI_SYS_MPU_ENABLE_REG(port, 0));
+ p->sys_mpu_enable[1] = hsi_inl(base +
+ HSI_SYS_MPU_U_ENABLE_REG(port, 0));
+ p->hst.frame_size = hsi_inl(base +
+ HSI_HST_FRAMESIZE_REG(port));
+ p->hst.divisor = hsi_inl(base + HSI_HST_DIVISOR_REG(port));
+ p->hst.channels = hsi_inl(base + HSI_HST_CHANNELS_REG(port));
+ p->hst.arb_mode = hsi_inl(base + HSI_HST_ARBMODE_REG(port));
+ p->hsr.frame_size = hsi_inl(base +
+ HSI_HSR_FRAMESIZE_REG(port));
+/*FIXME - check this register*/
+ p->hsr.timeout = hsi_inl(base + HSI_HSR_COUNTERS_REG(port));
+ p->hsr.channels = hsi_inl(base + HSI_HSR_CHANNELS_REG(port));
+ }
+}
+
+static void hsi_restore_ctx(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+/* FIXME - implement PM support
+ int loss_count;
+
+ loss_count = omap_pm_get_dev_context_loss_count(&pdev->dev);
+
+ if (loss_count == pdata->ctx.loss_count)
+ return;
+*/
+ hsi_outl(pdata->ctx.sysconfig, base + HSI_SYS_SYSCONFIG_REG);
+ hsi_outl(pdata->ctx.gdd_gcr, base + HSI_GDD_GCR_REG);
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ hsi_outl(p->sys_mpu_enable[0], base +
+ HSI_SYS_MPU_ENABLE_REG(port, 0));
+ hsi_outl(p->sys_mpu_enable[1], base +
+ HSI_SYS_MPU_U_ENABLE_REG(port, 0));
+ hsi_outl(p->hst.frame_size, base +
+ HSI_HST_FRAMESIZE_REG(port));
+ hsi_outl(p->hst.divisor, base + HSI_HST_DIVISOR_REG(port));
+ hsi_outl(p->hst.channels, base + HSI_HST_CHANNELS_REG(port));
+ hsi_outl(p->hst.arb_mode, base + HSI_HST_ARBMODE_REG(port));
+ hsi_outl(p->hsr.frame_size, base +
+ HSI_HSR_FRAMESIZE_REG(port));
+/* FIXME - check this register */
+ hsi_outl(p->hsr.timeout, base + HSI_HSR_COUNTERS_REG(port));
+ hsi_outl(p->hsr.channels, base + HSI_HSR_CHANNELS_REG(port));
+ }
+}
+
+static void hsi_pdev_release(struct device *dev)
+{
+}
+
+/*
+ * NOTE: We abuse a little bit the struct port_ctx to use it also for
+ * initialization.
+ */
+static struct port_ctx hsi_port_ctx[] = {
+ [0] = {
+ .hst.mode = HSI_MODE_FRAME,
+ .hst.flow = HSI_FLOW_SYNCHRONIZED,
+ .hst.frame_size = HSI_FRAMESIZE_DEFAULT,
+ .hst.divisor = 1,
+ .hst.channels = HSI_CHANNELS_DEFAULT,
+ .hst.arb_mode = HSI_ARBMODE_ROUNDROBIN,
+ .hsr.mode = HSI_MODE_FRAME,
+ .hsr.flow = HSI_FLOW_SYNCHRONIZED,
+ .hsr.frame_size = HSI_FRAMESIZE_DEFAULT,
+ .hsr.channels = HSI_CHANNELS_DEFAULT,
+ .hsr.divisor = 0,
+ .hsr.timeout = HSI_COUNTERS_FT_DEFAULT |
+ HSI_COUNTERS_TB_DEFAULT |
+ HSI_COUNTERS_FB_DEFAULT,
+ },
+};
+
+static struct hsi_platform_data hsi_pdata = {
+ .num_ports = ARRAY_SIZE(hsi_port_ctx),
+ .ctx.pctx = hsi_port_ctx,
+ .clk_notifier_register = hsi_clk_notifier_register,
+ .clk_notifier_unregister = hsi_clk_notifier_unregister,
+};
+
+static struct resource hsi_resources[] = {
+ [0] = {
+ .start = 0x4A058000,
+ .end = 0x4A05b950,
+ .name = "omap_hsi_iomem",
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_44XX_HSI_1_IRQ0,
+ .end = INT_44XX_HSI_1_IRQ0,
+ .name = "hsi_p1_mpu_irq0",
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = INT_44XX_HSI_2_IRQ1,
+ .end = INT_44XX_HSI_2_IRQ1,
+ .name = "hsi_p2_mpu_irq0",
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = INT_44XX_HSI_1_DMAIRQ,
+ .end = INT_44XX_HSI_1_DMAIRQ,
+ .name = "hsi_gdd",
+ .flags = IORESOURCE_IRQ,
+ },
+ [4] = {
+ .start = 16, /* DMA channels available */
+ .end = 16,
+ .name = "hsi_gdd_chan_count",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct platform_device hsi_pdev = {
+ .name = "omap_hsi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(hsi_resources),
+ .resource = hsi_resources,
+ .dev = {
+ .release = hsi_pdev_release,
+ .platform_data = &hsi_pdata,
+ },
+};
+
+#ifdef CONFIG_OMAP_HSI_POWER_MANAGEMENT
+#define __HSI_CLK_FIX__
+#ifdef __HSI_CLK_FIX__
+/*
+ * FIXME: TO BE REMOVED.
+ * This hack allows us to ensure that clocks are stable before accehsing
+ * HSI controller registers. To be removed when PM functionalty is in place.
+ */
+static int check_hsi_active(void)
+{
+ u32 reg;
+ unsigned long dl = jiffies + msecs_to_jiffies(500);
+ void __iomem *cm_idlest1 = OMAP2_IO_ADDRESS(0x48004a20);
+
+ reg = inl(cm_idlest1);
+ while ((!(reg & 0x01)) && (time_before(jiffies, dl)))
+ reg = inl(cm_idlest1);
+
+ if (!(reg & 0x01)) { /* HST */
+ pr_err("HSI is still in STANDBY ! (BUG !?)\n");
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* __HSI_CLK_FIX__ */
+#endif
+
+static int hsi_clk_init(struct hsi_internal_clk *hsi_clk)
+{
+/* FIXME - update clock names on OMAP4*/
+ const char *clk_names[] = {};
+
+ int i;
+ int j;
+
+ hsi_clk->n_childs = ARRAY_SIZE(clk_names);
+ hsi_clk->childs = kzalloc(hsi_clk->n_childs * sizeof(*hsi_clk->childs),
+ GFP_KERNEL);
+ if (!hsi_clk->childs)
+ return -ENOMEM;
+
+ for (i = 0; i < hsi_clk->n_childs; i++) {
+ hsi_clk->childs[i] = clk_get(&hsi_clk->pdev->dev, clk_names[i]);
+ if (IS_ERR(hsi_clk->childs[i])) {
+ pr_err("Unable to get HSI clock: %s", clk_names[i]);
+ for (j = i - 1; j >= 0; j--)
+ clk_put(hsi_clk->childs[j]);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+
+
+static int hsi_clk_enable(struct clk *clk)
+{
+ struct hsi_internal_clk *hsi_clk =
+ container_of(clk, struct hsi_internal_clk, clk);
+/* FIXME - implement PM support
+ int err;
+ int i;
+
+ for (i = 0; i < hsi_clk->n_childs; i++) {
+ err = omap2_clk_enable(hsi_clk->childs[i]);
+ if (unlikely(err < 0))
+ goto rollback;
+ }
+*/
+#ifdef __HSI_CLK_FIX__
+ /*
+ * FIXME: To be removed
+ * Wait until the HSI controller has the clocks stable
+ */
+ check_hsi_active();
+#endif
+ hsi_restore_ctx(hsi_clk->pdev);
+ if (!hsi_clk->rate_change)
+ hsi_restore_mode(hsi_clk->pdev);
+
+ return 0;
+/* FIXME - implement PM support
+rollback:
+ pr_err("Error on HSI clk child %d\n", i);
+ for (i = i - 1; i >= 0; i--)
+ omap2_clk_disable(hsi_clk->childs[i]);
+ return err;
+*/
+}
+
+static void hsi_clk_disable(struct clk *clk)
+{
+ struct hsi_internal_clk *hsi_clk =
+ container_of(clk, struct hsi_internal_clk, clk);
+/* int i;*/
+
+ if (!hsi_clk->rate_change) {
+ hsi_save_mode(hsi_clk->pdev);
+ hsi_set_mode(hsi_clk->pdev, HSI_MODE_SLEEP);
+ }
+ /* Save ctx in all ports */
+ hsi_save_ctx(hsi_clk->pdev);
+
+/* FIXME - implement PM support
+ for (i = 0; i < hsi_clk->n_childs; i++)
+ omap2_clk_disable(hsi_clk->childs[i]);
+*/
+}
+
+static const int omap44xx_hsi_pins[] = {
+ AE18_4430_HSI1_CAWAKE,
+ AG19_4430_HSI1_CADATA,
+ AF19_4430_HSI1_CAFLAG,
+ AE19_4430_HSI1_ACREADY,
+ AF18_4430_HSI1_ACWAKE,
+ AG18_4430_HSI1_ACDATA,
+ AE17_4430_HSI1_ACFLAG,
+ AF17_4430_HSI1_CAREADY,
+};
+
+/* Mux settings for OMAP4430 */
+void omap_hsi_mux_setup(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(omap44xx_hsi_pins); i++)
+ omap_cfg_reg(omap44xx_hsi_pins[i]);
+ pr_debug("Pin muxing for HSI support done\n");
+}
+
+static const struct clkops clkops_hsi = {
+ .enable = hsi_clk_enable,
+ .disable = hsi_clk_disable,
+};
+
+static struct hsi_internal_clk hsi_clock = {
+ .clk = {
+ .name = "hsi_clk",
+ .id = -1,
+ .clkdm_name = "core_l4_clkdm",
+ .ops = &clkops_hsi,
+ },
+ .nb = {
+ .notifier_call = hsi_clk_event,
+ .priority = INT_MAX,
+ },
+ .pdev = &hsi_pdev,
+};
+
+/* FIXME - implement PM support
+static struct clk_lookup hsi_lk = {
+ .dev_id = NULL,
+ .con_id = "hsi_clk",
+ .clk = &hsi_clock.clk,
+};
+*/
+
+static int __init omap_hsi_init(void)
+{
+ int err;
+ struct clk *hsi_clk = &hsi_clock.clk;
+
+ hsi_clk_init(&hsi_clock);
+ clk_preinit(hsi_clk);
+/* FIXME - implement PM support
+ clkdev_add(&hsi_lk);
+*/
+ clk_register(hsi_clk);
+/* FIXME - implement PM support
+ omap2_init_clk_clkdm(hsi_clk);
+*/
+ err = platform_device_register(&hsi_pdev);
+ if (err < 0) {
+ pr_err("Unable to register HSI platform device: %d\n", err);
+ return err;
+ }
+
+ omap_hsi_mux_setup();
+
+ pr_info("HSI: device registered\n");
+ return 0;
+}
+subsys_initcall(omap_hsi_init);
diff --git a/arch/arm/plat-omap/include/mach/hsi.h b/arch/arm/plat-omap/include/mach/hsi.h
new file mode 100644
index 0000000..0b7f5ad
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/hsi.h
@@ -0,0 +1,432 @@
+/*
+ * /mach/hsi.h
+ *
+ * Hardware definitions for HSI and SSI.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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.
+ */
+
+/* NOTE: This file defines the registers address offsets for both the
+ * SSI and HSI devices. Most of the registers share the same offset between
+ * these devices.
+ * When common or HSI only, the constants are name HSI*. Else the SSI specific
+ * constants are name HSI_SSI*
+ */
+
+#ifndef __HSI_H__
+#define __HSI_H__
+
+#define HSI_PORT_OFFSET 0x1000
+
+/*
+ * GDD base addr : 0x48059000 (SSI)
+ * GDD base addr : 0x4A059000 (HSI)
+ */
+#define HSI_GDD_OFFSET 0x1000
+#define HSI_GDD_BASE HSI_GDD_OFFSET /* 0x9000 */
+
+/*
+ * HST base addr:
+ * port 1: 0x4805a000 (SSI) - 0x4A05a000 (HSI)
+ * port 2: 0x4805b000 (SSI) - 0x4a05b000 (HSI)
+ */
+#define HSI_HST_OFFSET 0x2000
+#define HSI_HST_BASE(port) (HSI_HST_OFFSET + ((port - 1) *\
+ (HSI_PORT_OFFSET)))
+/*
+ * HSR base addr:
+ * port 1: 0x4805a800 (SSI) - 0x4A05a800 (HSI)
+ * port 2: 0x4805b800 (SSI) - 0x4A05b800 (HSI)
+ */
+#define HSI_HSR_OFFSET 0x2800
+#define HSI_HSR_BASE(port) (HSI_HSR_OFFSET + ((port - 1) *\
+ (HSI_PORT_OFFSET)))
+/*
+ * HSI SYS registers
+ */
+#define HSI_SYS_REVISION_REG 0x0000
+#define HSI_SSI_REV_MASK 0x000000ff
+#define HSI_SSI_REV_MAJOR 0xf0
+#define HSI_SSI_REV_MINOR 0x0f
+
+#define HSI_SYS_SYSCONFIG_REG 0x0010
+#define HSI_AUTOIDLE (1 << 0)
+#define HSI_SOFTRESET (1 << 1)
+#define HSI_FREE_EMU (1 << 2) /* Only for HSI */
+#define HSI_SIDLEMODE_FORCE 0
+#define HSI_SIDLEMODE_NO (1 << 3)
+#define HSI_SIDLEMODE_SMART (1 << 4)
+#define HSI_SIDLEMODE_MASK 0x00000018
+#define HSI_MIDLEMODE_FORCE 0
+#define HSI_MIDLEMODE_NO (1 << 12)
+#define HSI_MIDLEMODE_SMART (1 << 13)
+#define HSI_MIDLEMODE_MASK 0x00003000
+
+#define HSI_SYS_SYSSTATUS_REG 0x0014
+#define HSI_RESETDONE 1
+
+#define HSI_SYS_MPU_STATUS_BASE 0x0808
+#define HSI_SYS_MPU_STATUS_PORT_OFFSET 0x10
+#define HSI_SYS_MPU_STATUS_IRQ_OFFSET 8
+
+#define HSI_SYS_MPU_STATUS_REG(port, irq) \
+ (HSI_SYS_MPU_STATUS_BASE + \
+ (((port - 1) * HSI_SYS_MPU_STATUS_PORT_OFFSET) +\
+ (irq * HSI_SYS_MPU_STATUS_IRQ_OFFSET)))
+
+#define HSI_SYS_MPU_ENABLE_BASE 0x080c
+#define HSI_SYS_MPU_ENABLE_PORT_OFFSET 0x10
+#define HSI_SYS_MPU_ENABLE_IRQ_OFFSET 8
+
+#define HSI_SYS_MPU_ENABLE_REG(port, irq) \
+ (HSI_SYS_MPU_ENABLE_BASE + \
+ (((port - 1) * HSI_SYS_MPU_ENABLE_PORT_OFFSET) +\
+ (irq * HSI_SYS_MPU_ENABLE_IRQ_OFFSET)))
+#define HSI_HST_DATAACCEPT(channel) ((channel < 8) ? \
+ (1 << channel) : (1 << (channel - 8)))
+#define HSI_HSR_DATAAVAILABLE(channel) (channel < 8 ? \
+ (1 << (channel + 8)) : (1 << (channel - 8 + 8)))
+#define HSI_HSR_DATAOVERRUN(channel) (channel < 8 ? \
+ (1 << (channel + 16)) : (1 << (channel - 8 + 16)))
+#define HSI_ERROROCCURED (1 << 24)
+#define HSI_BREAKDETECTED (1 << 25)
+
+#define HSI_SYS_GDD_MPU_IRQ_STATUS_REG 0x0800
+#define HSI_SYS_GDD_MPU_IRQ_ENABLE_REG 0x0804
+#define HSI_GDD_LCH(channel) (1 << channel)
+
+#define HSI_SYS_WAKE_OFFSET 0x10
+#define HSI_SYS_WAKE_BASE 0x0c00
+#define HSI_SYS_WAKE_REG(port) (HSI_SYS_WAKE_BASE +\
+ ((port - 1) * HSI_SYS_WAKE_OFFSET))
+#define HSI_SYS_CLEAR_WAKE_BASE 0x0c04
+#define HSI_SYS_CLEAR_WAKE_REG(port) (HSI_SYS_CLEAR_WAKE_BASE +\
+ ((port - 1) * HSI_SYS_WAKE_OFFSET))
+#define HSI_SYS_SET_WAKE_BASE 0x0c08
+#define HSI_SYS_SET_WAKE_REG(port) (HSI_SYS_SET_WAKE_BASE +\
+ ((port - 1) * HSI_SYS_WAKE_OFFSET))
+# define HSI_SSI_WAKE_MASK 0xff /* for SSI */
+# define HSI_WAKE_MASK 0xffff /* for HSI */
+# define HSI_WAKE_4_WIRES (0 << 16)
+# define HSI_WAKE_READY_LVL_0 (0 << 17)
+# define HSI_WAKE(channel) (1 << channel | HSI_WAKE_4_WIRES |\
+ HSI_WAKE_READY_LVL_0)
+
+#define HSI_SYS_HWINFO_REG 0x0004 /* only for HSI */
+
+/* Additional registers definitions (for channels 8 .. 15) for HSI */
+#define HSI_SYS_MPU_U_STATUS_BASE 0x0408
+#define HSI_SYS_MPU_U_STATUS_REG(port, irq) \
+ (HSI_SYS_MPU_U_STATUS_BASE + \
+ (((port - 1) * HSI_SYS_MPU_STATUS_PORT_OFFSET) +\
+ (irq * HSI_SYS_MPU_STATUS_IRQ_OFFSET)))
+
+#define HSI_SYS_MPU_U_ENABLE_BASE 0x040c
+#define HSI_SYS_MPU_U_ENABLE_REG(port, irq) \
+ (HSI_SYS_MPU_U_ENABLE_BASE + \
+ (((port - 1) * HSI_SYS_MPU_ENABLE_PORT_OFFSET) +\
+ (irq * HSI_SYS_MPU_ENABLE_IRQ_OFFSET)))
+
+/*
+ * HSI HST registers
+ */
+#define HSI_HST_ID_REG(port) (HSI_HST_BASE(port) + 0x0000)
+
+#define HSI_HST_MODE_REG(port) (HSI_HST_BASE(port) + 0x0004)
+#define HSI_MODE_VAL_MASK 3
+#define HSI_MODE_SLEEP 0
+#define HSI_MODE_STREAM 1
+#define HSI_MODE_FRAME 2
+#define HSI_SSI_MODE_MULTIPOINTS 3 /* SSI only */
+#define HSI_FLOW_VAL_MASK (3 << 2) /* HSI only */
+#define HSI_FLOW_SYNCHRONIZED (0 << 2) /* HSI only */
+#define HSI_FLOW_PIPELINED (1 << 2) /* HSI only */
+#define HSI_FLOW_REAL_TIME (2 << 2) /* HSI only */
+#define HSI_MODE_WAKE_CTRL_AUTO (1 << 4) /* HSI only */
+#define HSI_MODE_WAKE_CTRL_SW (0 << 4) /* HSI only */
+
+#define HSI_HST_FRAMESIZE_REG(port) (HSI_HST_BASE(port) + 0x0008)
+#define HSI_FRAMESIZE_DEFAULT 31
+#define HSI_FRAMESIZE_MAX 0x1f
+
+#define HSI_HST_TXSTATE_REG(port) (HSI_HST_BASE(port) + 0x000c)
+#define TXSTATE_IDLE 0
+
+#define HSI_HST_BUFSTATE_REG(port) (HSI_HST_BASE(port) + 0x0010)
+#define HSI_HST_BUFSTATE_FIFO_REG(fifo) ((fifo < 8) ? \
+ HSI_HST_BUFSTATE_REG(1) : \
+ HSI_HST_BUFSTATE_REG(2))
+#define HSI_BUFSTATE_CHANNEL(channel) (channel < 8 ? \
+ (1 << channel) : (1 << (channel - 8)))
+
+#define HSI_HST_DIVISOR_REG(port) (HSI_HST_BASE(port) + 0x0018)
+#define HSI_DIVISOR_DEFAULT 1
+#define HSI_SSI_MAX_TX_DIVISOR 0x7f /* for SSI */
+#define HSI_MAX_TX_DIVISOR 0xff /* for HSI */
+
+#define HSI_HST_BREAK_REG(port) (HSI_HST_BASE(port) + 0x0020)
+#define HSI_HST_CHANNELS_REG(port) (HSI_HST_BASE(port) + 0x0024)
+#define HSI_CHANNELS_DEFAULT 4
+#define HSI_SSI_CHANNELS_MAX 8 /* for SSI */
+#define HSI_CHANNELS_MAX 16 /* for HSI */
+
+#define HSI_HST_ARBMODE_REG(port) (HSI_HST_BASE(port) + 0x0028)
+#define HSI_ARBMODE_ROUNDROBIN 0
+#define HSI_ARBMODE_PRIORITY 1
+
+#define HSI_HST_BUFFER_BASE(port) (HSI_HST_BASE(port) + 0x0080)
+#define HSI_HST_BUFFER_CH_REG(port, channel) (HSI_HST_BUFFER_BASE(port) +\
+ (channel * 4))
+#define HSI_HST_BUFFER_FIFO_REG(fifo) ((fifo < 8) ? \
+ (HSI_HST_BUFFER_CH_REG(1, fifo)) : \
+ (HSI_HST_BUFFER_CH_REG(2, fifo - 8)))
+
+#define HSI_HST_SWAPBUF_BASE(port) (HSI_HST_BASE(port) + 0x00c0)
+#define HSI_HST_SWAPBUF_CH_REG(port, channel) (HSI_HST_SWAPBUF_BASE(port) +\
+ (channel * 4))
+
+/* Additional registers for HSI */
+#define HSI_HST_FIFO_COUNT 16
+#define HSI_HST_MAPPING_FIFO_REG(fifo) (HSI_HST_BASE(1) + 0x0100 +\
+ (fifo * 4))
+#define HSI_MAPPING_ENABLE 1
+#define HSI_MAPPING_CH_NUMBER_OFFSET 1
+#define HSI_MAPPING_PORT_NUMBER_OFFSET 7
+#define HSI_HST_MAPPING_THRESH_OFFSET 10
+#define HSI_HST_MAPPING_THRESH_VALUE (0x0 << HSI_HST_MAPPING_THRESH_OFFSET)
+
+/*
+ * HSI HSR registers
+ */
+#define HSI_HSR_ID_REG(port) (HSI_HSR_BASE(port) + 0x0000)
+
+#define HSI_HSR_MODE_REG(port) (HSI_HSR_BASE(port) + 0x0004)
+
+#define HSI_HSR_FRAMESIZE_REG(port) (HSI_HSR_BASE(port) + 0x0008)
+
+#define HSI_HSR_RXSTATE_REG(port) (HSI_HSR_BASE(port) + 0x000c)
+
+#define HSI_HSR_BUFSTATE_REG(port) (HSI_HSR_BASE(port) + 0x0010)
+#define HSI_HSR_BUFSTATE_FIFO_REG(fifo) ((fifo < 8) ? \
+ HSI_HSR_BUFSTATE_REG(1) : \
+ HSI_HSR_BUFSTATE_REG(2))
+
+#define HSI_HSR_BREAK_REG(port) (HSI_HSR_BASE(port) + 0x001c)
+
+#define HSI_HSR_ERROR_REG(port) (HSI_HSR_BASE(port) + 0x0020)
+#define HSI_HSR_ERROR_SIG 1
+#define HSI_HSR_ERROR_FTE (1 << 1) /* HSI only */
+#define HSI_HSR_ERROR_TBE (1 << 4) /* HSI only */
+#define HSI_HSR_ERROR_RME (1 << 7) /* HSI only */
+#define HSI_HSR_ERROR_TME (1 << 11) /* HSI only */
+
+#define HSI_HSR_ERRORACK_REG(port) (HSI_HSR_BASE(port) + 0x0024)
+
+#define HSI_HSR_CHANNELS_REG(port) (HSI_HSR_BASE(port) + 0x0028)
+
+#define HSI_HSR_OVERRUN_REG(port) (HSI_HSR_BASE(port) + 0x002c)
+
+#define HSI_HSR_OVERRUNACK_REG(port) (HSI_HSR_BASE(port) + 0x0030)
+
+#define HSI_HSR_COUNTERS_REG(port) (HSI_HSR_BASE(port) + 0x0034)
+#define HSI_TIMEOUT_DEFAULT 0 /* SSI only */
+#define HSI_SSI_RX_TIMEOUT_MAX 0x1ff /* SSI only */
+#define HSI_COUNTERS_FT_MASK 0x000fffff /* HSI only */
+#define HSI_COUNTERS_TB_MASK 0x00f00000 /* HSI only */
+#define HSI_COUNTERS_FB_MASK 0xff000000 /* HSI only */
+#define HSI_COUNTERS_FT_OFFSET 0 /* HSI only */
+#define HSI_COUNTERS_TB_OFFSET 20 /* HSI only */
+#define HSI_COUNTERS_FB_OFFSET 24 /* HSI only */
+/* Default FT value: 2 x max_bits_per_frame + 20% margin */
+#define HSI_COUNTERS_FT_DEFAULT (90 << HSI_COUNTERS_FT_OFFSET)
+#define HSI_COUNTERS_TB_DEFAULT (6 << HSI_COUNTERS_TB_OFFSET)
+#define HSI_COUNTERS_FB_DEFAULT (8 << HSI_COUNTERS_FB_OFFSET)
+
+#define HSI_HSR_BUFFER_BASE(port) (HSI_HSR_BASE(port) + 0x0080)
+#define HSI_HSR_BUFFER_CH_REG(port, channel) (HSI_HSR_BUFFER_BASE(port) +\
+ (channel * 4))
+#define HSI_HSR_BUFFER_FIFO_REG(fifo) ((fifo < 8) ? \
+ (HSI_HSR_BUFFER_CH_REG(1, fifo)) : \
+ (HSI_HSR_BUFFER_CH_REG(2, fifo - 8)))
+
+#define HSI_HSR_SWAPBUF_BASE(port) (HSI_HSR_BASE(port) + 0x00c0)
+#define HSI_HSR_SWAPBUF_CH_REG(port, channel) (HSI_HSR_SWAPBUF_BASE +\
+ (channel * 4))
+
+/* Additional registers for HSI */
+#define HSI_HSR_FIFO_COUNT 16
+#define HSI_HSR_MAPPING_FIFO_REG(fifo) (HSI_HSR_BASE(1) + 0x0100 +\
+ (fifo * 4))
+#define HSI_HSR_MAPPING_WORDS_MASK (0xf << 10)
+
+#define HSI_HSR_DLL_REG(port) (HSI_HSR_BASE(port) + 0x0144)
+#define HSI_HSR_DLL_COCHRE 1
+#define HSI_HSR_DLL_COCHGR (1 << 4)
+#define HSI_HSR_DLL_INCO_MASK 0x0003ff00
+#define HSI_HSR_DLL_INCO_OFFSET 8
+
+#define HSI_HSR_DIVISOR_REG(port) (HSI_HSR_BASE(port) + 0x014C)
+#define HSI_HSR_DIVISOR_MASK 0xff
+#define HSI_MAX_RX_DIVISOR 0xff
+
+
+/*
+ * HSI GDD registers
+ */
+#define HSI_SSI_GDD_HW_ID_REG (HSI_GDD_BASE + 0x0000)
+
+#define HSI_SSI_GDD_PPORT_ID_REG (HSI_GDD_BASE + 0x0010)
+
+#define HSI_SSI_GDD_MPORT_ID_REG (HSI_GDD_BASE + 0x0014)
+
+#define HSI_SSI_GDD_PPORT_SR_REG (HSI_GDD_BASE + 0x0020)
+#define HSI_PPORT_ACTIVE_LCH_NUMBER_MASK 0xff
+
+#define HSI_GDD_MPORT_SR_REG (HSI_GDD_BASE + 0x0024)
+#define HSI_SSI_MPORT_ACTIVE_LCH_NUMBER_MASK 0xff
+
+#define HSI_SSI_GDD_TEST_REG (HSI_GDD_BASE + 0x0040)
+#define HSI_SSI_TEST 1
+
+#define HSI_GDD_GCR_REG (HSI_GDD_BASE + 0x0100)
+#define HSI_CLK_AUTOGATING_ON (1 << 3)
+#define HSI_SWITCH_OFF (1 << 0)
+
+#define HSI_GDD_GRST_REG (HSI_GDD_BASE + 0x0200)
+#define HSI_SWRESET 1
+
+#define HSI_GDD_CSDP_BASE (HSI_GDD_BASE + 0x0800)
+#define HSI_GDD_CSDP_OFFSET 0x40
+#define HSI_GDD_CSDP_REG(channel) (HSI_GDD_CSDP_BASE +\
+ (channel * HSI_GDD_CSDP_OFFSET))
+#define HSI_DST_BURST_EN_MASK 0xc000
+#define HSI_DST_SINGLE_ACCESS0 0
+#define HSI_DST_SINGLE_ACCESS (1 << 14)
+#define HSI_DST_BURST_4X32_BIT (2 << 14)
+#define HSI_DST_BURST_8x32_BIT (3 << 14)
+
+#define HSI_DST_MASK 0x1e00
+#define HSI_DST_MEMORY_PORT (8 << 9)
+#define HSI_DST_PERIPHERAL_PORT (9 << 9)
+
+#define HSI_SRC_BURST_EN_MASK 0x0180
+#define HSI_SRC_SINGLE_ACCESS0 0
+#define HSI_SRC_SINGLE_ACCESS (1 << 7)
+#define HSI_SRC_BURST_4x32_BIT (2 << 7)
+#define HSI_SRC_BURST_8x32_BIT (3 << 7)
+
+#define HSI_SRC_MASK 0x003c
+#define HSI_SRC_MEMORY_PORT (8 << 2)
+#define HSI_SRC_PERIPHERAL_PORT (9 << 2)
+
+#define HSI_DATA_TYPE_MASK 3
+#define HSI_DATA_TYPE_S32 2
+
+#define HSI_GDD_CCR_BASE (HSI_GDD_BASE + 0x0802)
+#define HSI_GDD_CCR_OFFSET 0x40
+#define HSI_GDD_CCR_REG(channel) (HSI_GDD_CCR_BASE +\
+ (channel * HSI_GDD_CCR_OFFSET))
+#define HSI_DST_AMODE_MASK (3 << 14)
+#define HSI_DST_AMODE_CONST 0
+#define HSI_DST_AMODE_POSTINC (1 << 14)
+
+#define HSI_SRC_AMODE_MASK (3 << 12)
+#define HSI_SRC_AMODE_CONST 0
+#define HSI_SRC_AMODE_POSTINC (1 << 12)
+
+#define HSI_CCR_ENABLE (1 << 7)
+
+#define HSI_CCR_SYNC_MASK 0x001f /* only for SSI */
+
+#define HSI_GDD_CICR_BASE (HSI_GDD_BASE + 0x0804)
+#define HSI_GDD_CICR_OFFSET 0x40
+#define HSI_GDD_CICR_REG(channel) (HSI_GDD_CICR_BASE +\
+ (channel * HSI_GDD_CICR_OFFSET))
+#define HSI_BLOCK_IE (1 << 5)
+#define HSI_HALF_IE (1 << 2)
+#define HSI_TOUT_IE (1 << 0)
+
+#define HSI_GDD_CSR_BASE (HSI_GDD_BASE + 0x0806)
+#define HSI_GDD_CSR_OFFSET 0x40
+#define HSI_GDD_CSR_REG(channel) (HSI_GDD_CSR_BASE +\
+ (channel * HSI_GDD_CSR_OFFSET))
+#define HSI_CSR_SYNC (1 << 6)
+#define HSI_CSR_BLOCK (1 << 5)
+#define HSI_CSR_HALF (1 << 2)
+#define HSI_CSR_TOUT (1 << 0)
+
+#define HSI_GDD_CSSA_BASE (HSI_GDD_BASE + 0x0808)
+#define HSI_GDD_CSSA_OFFSET 0x40
+#define HSI_GDD_CSSA_REG(channel) (HSI_GDD_CSSA_BASE +\
+ (channel * HSI_GDD_CSSA_OFFSET))
+
+#define HSI_GDD_CDSA_BASE (HSI_GDD_BASE + 0x080c)
+#define HSI_GDD_CDSA_OFFSET 0x40
+#define HSI_GDD_CDSA_REG(channel) (HSI_GDD_CDSA_BASE +\
+ (channel * HSI_GDD_CDSA_OFFSET))
+
+#define HSI_GDD_CEN_BASE (HSI_GDD_BASE + 0x0810)
+#define HSI_GDD_CEN_OFFSET 0x40
+#define HSI_GDD_CEN_REG(channel) (HSI_GDD_CEN_BASE +\
+ (channel * HSI_GDD_CEN_OFFSET))
+
+#define HSI_GDD_CSAC_BASE (HSI_GDD_BASE + 0x0818)
+#define HSI_GDD_CSAC_OFFSET 0x40
+#define HSI_GDD_CSAC_REG(channel) (HSI_GDD_CSAC_BASE +\
+ (channel * HSI_GDD_CSAC_OFFSET))
+
+#define HSI_GDD_CDAC_BASE (HSI_GDD_BASE + 0x081a)
+#define HSI_GDD_CDAC_OFFSET 0x40
+#define HSI_GDD_CDAC_REG(channel) (HSI_GDD_CDAC_BASE +\
+ (channel * HSI_GDD_CDAC_OFFSET))
+
+#define HSI_SSI_GDD_CLNK_CTRL_BASE (HSI_GDD_BASE + 0x0828)
+#define HSI_SSI_GDD_CLNK_CTRL_OFFSET 0x40
+#define HSI_SSI_GDD_CLNK_CTRL_REG(channel) (HSI_SSI_GDD_CLNK_CTRL_BASE +\
+ (channel * HSI_SSI_GDD_CLNK_CTRL_OFFSET))
+
+#define HSI_SSI_ENABLE_LNK (1 << 15)
+#define HSI_SSI_STOP_LNK (1 << 14)
+#define HSI_SSI_NEXT_CH_ID_MASK 0xf
+
+/*
+ * HSI Helpers
+ */
+#define HSI_SYS_MPU_ENABLE_CH_REG(port, irq, channel) \
+ ((channel < HSI_SSI_CHANNELS_MAX) ? \
+ HSI_SYS_MPU_ENABLE_REG(port, irq) : \
+ HSI_SYS_MPU_U_ENABLE_REG(port, irq))
+
+/**
+ * struct omap_ssi_config - SSI board configuration
+ * @num_ports: Number of ports in use
+ * @cawake_line: Array of cawake gpio lines
+ */
+struct omap_ssi_board_config {
+ unsigned int num_ports;
+ int cawake_gpio[2];
+};
+extern int omap_ssi_config(struct omap_ssi_board_config *ssi_config);
+
+/**
+ * struct omap_hsi_config - HSI board configuration
+ * @num_ports: Number of ports in use
+ */
+struct omap_hsi_board_config {
+ unsigned int num_ports;
+};
+extern int omap_hsi_config(struct omap_hsi_board_config *hsi_config);
+
+#endif /* __HSI_H__ */
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC PATCH 9/9] HSI: SSI device support and integration on 3430SDP platform
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (7 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 8/9] HSI: HSI device support Sebastien Jan
@ 2009-10-30 10:00 ` Sebastien Jan
2009-10-30 12:57 ` [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Felipe Balbi
9 siblings, 0 replies; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 10:00 UTC (permalink / raw)
To: linux-omap; +Cc: Sebastien Jan, Carlos Chinea
SSI device definition
Integration on 3430 SDP (pin muxing, platform init)
Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
---
arch/arm/mach-omap2/Makefile | 1 +
arch/arm/mach-omap2/board-3430sdp.c | 12 +
arch/arm/mach-omap2/mux.c | 17 +
arch/arm/mach-omap2/ssi.c | 530 +++++++++++++++++++++++++++++++++
arch/arm/plat-omap/include/mach/mux.h | 10 +
5 files changed, 570 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-omap2/ssi.c
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 3de1c20..34d3e7f 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -87,4 +87,5 @@ obj-y += $(onenand-m) $(onenand-y)
smc91x-$(CONFIG_SMC91X) := gpmc-smc91x.o
obj-y += $(smc91x-m) $(smc91x-y)
+obj-$(CONFIG_OMAP_SSI_DEVICE) += ssi.o
obj-$(CONFIG_OMAP_HSI_DEVICE) += hsi.o
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index 2dfab17..f982fd1 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -40,6 +40,7 @@
#include <mach/control.h>
#include <mach/keypad.h>
#include <mach/gpmc-smc91x.h>
+#include <mach/hsi.h>
#include "sdram-qimonda-hyb18m512160af-6.h"
#include "mmc-twl4030.h"
@@ -496,6 +497,13 @@ static struct ehci_hcd_omap_platform_data ehci_pdata __initconst = {
.reset_gpio_port[2] = -EINVAL
};
+#ifdef CONFIG_OMAP_SSI_DEVICE
+static struct omap_ssi_board_config ssi_board_config = {
+ .num_ports = 1,
+ .cawake_gpio = { 151 },
+};
+#endif
+
static void __init omap_3430sdp_init(void)
{
omap3430_i2c_init();
@@ -513,6 +521,10 @@ static void __init omap_3430sdp_init(void)
board_smc91x_init();
enable_board_wakeup_source();
usb_ehci_init(&ehci_pdata);
+
+#ifdef CONFIG_OMAP_SSI_DEVICE
+ omap_ssi_config(&ssi_board_config);
+#endif
}
static void __init omap_3430sdp_map_io(void)
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c
index b5fac32..8464034 100644
--- a/arch/arm/mach-omap2/mux.c
+++ b/arch/arm/mach-omap2/mux.c
@@ -451,6 +451,23 @@ MUX_CFG_34XX("AD2_3430_USB3FS_PHY_MM3_TXDAT", 0x188,
MUX_CFG_34XX("AC1_3430_USB3FS_PHY_MM3_TXEN_N", 0x18a,
OMAP34XX_MUX_MODE6 | OMAP34XX_PIN_OUTPUT)
+/* SSI support */
+MUX_CFG_34XX("AA8_3430_SSI1_DAT_TX", 0x17c,
+ OMAP34XX_MUX_MODE1) /* AC Data */
+MUX_CFG_34XX("AA9_3430_SSI1_FLAG_TX", 0x17e,
+ OMAP34XX_MUX_MODE1) /* AC Flag */
+MUX_CFG_34XX("W8_3430_SSI1_RDY_TX", 0x180,
+ OMAP34XX_MUX_MODE1 | OMAP3_INPUT_EN) /* CA Ready */
+MUX_CFG_34XX("Y8_3430_GPIO_151", 0x182,
+ OMAP34XX_MUX_MODE4 | OMAP3_INPUT_EN) /* CA Wake */
+MUX_CFG_34XX("AE1_3430_SSI1_DAT_RX", 0x184,
+ OMAP34XX_MUX_MODE1 | OMAP3_INPUT_EN) /* CA Data */
+MUX_CFG_34XX("AD1_3430_SSI1_FLAG_RX", 0x186,
+ OMAP34XX_MUX_MODE1 | OMAP3_INPUT_EN) /* CA Flag */
+MUX_CFG_34XX("AD2_3430_SSI1_RDY_RX", 0x188,
+ OMAP34XX_MUX_MODE1) /* AC Ready */
+MUX_CFG_34XX("AC1_3430_SSI1_WAKE", 0x18a,
+ OMAP34XX_MUX_MODE1) /* AC Wake */
/* 34XX GPIO - bidirectional, unless the name has an "_OUT" suffix.
* (Always specify PIN_INPUT, except for names suffixed by "_OUT".)
diff --git a/arch/arm/mach-omap2/ssi.c b/arch/arm/mach-omap2/ssi.c
new file mode 100644
index 0000000..676254a
--- /dev/null
+++ b/arch/arm/mach-omap2/ssi.c
@@ -0,0 +1,530 @@
+/*
+ * arch/arm/mach-omap2/ssi.c
+ *
+ * SSI device definition
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@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/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/notifier.h>
+#include <mach/clock.h>
+#include <mach/hsi.h>
+#include <linux/hsi_driver_if.h>
+#include "clock.h"
+#include <asm/clkdev.h>
+#include <mach/mux.h>
+
+#define hsi_inl(p) inl((unsigned long) p)
+#define hsi_outl(v, p) outl(v, (unsigned long) p)
+
+/**
+ * struct ssi_internal_clk - Generic virtual ssi clock
+ * @clk: clock data
+ * @nb: notfier block for the DVFS notification chain
+ * @childs: Array of SSI FCK and ICK clocks
+ * @n_childs: Number of clocks in childs array
+ * @rate_change: Tracks if we are in the middle of a clock rate change
+ * @pdev: Reference to the SSI platform device associated to the clock
+ * @drv_nb: Reference to driver nb, use to propagate the DVFS notification
+ */
+struct ssi_internal_clk {
+ struct clk clk;
+ struct notifier_block nb;
+
+ struct clk **childs;
+ int n_childs;
+
+ unsigned int rate_change:1;
+
+ struct platform_device *pdev;
+ struct notifier_block *drv_nb;
+};
+
+static void ssi_set_mode(struct platform_device *pdev, u32 mode)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ int port;
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ outl(mode, (unsigned int) base + HSI_HST_MODE_REG(port));
+ outl(mode, (unsigned int) base + HSI_HSR_MODE_REG(port));
+ }
+}
+
+static void ssi_save_mode(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ p->hst.mode = hsi_inl(base + HSI_HST_MODE_REG(port));
+ p->hsr.mode = hsi_inl(base + HSI_HSR_MODE_REG(port));
+ }
+}
+
+static void ssi_restore_mode(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ hsi_outl(p->hst.mode, base + HSI_HST_MODE_REG(port));
+ hsi_outl(p->hsr.mode, base + HSI_HSR_MODE_REG(port));
+ }
+}
+
+static int ssi_clk_event(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+/* TODO: implement clock change support
+ struct ssi_internal_clk *ssi_clk =
+ container_of(nb, struct ssi_internal_clk, nb);
+ switch (event) {
+ case CLK_PRE_RATE_CHANGE:
+ ssi_clk->drv_nb->notifier_call(ssi_clk->drv_nb, event, data);
+ ssi_clk->rate_change = 1;
+ if (ssi_clk->clk.usecount > 0) {
+ ssi_save_mode(ssi_clk->pdev);
+ ssi_set_mode(ssi_clk->pdev, HSI_MODE_SLEEP);
+ }
+ break;
+ case CLK_ABORT_RATE_CHANGE:
+ case CLK_POST_RATE_CHANGE:
+ if ((ssi_clk->clk.usecount > 0) && (ssi_clk->rate_change))
+ ssi_restore_mode(ssi_clk->pdev);
+
+ ssi_clk->rate_change = 0;
+ ssi_clk->drv_nb->notifier_call(ssi_clk->drv_nb, event, data);
+ break;
+ default:
+ break;
+ }
+*/
+
+ return NOTIFY_DONE;
+}
+
+static int ssi_clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+ struct ssi_internal_clk *ssi_clk;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ ssi_clk = container_of(clk, struct ssi_internal_clk, clk);
+ ssi_clk->drv_nb = nb;
+ ssi_clk->nb.priority = nb->priority;
+ /* NOTE: We only want notifications from the functional clock */
+/* TODO: implement clock change support
+ return clk_notifier_register(ssi_clk->childs[1], &ssi_clk->nb);
+*/
+ pr_debug("%s called\n", __func__);
+ return 0;
+}
+
+static int ssi_clk_notifier_unregister(struct clk *clk,
+ struct notifier_block *nb)
+{
+ struct ssi_internal_clk *ssi_clk;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ ssi_clk = container_of(clk, struct ssi_internal_clk, clk);
+ ssi_clk->drv_nb = NULL;
+/* TODO: implement clock change support
+ return clk_notifier_unregister(ssi_clk->childs[1], &ssi_clk->nb);
+*/
+ pr_debug(KERN_DEBUG "%s called", __func__);
+ return 0;
+}
+
+static void ssi_save_ctx(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+
+/* TODO: update support for omap_pm_get_dev_context_loss_count
+ pdata->ctx.loss_count =
+ omap_pm_get_dev_context_loss_count(&pdev->dev);
+*/
+ pdata->ctx.sysconfig = hsi_inl(base + HSI_SYS_SYSCONFIG_REG);
+ pdata->ctx.gdd_gcr = hsi_inl(base + HSI_GDD_GCR_REG);
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ p->sys_mpu_enable[0] = hsi_inl(base +
+ HSI_SYS_MPU_ENABLE_REG(port, 0));
+ p->sys_mpu_enable[1] = hsi_inl(base +
+ HSI_SYS_MPU_ENABLE_REG(port, 1));
+ p->hst.frame_size = hsi_inl(base +
+ HSI_HST_FRAMESIZE_REG(port));
+ p->hst.divisor = hsi_inl(base + HSI_HST_DIVISOR_REG(port));
+ p->hst.channels = hsi_inl(base + HSI_HST_CHANNELS_REG(port));
+ p->hst.arb_mode = hsi_inl(base + HSI_HST_ARBMODE_REG(port));
+ p->hsr.frame_size = hsi_inl(base +
+ HSI_HSR_FRAMESIZE_REG(port));
+ p->hsr.timeout = hsi_inl(base + HSI_HSR_COUNTERS_REG(port));
+ p->hsr.channels = hsi_inl(base + HSI_HSR_CHANNELS_REG(port));
+ }
+}
+
+static void ssi_restore_ctx(struct platform_device *pdev)
+{
+ struct hsi_platform_data *pdata = pdev->dev.platform_data;
+ void __iomem *base = OMAP2_IO_ADDRESS(pdev->resource[0].start);
+ struct port_ctx *p;
+ int port;
+/* TODO: update support for omap_pm_get_dev_context_loss_count
+ int loss_count;
+
+ loss_count = omap_pm_get_dev_context_loss_count(&pdev->dev);
+ if (loss_count == pdata->ctx.loss_count)
+ return;
+*/
+ hsi_outl(pdata->ctx.sysconfig, base + HSI_SYS_SYSCONFIG_REG);
+ hsi_outl(pdata->ctx.gdd_gcr, base + HSI_GDD_GCR_REG);
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx.pctx[port - 1];
+ hsi_outl(p->sys_mpu_enable[0], base +
+ HSI_SYS_MPU_ENABLE_REG(port, 0));
+ hsi_outl(p->sys_mpu_enable[1], base +
+ HSI_SYS_MPU_ENABLE_REG(port, 1));
+ hsi_outl(p->hst.frame_size, base +
+ HSI_HST_FRAMESIZE_REG(port));
+ hsi_outl(p->hst.divisor, base + HSI_HST_DIVISOR_REG(port));
+ hsi_outl(p->hst.channels, base + HSI_HST_CHANNELS_REG(port));
+ hsi_outl(p->hst.arb_mode, base + HSI_HST_ARBMODE_REG(port));
+ hsi_outl(p->hsr.frame_size, base +
+ HSI_HSR_FRAMESIZE_REG(port));
+ hsi_outl(p->hsr.timeout, base + HSI_HSR_COUNTERS_REG(port));
+ hsi_outl(p->hsr.channels, base + HSI_HSR_CHANNELS_REG(port));
+ }
+}
+
+static void ssi_pdev_release(struct device *dev)
+{
+}
+
+/*
+ * NOTE: We abuse a little bit the struct port_ctx to use it also for
+ * initialization.
+ */
+static struct port_ctx ssi_port_ctx[] = {
+ [0] = {
+ .hst.mode = HSI_MODE_FRAME,
+ .hst.flow = 0, /* not supported on SSI */
+ .hst.frame_size = HSI_FRAMESIZE_DEFAULT,
+ .hst.divisor = 1,
+ .hst.channels = HSI_CHANNELS_DEFAULT,
+ .hst.arb_mode = HSI_ARBMODE_ROUNDROBIN,
+ .hsr.mode = HSI_MODE_FRAME,
+ .hsr.flow = 0, /* not supported on SSI */
+ .hsr.frame_size = HSI_FRAMESIZE_DEFAULT,
+ .hsr.channels = HSI_CHANNELS_DEFAULT,
+ .hsr.divisor = 0, /* not supported on SSI */
+ .hsr.timeout = HSI_TIMEOUT_DEFAULT,
+ },
+};
+
+static struct hsi_platform_data ssi_pdata = {
+ .num_ports = ARRAY_SIZE(ssi_port_ctx),
+ .ctx.pctx = ssi_port_ctx,
+ .clk_notifier_register = ssi_clk_notifier_register,
+ .clk_notifier_unregister = ssi_clk_notifier_unregister,
+};
+
+static struct resource ssi_resources[] = {
+ [0] = {
+ .start = 0x48058000,
+ .end = 0x4805bbff,
+ .name = "omap_ssi_iomem",
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 67,
+ .end = 67,
+ .name = "ssi_p1_mpu_irq0",
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = 69,
+ .end = 69,
+ .name = "ssi_p1_mpu_irq1",
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = 68,
+ .end = 68,
+ .name = "ssi_p2_mpu_irq0",
+ .flags = IORESOURCE_IRQ,
+ },
+ [4] = {
+ .start = 70,
+ .end = 70,
+ .name = "ssi_p2_mpu_irq1",
+ .flags = IORESOURCE_IRQ,
+ },
+ [5] = {
+ .start = 71,
+ .end = 71,
+ .name = "ssi_gdd",
+ .flags = IORESOURCE_IRQ,
+ },
+ [6] = {
+ .start = 151,
+ .end = 0,
+ .name = "ssi_p1_cawake_gpio",
+ .flags = IORESOURCE_IRQ | IORESOURCE_UNSET,
+ },
+ [7] = {
+ .start = 0,
+ .end = 0,
+ .name = "ssi_p2_cawake_gpio",
+ .flags = IORESOURCE_IRQ | IORESOURCE_UNSET,
+ },
+ [8] = {
+ .start = 8, /* DMA channels available */
+ .end = 8,
+ .name = "hsi_gdd_chan_count",
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static struct platform_device ssi_pdev = {
+ .name = "omap_ssi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ssi_resources),
+ .resource = ssi_resources,
+ .dev = {
+ .release = ssi_pdev_release,
+ .platform_data = &ssi_pdata,
+ },
+};
+
+#if 0 /* TODO: check if following code can be removed when testing with PM */
+#define __HSI_CLK_FIX__
+#ifdef __HSI_CLK_FIX__
+/*
+ * FIXME: TO BE REMOVED.
+ * This hack allows us to ensure that clocks are stable before accessing
+ * SSI controller registers. To be removed when PM functionalty is in place.
+ */
+static int check_ssi_active(void)
+{
+ u32 reg;
+ unsigned long dl = jiffies + msecs_to_jiffies(500);
+ void __iomem *cm_idlest1 = OMAP2_IO_ADDRESS(0x48004a20);
+
+ reg = inl(cm_idlest1);
+ while ((!(reg & 0x01)) && (time_before(jiffies, dl)))
+ reg = inl(cm_idlest1);
+
+ if (!(reg & 0x01)) { /* ST_SSI */
+ pr_err("SSI is still in STANDBY ! (BUG !?)\n");
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* __HSI_CLK_FIX__ */
+#endif
+
+static int ssi_clk_init(struct ssi_internal_clk *ssi_clk)
+{
+ const char *clk_names[] =
+ { "ssi_ssr_fck", "ssi_sst_fck", "ssi_l4_ick", "ssi_ick" };
+ int i;
+ int j;
+
+ ssi_clk->n_childs = ARRAY_SIZE(clk_names);
+ ssi_clk->childs = kzalloc(ssi_clk->n_childs * sizeof(*ssi_clk->childs),
+ GFP_KERNEL);
+ if (!ssi_clk->childs)
+ return -ENOMEM;
+
+ for (i = 0; i < ssi_clk->n_childs; i++) {
+ ssi_clk->childs[i] = clk_get(&ssi_clk->pdev->dev, clk_names[i]);
+ if (IS_ERR(ssi_clk->childs[i])) {
+ pr_err("Unable to get SSI clock: %s\n", clk_names[i]);
+ for (j = i - 1; j >= 0; j--)
+ clk_put(ssi_clk->childs[j]);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static int ssi_clk_enable(struct clk *clk)
+{
+ struct ssi_internal_clk *ssi_clk =
+ container_of(clk, struct ssi_internal_clk, clk);
+ int err;
+ int i;
+
+ for (i = 0; i < ssi_clk->n_childs; i++) {
+ err = omap2_clk_enable(ssi_clk->childs[i]);
+ if (unlikely(err < 0))
+ goto rollback;
+ }
+#ifdef __HSI_CLK_FIX__
+ /*
+ * FIXME: To be removed
+ * Wait until the SSI controller has the clocks stable
+ */
+ check_ssi_active();
+#endif
+ ssi_restore_ctx(ssi_clk->pdev);
+ if (!ssi_clk->rate_change)
+ ssi_restore_mode(ssi_clk->pdev);
+
+ return 0;
+rollback:
+ pr_err("Error on SSI clk child %d\n", i);
+ for (i = i - 1; i >= 0; i--)
+ omap2_clk_disable(ssi_clk->childs[i]);
+
+ return err;
+}
+
+static void ssi_clk_disable(struct clk *clk)
+{
+ struct ssi_internal_clk *ssi_clk =
+ container_of(clk, struct ssi_internal_clk, clk);
+ int i;
+
+ if (!ssi_clk->rate_change) {
+ ssi_save_mode(ssi_clk->pdev);
+ ssi_set_mode(ssi_clk->pdev, HSI_MODE_SLEEP);
+ }
+ /* Save ctx in all ports */
+ ssi_save_ctx(ssi_clk->pdev);
+
+ for (i = 0; i < ssi_clk->n_childs; i++)
+ omap2_clk_disable(ssi_clk->childs[i]);
+}
+
+int omap_ssi_config(struct omap_ssi_board_config *ssi_config)
+{
+ int port;
+ int cawake_gpio;
+
+ ssi_pdata.num_ports = ssi_config->num_ports;
+ for (port = 0; port < ssi_config->num_ports; port++) {
+ cawake_gpio = ssi_config->cawake_gpio[port];
+ if (cawake_gpio < 0)
+ continue; /* Nothing to do */
+
+ if (gpio_request(cawake_gpio, "CAWAKE") < 0) {
+ dev_err(&ssi_pdev.dev, "FAILED to request CAWAKE"
+ " GPIO %d\n", cawake_gpio);
+ return -EBUSY;
+ }
+
+ gpio_direction_input(cawake_gpio);
+ ssi_resources[6 + port].start = gpio_to_irq(cawake_gpio);
+ ssi_resources[6 + port].flags &= ~IORESOURCE_UNSET;
+ ssi_resources[6 + port].flags |= IORESOURCE_IRQ_HIGHEDGE |
+ IORESOURCE_IRQ_LOWEDGE;
+ }
+
+ pr_debug("GPIO for SSI is now configured\n");
+ return 0;
+}
+
+static const int omap34xx_ssi_pins[] = {
+ AA8_3430_SSI1_DAT_TX,
+ AA9_3430_SSI1_FLAG_TX,
+ W8_3430_SSI1_RDY_TX,
+ Y8_3430_GPIO_151,
+ AE1_3430_SSI1_DAT_RX,
+ AD1_3430_SSI1_FLAG_RX,
+ AD2_3430_SSI1_RDY_RX,
+ AC1_3430_SSI1_WAKE,
+};
+
+/* Mux settings for OMAP3430 */
+void omap_ssi_mux_setup(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(omap34xx_ssi_pins); i++)
+ omap_cfg_reg(omap34xx_ssi_pins[i]);
+ pr_debug("Pin muxing for SSI support (disables UART1 and McBsp4)\n");
+}
+
+static const struct clkops clkops_ssi = {
+ .enable = ssi_clk_enable,
+ .disable = ssi_clk_disable,
+};
+
+static struct ssi_internal_clk ssi_clock = {
+ .clk = {
+ .name = "hsi_clk",
+ .id = -1,
+ .clkdm_name = "core_l4_clkdm",
+ .ops = &clkops_ssi,
+ },
+ .nb = {
+ .notifier_call = ssi_clk_event,
+ .priority = INT_MAX,
+ },
+ .pdev = &ssi_pdev,
+};
+
+static struct clk_lookup hsi_lk = {
+ .dev_id = NULL,
+ .con_id = "hsi_clk",
+ .clk = &ssi_clock.clk,
+};
+
+static int __init omap_ssi_init(void)
+{
+ int err;
+ struct clk *hsi_clk = &ssi_clock.clk;
+
+ ssi_clk_init(&ssi_clock);
+ clk_preinit(hsi_clk);
+ clkdev_add(&hsi_lk);
+ clk_register(hsi_clk);
+ omap2_init_clk_clkdm(hsi_clk);
+
+ err = platform_device_register(&ssi_pdev);
+ if (err < 0) {
+ pr_err("Unable to register SSI platform device: %d\n", err);
+ return err;
+ }
+
+ omap_ssi_mux_setup();
+
+ pr_info("SSI: device registered\n");
+
+ return 0;
+}
+subsys_initcall(omap_ssi_init);
diff --git a/arch/arm/plat-omap/include/mach/mux.h b/arch/arm/plat-omap/include/mach/mux.h
index f3c1d8a..4d2d9e4 100644
--- a/arch/arm/plat-omap/include/mach/mux.h
+++ b/arch/arm/plat-omap/include/mach/mux.h
@@ -782,6 +782,16 @@ enum omap34xx_index {
AD2_3430_USB3FS_PHY_MM3_TXDAT,
AC1_3430_USB3FS_PHY_MM3_TXEN_N,
+ /* SSI support */
+ AA8_3430_SSI1_DAT_TX,
+ AA9_3430_SSI1_FLAG_TX,
+ W8_3430_SSI1_RDY_TX,
+ Y8_3430_GPIO_151,
+ AE1_3430_SSI1_DAT_RX,
+ AD1_3430_SSI1_FLAG_RX,
+ AD2_3430_SSI1_RDY_RX,
+ AC1_3430_SSI1_WAKE,
+
/* 34xx GPIO
* - normally these are bidirectional, no internal pullup/pulldown
* - "_UP" suffix (GPIO3_UP) if internal pullup is configured
--
1.6.0.4
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [RFC PATCH 1/9] HSI: Low Level Driver interface
2009-10-30 10:00 ` [RFC PATCH 1/9] HSI: Low Level Driver interface Sebastien Jan
@ 2009-10-30 12:49 ` Felipe Balbi
0 siblings, 0 replies; 15+ messages in thread
From: Felipe Balbi @ 2009-10-30 12:49 UTC (permalink / raw)
To: ext Sebastien Jan
Cc: linux-omap@vger.kernel.org, Chinea Carlos (Nokia-D/Helsinki)
Hi,
On Fri, Oct 30, 2009 at 11:00:00AM +0100, ext Sebastien Jan wrote:
> 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/
not omap specific.
> 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
this shouldn't be omap specific. The comment is misleading.
> 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
this should a more generic bus driver and it shouldn't know about how
the hardware handles e.g. reset signals.
What I suggest you to do is:
Bus Layer
hsi.ko -> MIPI HSI Bus Driver (like i2c-core.c or usbcore, etc etc)
HW Layer
hsi-omap.ko-> OMAP HSI Controller (like i2c-omap.c)
Clients Layer
any protocol driver using the HSI bus
> 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@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 "hsi_driver.h"
should be including something like:
#include <linux/hsi/driver.h>
or even
#include <linux/hsi.h>
> +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);
are these used on some file_operations structure ?
they should be implemented by the client driver if when it needs instead
of letting the bus driver dictacte so much of the design.
> +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));
I wouldn't do it like this. If this is what I'm guessing, you're using
these functions to be sure when a transfer has completed like ?
I remember suggesting this to be done more like the usb gadget
framework:
struct hsi_request {
void *buf;
dma_addr_t dma;
unsigned length;
unsigned direction;
void (*complete)(struct hsi_device *hsi, struct
hsi_channel *channel, struct
hsi_request *req);
};
then, you send the data and when you get a complete irq, you call
request->complete();
maybe that would be better ??
--
balbi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC PATCH 2/9] HSI: Low Level Driver core
2009-10-30 10:00 ` [RFC PATCH 2/9] HSI: Low Level Driver core Sebastien Jan
@ 2009-10-30 12:56 ` Felipe Balbi
0 siblings, 0 replies; 15+ messages in thread
From: Felipe Balbi @ 2009-10-30 12:56 UTC (permalink / raw)
To: ext Sebastien Jan
Cc: linux-omap@vger.kernel.org, Chinea Carlos (Nokia-D/Helsinki)
Hi,
On Fri, Oct 30, 2009 at 11:00:01AM +0100, ext Sebastien Jan wrote:
> +static int __init reg_hsi_dev_ch(struct hsi_dev *hsi_ctrl, unsigned int p,
> + unsigned int ch)
> +{
> + struct hsi_device *dev;
> + struct hsi_port *port = &hsi_ctrl->hsi_port[p];
> + int err;
> +
> + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> +
> + dev->n_ctrl = hsi_ctrl->id;
> + dev->n_p = p;
> + dev->n_ch = ch;
> + dev->ch = &port->hsi_channel[ch];
> + dev->device.bus = &hsi_bus_type;
> + dev->device.parent = hsi_ctrl->dev;
> + dev->device.release = hsi_dev_release;
> + if (dev->n_ctrl < 0)
> + dev_set_name(&dev->device, "omap_hsi-p%u.c%u", p, ch);
> + else
> + dev_set_name(&dev->device, "omap_hsi%d-p%u.c%u", dev->n_ctrl, p,
> + ch);
> +
> + err = device_register(&dev->device);
> + if (err >= 0) {
> + write_lock_bh(&port->hsi_channel[ch].rw_lock);
> + port->hsi_channel[ch].dev = dev;
> + write_unlock_bh(&port->hsi_channel[ch].rw_lock);
> + } else {
> + kfree(dev);
> + }
> + return err;
> +}
this will make a client's probe() function to be called twice, no ??
you should/could have a hsi_new_dummy() like on i2c ??
> +static int __init hsi_request_mpu_irq(struct hsi_port *hsi_p)
this should be done by hsi-omap.ko driver.
> +static int __init hsi_request_cawake_irq(struct hsi_port *hsi_p)
ditto.
> +static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl)
ditto.
> +static int __init hsi_request_gdd_irq(struct hsi_dev *hsi_ctrl)
ditto.
> +static int __init hsi_controller_init(struct hsi_dev *hsi_ctrl,
> + struct platform_device *pd)
ditto.
--
balbi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
` (8 preceding siblings ...)
2009-10-30 10:00 ` [RFC PATCH 9/9] HSI: SSI device support and integration on 3430SDP platform Sebastien Jan
@ 2009-10-30 12:57 ` Felipe Balbi
2009-10-30 14:32 ` Sebastien Jan
9 siblings, 1 reply; 15+ messages in thread
From: Felipe Balbi @ 2009-10-30 12:57 UTC (permalink / raw)
To: ext Sebastien Jan; +Cc: linux-omap@vger.kernel.org
On Fri, Oct 30, 2009 at 10:59:59AM +0100, ext Sebastien Jan wrote:
> This is an RFC for a series of patch to add the support of MIPI HSI and SSI
> devices to OMAP platforms.
> The patch includes the HSI device driver and the device files for MIPI HSI and
> SSI.
>
> The driver is made of 2 distinct modules:
> - omap_hsi: is the Low-Level Driver (or hardware driver). It provides a
> kernel functional interface, to be used by other kernel modules and
> abstracts the HW.
> This part of the patch is based on Nokia SSI driver, already submitted as
> RFC by Carlos Chinea [1].
> - hsi-char: is a char device driver proposing a user-space interface for
> using the HSI and relies on omap_hsi.
> This part of the patch is based on a development from Andras Domokos.
> The intent is to propose a single HSI driver that can support MIPI HSI 1.1 as
> well as previous implementations (SSI) for OMAP platforms (OMAP3 / OMAP4).
>
> The driver core functionalities are there and ready for review. The following
> updates will come in the following weeks / months:
> - Clocks and Power Management support for OMAP4 platform (HSI device file)
> - HSI-char interface documentation
>
> Validation: The driver is validated on 3430 SDP (with a HW loopback on the SSI
> device), and is being validated on 4430 Virtio platform (MIPI HSI device).
>
> [1] http://lwn.net/Articles/301918/
pass all your drivers on checkpatch.pl --strict and to sparse as well.
--
balbi
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms
2009-10-30 12:57 ` [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Felipe Balbi
@ 2009-10-30 14:32 ` Sebastien Jan
2009-10-30 17:58 ` Felipe Balbi
0 siblings, 1 reply; 15+ messages in thread
From: Sebastien Jan @ 2009-10-30 14:32 UTC (permalink / raw)
To: felipe.balbi@nokia.com; +Cc: linux-omap@vger.kernel.org
On Friday 30 October 2009 13:57:50 Felipe Balbi wrote:
> On Fri, Oct 30, 2009 at 10:59:59AM +0100, ext Sebastien Jan wrote:
> > This is an RFC for a series of patch to add the support of MIPI HSI and
> > SSI devices to OMAP platforms.
> > The patch includes the HSI device driver and the device files for MIPI
> > HSI and SSI.
> >
> > The driver is made of 2 distinct modules:
> > - omap_hsi: is the Low-Level Driver (or hardware driver). It provides a
> > kernel functional interface, to be used by other kernel modules and
> > abstracts the HW.
> > This part of the patch is based on Nokia SSI driver, already submitted
> > as RFC by Carlos Chinea [1].
> > - hsi-char: is a char device driver proposing a user-space interface for
> > using the HSI and relies on omap_hsi.
> > This part of the patch is based on a development from Andras Domokos.
> > The intent is to propose a single HSI driver that can support MIPI HSI
> > 1.1 as well as previous implementations (SSI) for OMAP platforms (OMAP3 /
> > OMAP4).
> >
> > The driver core functionalities are there and ready for review. The
> > following updates will come in the following weeks / months:
> > - Clocks and Power Management support for OMAP4 platform (HSI device
> > file) - HSI-char interface documentation
> >
> > Validation: The driver is validated on 3430 SDP (with a HW loopback on
> > the SSI device), and is being validated on 4430 Virtio platform (MIPI HSI
> > device).
> >
> > [1] http://lwn.net/Articles/301918/
>
> pass all your drivers on checkpatch.pl --strict and to sparse as well.
Thanks to point this out: I forgot to mention that I did run checkpatch.pl --
strict and sparse on these patches.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms
2009-10-30 14:32 ` Sebastien Jan
@ 2009-10-30 17:58 ` Felipe Balbi
0 siblings, 0 replies; 15+ messages in thread
From: Felipe Balbi @ 2009-10-30 17:58 UTC (permalink / raw)
To: ext Sebastien Jan
Cc: Balbi Felipe (Nokia-D/Helsinki), linux-omap@vger.kernel.org
On Fri, Oct 30, 2009 at 03:32:06PM +0100, ext Sebastien Jan wrote:
> On Friday 30 October 2009 13:57:50 Felipe Balbi wrote:
> > On Fri, Oct 30, 2009 at 10:59:59AM +0100, ext Sebastien Jan wrote:
> > > This is an RFC for a series of patch to add the support of MIPI HSI and
> > > SSI devices to OMAP platforms.
> > > The patch includes the HSI device driver and the device files for MIPI
> > > HSI and SSI.
> > >
> > > The driver is made of 2 distinct modules:
> > > - omap_hsi: is the Low-Level Driver (or hardware driver). It provides a
> > > kernel functional interface, to be used by other kernel modules and
> > > abstracts the HW.
> > > This part of the patch is based on Nokia SSI driver, already submitted
> > > as RFC by Carlos Chinea [1].
> > > - hsi-char: is a char device driver proposing a user-space interface for
> > > using the HSI and relies on omap_hsi.
> > > This part of the patch is based on a development from Andras Domokos.
> > > The intent is to propose a single HSI driver that can support MIPI HSI
> > > 1.1 as well as previous implementations (SSI) for OMAP platforms (OMAP3 /
> > > OMAP4).
> > >
> > > The driver core functionalities are there and ready for review. The
> > > following updates will come in the following weeks / months:
> > > - Clocks and Power Management support for OMAP4 platform (HSI device
> > > file) - HSI-char interface documentation
> > >
> > > Validation: The driver is validated on 3430 SDP (with a HW loopback on
> > > the SSI device), and is being validated on 4430 Virtio platform (MIPI HSI
> > > device).
> > >
> > > [1] http://lwn.net/Articles/301918/
> >
> > pass all your drivers on checkpatch.pl --strict and to sparse as well.
>
> Thanks to point this out: I forgot to mention that I did run checkpatch.pl --
> strict and sparse on these patches.
Weird they passed. Just looking over the patches I saw some weird
comments here and there.
--
balbi
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2009-10-30 17:58 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-30 9:59 [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 1/9] HSI: Low Level Driver interface Sebastien Jan
2009-10-30 12:49 ` Felipe Balbi
2009-10-30 10:00 ` [RFC PATCH 2/9] HSI: Low Level Driver core Sebastien Jan
2009-10-30 12:56 ` Felipe Balbi
2009-10-30 10:00 ` [RFC PATCH 3/9] HSI: Low Level Driver device management Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 4/9] HSI: Low Level Driver debugfs support Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 5/9] HSI: Low Level Driver documentation Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 6/9] HSI: character driver interface Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 7/9] HSI: character driver low level interface Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 8/9] HSI: HSI device support Sebastien Jan
2009-10-30 10:00 ` [RFC PATCH 9/9] HSI: SSI device support and integration on 3430SDP platform Sebastien Jan
2009-10-30 12:57 ` [RFC PATCH 0/9] MIPI HSI device support for OMAP platforms Felipe Balbi
2009-10-30 14:32 ` Sebastien Jan
2009-10-30 17:58 ` Felipe Balbi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox