blob: ffcb9673bb3b9a2f70af90f3e89f51bd2ef879e3 [file] [log] [blame]
/*
* Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DEBUG 1
#define VERBOSE_DEBUG 1
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mipi-bif.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mipi-bif-tegra.h>
#include <linux/pm_runtime.h>
#include <linux/clk/tegra.h>
#define TEGRA_MIPIBIF_TIMEOUT 1000
#define MIPIBIF_CTRL 0x0
#define MIPIBIF_CTRL_GO (1<<31)
#define MIPIBIF_CTRL_COMMAND_TYPE_SHIFT 20
#define MIPIBIF_CTRL_COMMAND_NO_READ (0x0 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_READ_DATA (0x1 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_INTERRUPT_DATA (0x2 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_BUS_QUERY (0x3 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_STBY (0x4 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_PWDN (0x5 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_HARD_RESET (0x6 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_ACTIVATE (0x7 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_COMMAND_EXIT_INT (0x8 << MIPIBIF_CTRL_COMMAND_TYPE_SHIFT)
#define MIPIBIF_CTRL_PACKETCOUNT_SHIFT 8
#define MIPIBIF_CTRL_PACKETCOUNT_MASK (1FF << MIPIBIF_CTRL_PACKETCOUNT_SHIFT)
#define MIPIBIF_TIMING_PVT 0x4
#define MIPIBIF_TIMING_TBIF_0_SHIFT 16
#define MIPIBIF_TIMING_TBIF_1_SHIFT 12
#define MIPIBIF_TIMING_TBIF_STOP_SHIFT 8
#define MIPIBIF_TIMING0 0x8
#define MIPIBIF_TIMING0_TRESP_MIN_SHIFT 28
#define MIPIBIF_TIMING0_TRESP_MAX_SHIFT 20
#define MIPIBIF_TIMING0_TBIF_SHIFT 0
#define MIPIBIF_TIMING1 0xc
#define MIPIBIF_TIMING1_INT_TO_SHIFT 16
#define MIPIBIF_TIMING1_TINTARM_SHIFT 8
#define MIPIBIF_TIMING1_TINTTRA_SHIFT 4
#define MIPIBIF_TIMING1_TINTACT_SHIFT 0
#define MIPIBIF_TIMING2 0x10
#define MIPIBIF_TIMING2_TPDL_SHIFT 15
#define MIPIBIF_TIMING2_TPUL_SHIFT 0
#define MIPIBIF_TIMING3 0x14
#define MIPIBIF_TIMING3_TACT_SHIFT 20
#define MIPIBIF_TIMING3_TPUP_SHIFT 0
#define MIPIBIF_TIMING4 0x18
#define MIPIBIF_TIMING4_TCLWS_SHIFT 0
#define MIPIBIF_STATUS 0x2c
#define MIPIBIF_NUM_PACKETS_TRANSMITTED_SHIFT 16
#define MIPIBIF_NUM_PACKETS_TRANSMITTED_MASK (0x1FF << MIPIBIF_NUM_PACKETS_TRANSMITTED_SHIFT)
#define MIPIBIF_NUM_PACKETS_RCVD_SHIFT 4
#define MIPIBIF_NUM_PACKETS_RCVD_MASK (0x1FF << MIPIBIF_NUM_PACKETS_RCVD_SHIFT)
#define MIPIBIF_CTRL_BUSY (1<<2)
#define MIPIBIF_INTERRUPT_RECV_STATUS (1<<1)
#define MIPIBIF_BQ_RECV_STATUS (1<<0)
#define MIPIBIF_INTERRUPT_EN 0x30
#define MIPIBIF_INTERRUPT_RXF_UNR_OVR_INT_EN (1<<10)
#define MIPIBIF_INTERRUPT_TXF_UNR_OVR_INT_EN (1<<9)
#define MIPIBIF_INTERRUPT_NO_RESPONSE_ERR_INT_EN (1<<8)
#define MIPIBIF_INTERRUPT_RXF_DATA_REQ_INT_EN (1<<7)
#define MIPIBIF_INTERRUPT_TXF_DATA_REQ_INT_EN (1<<6)
#define MIPIBIF_INTERRUPT_XFER_DONE_INT_EN (1<<5)
#define MIPIBIF_INTERRUPT_INV_ERR_INT_EN (1<<4)
#define MIPIBIF_INTERRUPT_PKT_RECV_ERR_INT_EN (1<<3)
#define MIPIBIF_INTERRUPT_LOW_PHASE_IN_WORD_ERR_INT_EN (1<<2)
#define MIPIBIF_INTERRUPT_INCOMPLETE_PKT_RECV_ERR_INT_EN (1<<1)
#define MIPIBIF_INTERRUPT_PARITY_ERR_INT_EN (1<<0)
#define MIPIBIF_BCL_ERROR_INTERRUPTS_EN (MIPIBIF_INTERRUPT_LOW_PHASE_IN_WORD_ERR_INT_EN | MIPIBIF_INTERRUPT_INCOMPLETE_PKT_RECV_ERR_INT_EN | MIPIBIF_INTERRUPT_NO_RESPONSE_ERR_INT_EN)
#define MIPIBIF_FIFO_ERROR_INTERRUPTS_EN (MIPIBIF_INTERRUPT_RXF_UNR_OVR_INT_EN | MIPIBIF_INTERRUPT_TXF_UNR_OVR_INT_EN)
#define MIPIBIF_DEFAULT_INTMASK (MIPIBIF_BCL_ERROR_INTERRUPTS_EN | MIPIBIF_FIFO_ERROR_INTERRUPTS_EN | MIPIBIF_INTERRUPT_XFER_DONE_INT_EN)
#define MIPIBIF_INTERRUPT_STATUS 0x34
#define MIPIBIF_INTERRUPT_NO_RESPONSE_ERR (1<<8)
#define MIPIBIF_INTERRUPT_RXF_DATA_REQ (1<<7)
#define MIPIBIF_INTERRUPT_TXF_DATA_REQ (1<<6)
#define MIPIBIF_INTERRUPT_XFER_DONE (1<<5)
#define MIPIBIF_INTERRUPT_INV_ERR (1<<4)
#define MIPIBIF_INTERRUPT_PKT_RECV_ERR (1<<3)
#define MIPIBIF_INTERRUPT_LOW_PHASE_IN_WORD_ERR (1<<2)
#define MIPIBIF_INTERRUPT_INCOMPLETE_PKT_RECV_ERR (1<<1)
#define MIPIBIF_INTERRUPT_PARITY_ERR (1<<0)
#define MIPIBIF_TX_FIFO 0x38
#define MIPIBIF_TX_FIFO_MASK 0x3FF
#define MIPIBIF_RX_FIFO 0x3c
#define MIPIBIF_RX_FIFO_MASK 0x3FFFF
#define MIPIBIF_FIFO_CONTROL 0x40
#define MIPIBIF_RXFIFO_FLUSH (1<<9)
#define MIPIBIF_TXFIFO_FLUSH (1<<8)
#define MIPIBIF_RXFIFO_ATN_LVL_SHIFT 4
#define MIPIBIF_TXFIFO_ATN_LVL_SHIFT 0
#define MIPIBIF_FIFO_STATUS 0x44
#define MIPIBIF_RX_FIFO_FULL_COUNT_SHIFT 24
#define MIPIBIF_RX_FIFO_FULL_COUNT_MASK (0xFF<<MIPIBIF_RX_FIFO_FULL_COUNT_SHIFT)
#define MIPIBIF_TX_FIFO_EMPTY_COUNT_SHIFT 16
#define MIPIBIF_TX_FIFO_EMPTY_COUNT_MASK (0xFF << MIPIBIF_TX_FIFO_EMPTY_COUNT_SHIFT)
#define MIPIBIF_TX_FIFO_OVF (1<<7)
#define MIPIBIF_TX_FIFO_UNR (1<<6)
#define MIPIBIF_RX_FIFO_OVF (1<<5)
#define MIPIBIF_RX_FIFO_UNR (1<<4)
#define MIPIBIF_TX_FIFO_FULL (1<<3)
#define MIPIBIF_TX_FIFO_EMPTY (1<<2)
#define MIPIBIF_RX_FIFO_FULL (1<<1)
#define MIPIBIF_RX_FIFO_EMPTY (1<<0)
#define MIPIBIF_ERR_NONE 0
#define MIPIBIF_ERR_NO_RESPONSE 0x1
#define MIPIBIF_ERR_INV 0x2
#define MIPIBIF_ERR_PKT_RECV 0x4
#define MIPIBIF_ERR_LOW_PHASE_IN_WORD 0x8
#define MIPIBIF_ERR_INCOMPLETE_PKT_RECV 0x10
#define MIPIBIF_ERR_PARITY 0x20
#define MIPIBIF_ERR_RECV_DATA_TYPE (MIPIBIF_ERR_PARITY | MIPIBIF_ERR_PKT_RECV | MIPIBIF_ERR_INV)
struct tegra_mipi_bif_dev {
struct device *dev;
struct clk *mipi_bif_clk;
struct rt_mutex dev_lock;
spinlock_t fifo_lock;
void __iomem *base;
int cont_id;
int irq;
int tauBIF;
struct completion msg_complete;
int msg_err;
u8 *msg_buf;
u16 msg_device_addr;
u16 msg_reg_addr;
u16 msg_commands;
u16 msg_len;
size_t msg_buf_remaining;
int current_command_count;
struct mipi_bif_adapter adapter;
unsigned long bus_clk_rate;
bool is_suspended;
};
static void tegra_mipi_bif_send(struct tegra_mipi_bif_dev *mipi_bif_dev,
u32 cmd)
{
writel(cmd, mipi_bif_dev->base + MIPIBIF_TX_FIFO);
mipi_bif_dev->current_command_count++;
}
static void tegra_mipi_bif_mask_irq(struct tegra_mipi_bif_dev *mipi_bif_dev,
u32 mask)
{
u32 int_mask = readl(mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
int_mask &= ~mask;
writel(int_mask, mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
}
static void tegra_mipi_bif_unmask_irq(struct tegra_mipi_bif_dev *mipi_bif_dev,
u32 mask)
{
u32 int_mask = readl(mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
int_mask |= mask;
writel(int_mask, mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
}
static int
tegra_mipi_bif_send_DIP0_command(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPI_BIF_BUS_COMMAND_DIP0,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_COMMAND_BUS_QUERY,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
if (readl(mipi_bif_dev->base + MIPIBIF_STATUS) & MIPIBIF_BQ_RECV_STATUS)
return 1;
return 0;
}
static int
tegra_mipi_bif_send_DIP1_command(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPI_BIF_BUS_COMMAND_DIP1,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_COMMAND_BUS_QUERY,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
if (readl(mipi_bif_dev->base + MIPIBIF_STATUS) & MIPIBIF_BQ_RECV_STATUS)
return 1;
return 0;
}
static int
tegra_mipi_bif_send_DIE0_command(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPI_BIF_BUS_COMMAND_DIE0,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO, mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int
tegra_mipi_bif_send_DIE1_command(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPI_BIF_BUS_COMMAND_DIE1,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO, mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int
tegra_mipi_bif_send_DISS_command(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPI_BIF_BUS_COMMAND_DISS,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO, mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int tegra_mipi_bif_HardReset(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_COMMAND_HARD_RESET,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int tegra_mipi_bif_StandBy(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPI_BIF_BUS_COMMAND_STBY,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_COMMAND_STBY,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int tegra_mipi_bif_Activate(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_ACTIVATE,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int tegra_mipi_bif_PowerDown(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPI_BIF_BUS_COMMAND_PWDN,
mipi_bif_dev->base + MIPIBIF_TX_FIFO);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_COMMAND_PWDN,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int tegra_mipi_bif_Exit_Interrupt(
struct tegra_mipi_bif_dev *mipi_bif_dev)
{
int ret;
init_completion(&mipi_bif_dev->msg_complete);
writel(MIPIBIF_DEFAULT_INTMASK,
mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
writel(MIPIBIF_CTRL_GO | MIPIBIF_CTRL_COMMAND_EXIT_INT,
mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:transfer timed out", __func__);
return -ETIMEDOUT;
}
return 0;
}
static int tegra_mipi_bif_flush_fifos(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
unsigned long timeout = jiffies + HZ;
u32 val = readl(mipi_bif_dev->base + MIPIBIF_FIFO_CONTROL);
val |= MIPIBIF_RXFIFO_FLUSH;
val |= MIPIBIF_TXFIFO_FLUSH;
writel(val, mipi_bif_dev->base + MIPIBIF_FIFO_CONTROL);
while (readl(mipi_bif_dev->base + MIPIBIF_FIFO_CONTROL) &
(MIPIBIF_RXFIFO_FLUSH | MIPIBIF_RXFIFO_FLUSH)) {
if (time_after(jiffies, timeout)) {
dev_warn(mipi_bif_dev->dev, "timeout for fifo flush\n");
return -ETIMEDOUT;
}
msleep(1);
}
return 0;
}
static int
tegra_mipi_bif_program_timings(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
u32 timing_pvt;
u32 timing0, timing1, timing2, timing3, timing4;
timing_pvt = 0 << MIPIBIF_TIMING_TBIF_0_SHIFT;
timing_pvt |= 2 << MIPIBIF_TIMING_TBIF_1_SHIFT;
timing_pvt |= 4 << MIPIBIF_TIMING_TBIF_STOP_SHIFT;
writel(timing_pvt, mipi_bif_dev->base + MIPIBIF_TIMING_PVT);
timing0 = 3 << MIPIBIF_TIMING0_TRESP_MIN_SHIFT;
timing0 |= 0xe << MIPIBIF_TIMING0_TRESP_MAX_SHIFT;
timing0 |= (mipi_bif_dev->bus_clk_rate * mipi_bif_dev->tauBIF - 1)
<< MIPIBIF_TIMING0_TBIF_SHIFT;
writel(timing0, mipi_bif_dev->base + MIPIBIF_TIMING0);
timing2 = (2500 * mipi_bif_dev->bus_clk_rate - 1)
<< MIPIBIF_TIMING2_TPDL_SHIFT;
timing2 |= (50 * mipi_bif_dev->bus_clk_rate - 1)
<< MIPIBIF_TIMING2_TPUL_SHIFT;
writel(timing2, mipi_bif_dev->base + MIPIBIF_TIMING2);
timing3 = (100 * mipi_bif_dev->bus_clk_rate - 1)
<< MIPIBIF_TIMING3_TACT_SHIFT;
timing3 |= (11000 * mipi_bif_dev->bus_clk_rate - 1)
<< MIPIBIF_TIMING3_TPUP_SHIFT;
writel(timing3, mipi_bif_dev->base + MIPIBIF_TIMING3);
timing1 = readl(mipi_bif_dev->base + MIPIBIF_TIMING1);
writel(timing1 | ((5000 / mipi_bif_dev->tauBIF) - 1),
mipi_bif_dev->base + MIPIBIF_TIMING1);
timing4 = 0xdab << MIPIBIF_TIMING4_TCLWS_SHIFT;
writel(timing4, mipi_bif_dev->base + MIPIBIF_TIMING4);
return 0;
}
static int tegra_mipi_bif_init(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
u32 fifo_control;
pm_runtime_get_sync(mipi_bif_dev->dev);
clk_prepare_enable(mipi_bif_dev->mipi_bif_clk);
tegra_periph_reset_assert(mipi_bif_dev->mipi_bif_clk);
udelay(2);
tegra_periph_reset_deassert(mipi_bif_dev->mipi_bif_clk);
clk_set_rate(mipi_bif_dev->mipi_bif_clk,
mipi_bif_dev->bus_clk_rate * 1000000);
writel(0, mipi_bif_dev->base + MIPIBIF_INTERRUPT_EN);
fifo_control = 0 << MIPIBIF_RXFIFO_ATN_LVL_SHIFT;
fifo_control |= 5 << MIPIBIF_TXFIFO_ATN_LVL_SHIFT;
fifo_control |= MIPIBIF_RXFIFO_FLUSH;
fifo_control |= MIPIBIF_TXFIFO_FLUSH;
writel(fifo_control, mipi_bif_dev->base + MIPIBIF_FIFO_CONTROL);
tegra_mipi_bif_program_timings(mipi_bif_dev);
clk_disable_unprepare(mipi_bif_dev->mipi_bif_clk);
pm_runtime_put(mipi_bif_dev->dev);
return 0;
}
static int tegra_mipi_bif_empty_rx_fifo(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
u32 val;
int rx_fifo_avail;
u8 *buf = mipi_bif_dev->msg_buf;
size_t buf_remaining = mipi_bif_dev->msg_buf_remaining;
int to_be_transferred = buf_remaining;
int ret = 0;
val = readl(mipi_bif_dev->base + MIPIBIF_FIFO_STATUS);
rx_fifo_avail = (val & MIPIBIF_RX_FIFO_FULL_COUNT_MASK)
>> MIPIBIF_RX_FIFO_FULL_COUNT_SHIFT;
if (to_be_transferred > rx_fifo_avail)
to_be_transferred = rx_fifo_avail;
while (to_be_transferred > 0) {
val = readl(mipi_bif_dev->base + MIPIBIF_RX_FIFO);
val = val >> 7;
if (!(val & MIPI_BIF_RD_ACK_BIT)) {
dev_warn(mipi_bif_dev->dev, "Error in data:%x\n", val);
mipi_bif_dev->msg_err = MIPIBIF_ERR_RECV_DATA_TYPE;
ret = -EIO;
break;
}
*buf = val & 0xFF;
rx_fifo_avail--;
to_be_transferred--;
buf_remaining--;
buf++;
}
mipi_bif_dev->msg_buf_remaining = buf_remaining;
mipi_bif_dev->msg_buf = buf;
return ret;
}
static int tegra_mipi_bif_fill_tx_fifo(struct tegra_mipi_bif_dev *mipi_bif_dev)
{
u8 *buf;
u32 val;
size_t buf_remaining;
int tx_fifo_available;
int to_be_transferred;
buf = mipi_bif_dev->msg_buf;
buf_remaining = mipi_bif_dev->msg_buf_remaining;
to_be_transferred = buf_remaining;
val = readl(mipi_bif_dev->base + MIPIBIF_FIFO_STATUS);
tx_fifo_available = (val & MIPIBIF_TX_FIFO_EMPTY_COUNT_MASK)
>> MIPIBIF_TX_FIFO_EMPTY_COUNT_SHIFT;
if (tx_fifo_available > 0) {
if (to_be_transferred > tx_fifo_available)
to_be_transferred = tx_fifo_available;
mipi_bif_dev->msg_buf = buf + to_be_transferred;
mipi_bif_dev->msg_buf_remaining -= to_be_transferred;
buf_remaining = mipi_bif_dev->msg_buf_remaining;
while (to_be_transferred) {
writel(*buf, mipi_bif_dev->base + MIPIBIF_TX_FIFO);
buf++;
to_be_transferred--;
tx_fifo_available--;
}
}
return 0;
}
static void
tegra_mipi_bif_device_detect(struct tegra_mipi_bif_dev *mipi_bif_dev, int n)
{
int ret;
if (!n)
return;
if (tegra_mipi_bif_send_DIP0_command(mipi_bif_dev)) {
dev_dbg(mipi_bif_dev->dev, "0");
ret = tegra_mipi_bif_send_DIE0_command(mipi_bif_dev);
tegra_mipi_bif_device_detect(mipi_bif_dev, n - 1);
}
if (tegra_mipi_bif_send_DIP1_command(mipi_bif_dev)) {
dev_dbg(mipi_bif_dev->dev, "1");
ret = tegra_mipi_bif_send_DIE1_command(mipi_bif_dev);
tegra_mipi_bif_device_detect(mipi_bif_dev, n - 1);
}
}
static int
tegra_mipi_bif_xfer(struct mipi_bif_adapter *adap, struct mipi_bif_msg *msg)
{
struct tegra_mipi_bif_dev *mipi_bif_dev = mipi_bif_get_adapdata(adap);
int ret = 0;
u32 sda_part;
u32 era_part;
u32 wra_part;
u32 rra_part;
u32 rbe = MIPI_BIF_BUS_COMMAND_RBE0;
u32 rbl = MIPI_BIF_BUS_COMMAND_RBL0;
u32 ctrl_reg = 0;
u32 def_int_mask = MIPIBIF_DEFAULT_INTMASK;
u32 int_mask = def_int_mask;
rt_mutex_lock(&mipi_bif_dev->dev_lock);
if (mipi_bif_dev->is_suspended) {
rt_mutex_unlock(&mipi_bif_dev->dev_lock);
return -EBUSY;
}
tegra_mipi_bif_init(mipi_bif_dev);
pm_runtime_get_sync(mipi_bif_dev->dev);
clk_prepare_enable(mipi_bif_dev->mipi_bif_clk);
tegra_mipi_bif_flush_fifos(mipi_bif_dev);
INIT_COMPLETION(mipi_bif_dev->msg_complete);
mipi_bif_dev->msg_device_addr = msg->device_addr;
mipi_bif_dev->msg_commands = msg->commands;
mipi_bif_dev->msg_buf_remaining = 0;
mipi_bif_dev->current_command_count = 0;
mipi_bif_dev->msg_err = MIPIBIF_ERR_NONE;
sda_part = msg->device_addr & 0xFF;
if (msg->commands == MIPI_BIF_WRITE) {
if (msg->len == 0 || (!msg->buf))
ret = -EINVAL;
mipi_bif_dev->msg_buf = msg->buf;
mipi_bif_dev->msg_len = msg->len;
mipi_bif_dev->msg_buf_remaining = msg->len;
mipi_bif_dev->msg_reg_addr = msg->reg_addr;
wra_part = msg->reg_addr & 0xFF;
era_part = (msg->reg_addr & 0xFF00) >> 8;
tegra_mipi_bif_send(mipi_bif_dev, sda_part | MIPI_BIF_SDA);
tegra_mipi_bif_send(mipi_bif_dev, era_part | MIPI_BIF_ERA);
tegra_mipi_bif_send(mipi_bif_dev, wra_part | MIPI_BIF_WRA);
ctrl_reg = MIPIBIF_CTRL_COMMAND_NO_READ;
ctrl_reg |= ((msg->len + mipi_bif_dev->current_command_count - 1)
<< MIPIBIF_CTRL_PACKETCOUNT_SHIFT);
tegra_mipi_bif_fill_tx_fifo(mipi_bif_dev);
if (mipi_bif_dev->msg_buf_remaining)
int_mask |= MIPIBIF_INTERRUPT_TXF_DATA_REQ_INT_EN;
tegra_mipi_bif_unmask_irq(mipi_bif_dev, int_mask);
ctrl_reg |= MIPIBIF_CTRL_GO;
writel(ctrl_reg, mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:timed out", __func__);
ret = -ETIMEDOUT;
}
} else if (msg->commands == MIPI_BIF_READDATA) {
if (msg->len == 0)
ret = -EINVAL;
mipi_bif_dev->msg_buf = msg->buf;
mipi_bif_dev->msg_len = msg->len;
mipi_bif_dev->msg_buf_remaining = msg->len;
mipi_bif_dev->msg_reg_addr = msg->reg_addr;
rra_part = msg->reg_addr & 0xFF;
era_part = (msg->reg_addr & 0xFF00) >> 8;
rbe |= ((msg->len & 0xFF00) >> 8);
rbl |= (msg->len & 0xFF);
if (msg->len == 256)
rbe = rbl = 0;
tegra_mipi_bif_send(mipi_bif_dev, sda_part | MIPI_BIF_SDA);
tegra_mipi_bif_send(mipi_bif_dev,
rbe | MIPI_BIF_BUS_COMMAND_RBE0);
tegra_mipi_bif_send(mipi_bif_dev,
rbl | MIPI_BIF_BUS_COMMAND_RBL0);
tegra_mipi_bif_send(mipi_bif_dev, era_part | MIPI_BIF_ERA);
tegra_mipi_bif_send(mipi_bif_dev, rra_part | MIPI_BIF_RRA);
int_mask |= MIPIBIF_INTERRUPT_RXF_DATA_REQ_INT_EN;
tegra_mipi_bif_unmask_irq(mipi_bif_dev, int_mask);
ctrl_reg |= MIPIBIF_CTRL_COMMAND_READ_DATA;
ctrl_reg |= ((mipi_bif_dev->current_command_count - 1)
<< MIPIBIF_CTRL_PACKETCOUNT_SHIFT);
ctrl_reg |= MIPIBIF_CTRL_GO;
writel(ctrl_reg, mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:timed out", __func__);
ret = -ETIMEDOUT;
}
} else if (msg->commands == MIPI_BIF_INT_READ) {
tegra_mipi_bif_send(mipi_bif_dev, sda_part | MIPI_BIF_SDA);
tegra_mipi_bif_send(mipi_bif_dev, MIPI_BIF_BUS_COMMAND_EINT);
tegra_mipi_bif_unmask_irq(mipi_bif_dev, int_mask);
ctrl_reg = MIPIBIF_CTRL_COMMAND_INTERRUPT_DATA;
ctrl_reg |= ((mipi_bif_dev->current_command_count - 1)
<< MIPIBIF_CTRL_PACKETCOUNT_SHIFT);
ctrl_reg |= MIPIBIF_CTRL_GO;
writel(ctrl_reg, mipi_bif_dev->base + MIPIBIF_CTRL);
ret = wait_for_completion_timeout(&mipi_bif_dev->msg_complete,
TEGRA_MIPIBIF_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(mipi_bif_dev->dev, "%s:timed out", __func__);
ret = -ETIMEDOUT;
}
if (!(readl(mipi_bif_dev->base + MIPIBIF_STATUS)
& MIPIBIF_INTERRUPT_RECV_STATUS)) {
dev_err(mipi_bif_dev->dev, "%s:no interrupt", __func__);
ret = -EIO;
}
} else if (msg->commands == MIPI_BIF_STDBY) {
ret = tegra_mipi_bif_StandBy(mipi_bif_dev);
} else if (msg->commands == MIPI_BIF_PWRDOWN) {
ret = tegra_mipi_bif_PowerDown(mipi_bif_dev);
} else if (msg->commands == MIPI_BIF_ACTIVATE) {
ret = tegra_mipi_bif_Activate(mipi_bif_dev);
} else if (msg->commands == MIPI_BIF_INT_EXIT) {
ret = tegra_mipi_bif_Exit_Interrupt(mipi_bif_dev);
} else if (msg->commands == MIPI_BIF_HARD_RESET) {
ret = tegra_mipi_bif_HardReset(mipi_bif_dev);
}/* else if (msg->commands == MIPI_BIF_BUSQUERY) {
ret = tegra_mipi_bif_send_DISS_command(mipi_bif_dev);
tegra_mipi_bif_device_detect(mipi_bif_dev, 80);
}*/
tegra_mipi_bif_mask_irq(mipi_bif_dev, int_mask);
if (mipi_bif_dev->msg_err & MIPIBIF_ERR_RECV_DATA_TYPE)
ret = -EAGAIN;
else if (mipi_bif_dev->msg_err != MIPIBIF_ERR_NONE)
ret = -EIO;
clk_disable_unprepare(mipi_bif_dev->mipi_bif_clk);
pm_runtime_put(mipi_bif_dev->dev);
rt_mutex_unlock(&mipi_bif_dev->dev_lock);
return ret;
}
static const struct mipi_bif_algorithm tegra_mipi_bif_algo = {
.master_xfer = tegra_mipi_bif_xfer,
};
static irqreturn_t tegra_mipi_bif_isr(int irq, void *dev_id)
{
u32 status;
int ret;
struct tegra_mipi_bif_dev *mipi_bif_dev = dev_id;
status = readl(mipi_bif_dev->base + MIPIBIF_INTERRUPT_STATUS);
if (status == 0) {
dev_warn(mipi_bif_dev->dev, "Unknown interrupt\n");
goto err;
}
if (status & MIPIBIF_INTERRUPT_NO_RESPONSE_ERR) {
mipi_bif_dev->msg_err = MIPIBIF_ERR_NO_RESPONSE;
dev_warn(mipi_bif_dev->dev,
"error: No response from slave within tresp\n");
goto err;
}
if (status & MIPIBIF_INTERRUPT_INV_ERR) {
mipi_bif_dev->msg_err = MIPIBIF_ERR_INV;
dev_warn(mipi_bif_dev->dev,
"error: Incorrect inversion received\n");
goto err;
}
if (status & MIPIBIF_INTERRUPT_PKT_RECV_ERR) {
mipi_bif_dev->msg_err = MIPIBIF_ERR_PKT_RECV;
dev_warn(mipi_bif_dev->dev,
"error: Incorrect ack received\n");
goto err;
}
if (status & MIPIBIF_INTERRUPT_LOW_PHASE_IN_WORD_ERR) {
mipi_bif_dev->msg_err = MIPIBIF_ERR_LOW_PHASE_IN_WORD;
dev_warn(mipi_bif_dev->dev,
"error: Low phase in word err received\n");
goto err;
}
if (status & MIPIBIF_INTERRUPT_INCOMPLETE_PKT_RECV_ERR) {
mipi_bif_dev->msg_err = MIPIBIF_ERR_INCOMPLETE_PKT_RECV;
dev_warn(mipi_bif_dev->dev,
"error: Incomplete packet received\n");
goto err;
}
if (status & MIPIBIF_INTERRUPT_PARITY_ERR) {
mipi_bif_dev->msg_err = MIPIBIF_ERR_PARITY;
dev_warn(mipi_bif_dev->dev,
"error: Incorrect parity received\n");
goto err;
}
if ((mipi_bif_dev->msg_commands == MIPI_BIF_WRITE)
&& (status & MIPIBIF_INTERRUPT_TXF_DATA_REQ)) {
if (mipi_bif_dev->msg_buf_remaining)
tegra_mipi_bif_fill_tx_fifo(mipi_bif_dev);
else
tegra_mipi_bif_mask_irq(mipi_bif_dev,
MIPIBIF_INTERRUPT_TXF_DATA_REQ_INT_EN);
}
if ((mipi_bif_dev->msg_commands == MIPI_BIF_READDATA)
&& (status & MIPIBIF_INTERRUPT_RXF_DATA_REQ)) {
if (mipi_bif_dev->msg_buf_remaining) {
ret = tegra_mipi_bif_empty_rx_fifo(mipi_bif_dev);
if (ret)
goto err;
} else {
tegra_mipi_bif_mask_irq(mipi_bif_dev,
MIPIBIF_INTERRUPT_RXF_DATA_REQ_INT_EN);
}
}
if (status & MIPIBIF_INTERRUPT_XFER_DONE) {
WARN_ON(mipi_bif_dev->msg_buf_remaining);
complete(&mipi_bif_dev->msg_complete);
}
writel(status, mipi_bif_dev->base + MIPIBIF_INTERRUPT_STATUS);
return IRQ_HANDLED;
err:
writel(status, mipi_bif_dev->base + MIPIBIF_INTERRUPT_STATUS);
complete(&mipi_bif_dev->msg_complete);
return IRQ_HANDLED;
}
static int tegra_mipi_bif_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
return 0;
}
static int tegra_mipi_bif_probe(struct platform_device *pdev)
{
struct tegra_mipi_bif_dev *mipi_bif_dev;
struct tegra_mipi_bif_platform_data *plat = pdev->dev.platform_data;
struct resource *res;
struct clk *mipi_bif_clk;
void __iomem *base;
int irq;
int ret;
if (!plat) {
dev_err(&pdev->dev, "no platform data?\n");
return -ENODEV;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no mem resource\n");
return -EINVAL;
}
base = devm_request_and_ioremap(&pdev->dev, res);
if (!base) {
dev_err(&pdev->dev, "Cannot request/ioremap MIPIBIF regs\n");
return -EADDRNOTAVAIL;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "no irq resource\n");
return -EINVAL;
}
irq = res->start;
mipi_bif_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(mipi_bif_clk)) {
dev_err(&pdev->dev, "missing mipi bif controller clock\n");
return PTR_ERR(mipi_bif_clk);
}
mipi_bif_dev = devm_kzalloc(&pdev->dev,
sizeof(struct tegra_mipi_bif_dev), GFP_KERNEL);
if (!mipi_bif_dev) {
dev_err(&pdev->dev, "Failed to allocate memory\n");
return -ENOMEM;
}
mipi_bif_dev->bus_clk_rate = plat->bus_clk_rate;
mipi_bif_dev->tauBIF = plat->tauBIF;
mipi_bif_dev->base = base;
mipi_bif_dev->mipi_bif_clk = mipi_bif_clk;
mipi_bif_dev->irq = irq;
mipi_bif_dev->cont_id = pdev->id;
mipi_bif_dev->dev = &pdev->dev;
ret = devm_request_irq(&pdev->dev, mipi_bif_dev->irq,
tegra_mipi_bif_isr, 0, pdev->name, mipi_bif_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n",
mipi_bif_dev->irq);
return ret;
}
pm_runtime_enable(&pdev->dev);
platform_set_drvdata(pdev, mipi_bif_dev);
rt_mutex_init(&mipi_bif_dev->dev_lock);
spin_lock_init(&mipi_bif_dev->fifo_lock);
init_completion(&mipi_bif_dev->msg_complete);
mipi_bif_dev->adapter.algo = &tegra_mipi_bif_algo;
strlcpy(mipi_bif_dev->adapter.name, "Tegra MIPIBIF adapter",
sizeof(mipi_bif_dev->adapter.name));
mipi_bif_dev->adapter.dev.parent = &pdev->dev;
mipi_bif_dev->adapter.nr = plat->adapter_nr;
mipi_bif_set_adapdata(&mipi_bif_dev->adapter, mipi_bif_dev);
ret = mipi_bif_add_numbered_adapter(&mipi_bif_dev->adapter);
if (ret) {
dev_err(&pdev->dev, "Failed to add mipi_bif adapter\n");
return ret;
}
return 0;
}
static int tegra_mipi_bif_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tegra_mipi_bif_dev *mipi_bif_dev = platform_get_drvdata(pdev);
rt_mutex_lock(&mipi_bif_dev->dev_lock);
mipi_bif_dev->is_suspended = false;
rt_mutex_unlock(&mipi_bif_dev->dev_lock);
return 0;
}
static int tegra_mipi_bif_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tegra_mipi_bif_dev *mipi_bif_dev = platform_get_drvdata(pdev);
rt_mutex_lock(&mipi_bif_dev->dev_lock);
mipi_bif_dev->is_suspended = true;
rt_mutex_unlock(&mipi_bif_dev->dev_lock);
return 0;
}
static const struct dev_pm_ops tegra_mipi_bif_pm = {
.suspend = tegra_mipi_bif_suspend,
.resume = tegra_mipi_bif_resume,
};
static struct platform_driver tegra_mipi_bif_driver = {
.probe = tegra_mipi_bif_probe,
.remove = tegra_mipi_bif_remove,
.driver = {
.name = "tegra-mipi-bif",
.owner = THIS_MODULE,
.pm = &tegra_mipi_bif_pm
},
};
static int __init tegra_mipi_bif_init_driver(void)
{
return platform_driver_register(&tegra_mipi_bif_driver);
}
static void __exit tegra_mipi_bif_exit_driver(void)
{
platform_driver_unregister(&tegra_mipi_bif_driver);
}
subsys_initcall(tegra_mipi_bif_init_driver);
module_exit(tegra_mipi_bif_exit_driver);
MODULE_DESCRIPTION("nVidia Tegra MIPI BIF Controller driver");
MODULE_AUTHOR("Chaitanya Bandi");
MODULE_LICENSE("GPL v2");