blob: 862a12d5970266b8fe73f7d940ce22f4b25c5632 [file] [log] [blame]
/*
* 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. Range [1, 2]
*
* 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;
int fifo_port; /* Range [1, 2] */
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) ||
(hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT2)) {
fifo_port = (hsi_ctrl->fifo_mapping_strategy ==
HSI_FIFO_MAPPING_ALL_PORT1) ? 1 : 2;
if (unlikely(port != fifo_port)) {
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_ALL_PORT2) {
*channel = fifo;
*port = 2;
} 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 error code (< 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) ||
(mtype == HSI_FIFO_MAPPING_ALL_PORT2)) {
port = (mtype == HSI_FIFO_MAPPING_ALL_PORT1) ? 0 : 1;
channel = 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 (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_UNDEF)
dev_dbg(hsi_ctrl->dev, "Fifo mapping : All FIFOs for "
"Port %d\n", port + 1);
} 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");
} else {
dev_err(hsi_ctrl->dev, "Bad Fifo strategy request : %d\n",
mtype);
err = -EINVAL;
}
hsi_ctrl->fifo_mapping_strategy = mtype;
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;
if (unlikely(fifo < 0)) {
dev_err(hsi_ctrl->dev, "Invalid FIFO id %d.\n", fifo);
return 0;
}
hsr_mapping = hsi_inl(base, HSI_HSR_MAPPING_FIFO_REG(fifo));
mapping_words = (hsr_mapping >> HSI_HST_MAPPING_THRESH_OFFSET) & 0xF;
return mapping_words;
}