aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/omap_hsi/hsi_driver_fifo.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/omap_hsi/hsi_driver_fifo.c')
-rw-r--r--drivers/omap_hsi/hsi_driver_fifo.c325
1 files changed, 325 insertions, 0 deletions
diff --git a/drivers/omap_hsi/hsi_driver_fifo.c b/drivers/omap_hsi/hsi_driver_fifo.c
new file mode 100644
index 0000000..aa33a1a
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_fifo.c
@@ -0,0 +1,325 @@
+/*
+ * 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 "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)) {
+ fifo_index = err;
+ dev_err(hsi_ctrl->dev, "Cannot map a FIFO to the requested "
+ "params: channel:%d, port:%d; ERR=%d\n", channel, port,
+ 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 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++;
+ }
+ if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_UNDEF)
+ dev_dbg(hsi_ctrl->dev, "Fifo mapping : All FIFOs for "
+ "Port1\n");
+ 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;
+ }
+ }
+
+ if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_UNDEF)
+ dev_dbg(hsi_ctrl->dev, "Fifo mapping : 8 FIFOs per Port"
+ " (SSI compatible mode)\n");
+ hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_SSI;
+ } else {
+ hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_UNDEF;
+ dev_err(hsi_ctrl->dev, "Bad Fifo strategy request : %d\n",
+ mtype);
+ 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 (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hst_bufstate_f_reg ERROR : %d\n", fifo);
+ 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 (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hsr_bufstate_f_reg ERROR : %d\n", fifo);
+ 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)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hst_bufstate_f_reg ERROR : %d\n", fifo);
+ 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 (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hsr_bufstate_f_reg ERROR : %d\n", fifo);
+ return fifo;
+ } else
+ return HSI_HSR_BUFFER_FIFO_REG(fifo);
+ } else {
+ return HSI_HSR_BUFFER_CH_REG(port, channel);
+ }
+}
+
+/**
+ * hsi_get_rx_fifo_occupancy - Return the size of data remaining
+ * in the given FIFO
+ * @hsi_ctrl - HSI controler data
+ * @fifo - FIFO to look at
+ *
+ * Returns the number of frames (32bits) remaining in the FIFO
+ */
+u8 hsi_get_rx_fifo_occupancy(struct hsi_dev *hsi_ctrl, u8 fifo)
+{
+ void __iomem *base = hsi_ctrl->base;
+ int hsr_mapping, mapping_words;
+
+ hsr_mapping = hsi_inl(base, HSI_HSR_MAPPING_FIFO_REG(fifo));
+ mapping_words = (hsr_mapping >> HSI_HST_MAPPING_THRESH_OFFSET) & 0xF;
+ return mapping_words;
+}
+