cLK/lk/platform/msm_shared/i2c_qup.c

841 lines
25 KiB
C
Raw Normal View History

2011-03-25 21:39:33 +00:00
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* QUP driver for Qualcomm MSM platforms
*
*/
#include <debug.h>
#include <arch/arm.h>
#include <reg.h>
#include <kernel/thread.h>
#include <i2c_qup.h>
#include <platform/irqs.h>
#include <platform/iomap.h>
#include <platform/gpio_hw.h>
static struct qup_i2c_dev *dev_addr = NULL;
/* QUP Registers */
enum {
QUP_CONFIG = 0x0,
QUP_STATE = 0x4,
QUP_IO_MODE = 0x8,
QUP_SW_RESET = 0xC,
QUP_OPERATIONAL = 0x18,
QUP_ERROR_FLAGS = 0x1C,
QUP_ERROR_FLAGS_EN = 0x20,
QUP_MX_READ_CNT = 0x208,
QUP_MX_INPUT_CNT = 0x200,
QUP_MX_WR_CNT = 0x100,
QUP_OUT_DEBUG = 0x108,
QUP_OUT_FIFO_CNT = 0x10C,
QUP_OUT_FIFO_BASE = 0x110,
QUP_IN_READ_CUR = 0x20C,
QUP_IN_DEBUG = 0x210,
QUP_IN_FIFO_CNT = 0x214,
QUP_IN_FIFO_BASE = 0x218,
QUP_I2C_CLK_CTL = 0x400,
QUP_I2C_STATUS = 0x404,
};
/* QUP States and reset values */
enum {
QUP_RESET_STATE = 0,
QUP_RUN_STATE = 1U,
QUP_STATE_MASK = 3U,
QUP_PAUSE_STATE = 3U,
QUP_STATE_VALID = 1U << 2,
QUP_I2C_MAST_GEN = 1U << 4,
QUP_OPERATIONAL_RESET = 0xFF0,
QUP_I2C_STATUS_RESET = 0xFFFFFC,
};
/* QUP OPERATIONAL FLAGS */
enum {
QUP_OUT_SVC_FLAG = 1U << 8,
QUP_IN_SVC_FLAG = 1U << 9,
QUP_MX_INPUT_DONE = 1U << 11,
};
/* I2C mini core related values */
enum {
I2C_MINI_CORE = 2U << 8,
I2C_N_VAL = 0xF,
};
/* Packing Unpacking words in FIFOs , and IO modes*/
enum {
QUP_WR_BLK_MODE = 1U << 10,
QUP_RD_BLK_MODE = 1U << 12,
QUP_UNPACK_EN = 1U << 14,
QUP_PACK_EN = 1U << 15,
};
/* QUP tags */
enum {
QUP_OUT_NOP = 0,
QUP_OUT_START = 1U << 8,
QUP_OUT_DATA = 2U << 8,
QUP_OUT_STOP = 3U << 8,
QUP_OUT_REC = 4U << 8,
QUP_IN_DATA = 5U << 8,
QUP_IN_STOP = 6U << 8,
QUP_IN_NACK = 7U << 8,
};
/* Status, Error flags */
enum {
I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
I2C_STATUS_BUS_ACTIVE = 1U << 8,
I2C_STATUS_ERROR_MASK = 0x38000FC,
QUP_I2C_NACK_FLAG = 1U << 3,
QUP_IN_NOT_EMPTY = 1U << 5,
QUP_STATUS_ERROR_FLAGS = 0x7C,
};
void set_i2c_clk(struct qup_i2c_dev *dev)
{
uint32_t md = 0;
uint32_t ns = 0;
switch (dev->src_clk_freq) {
case 24000000:
ns = I2C_APPS_CLK_NS_24MHz;
md = I2C_APPS_CLK_MD_24MHz;
break;
default:
return;
}
/* Enable the GSBI8 HCLK */
writel((GSBI8_HCLK_CTL_CLK_ENA << GSBI8_HCLK_CTL_S),
GSBIn_HCLK_CTL(dev->gsbi_number));
clock_config(ns,
md,
GSBIn_QUP_APPS_NS(dev->gsbi_number),
GSBIn_QUP_APPS_MD(dev->gsbi_number));
}
void i2c_gpio_cfg(uint32_t base)
{
switch (base) {
case GSBI8_BASE:
gpio_tlmm_config(64, 1, GPIO_OUTPUT, GPIO_NO_PULL,
GPIO_2MA, GPIO_DISABLE);
gpio_tlmm_config(65, 1, GPIO_OUTPUT, GPIO_NO_PULL,
GPIO_2MA, GPIO_DISABLE);
break;
default:
break;
}
}
#ifdef DEBUG
static void qup_print_status(struct qup_i2c_dev *dev)
{
unsigned val;
val = readl(dev->base + QUP_CONFIG);
dprintf(INFO, "Qup config is :0x%x\n", val);
val = readl(dev->base + QUP_STATE);
dprintf(INFO, "Qup state is :0x%x\n", val);
val = readl(dev->base + QUP_IO_MODE);
dprintf(INFO, "Qup mode is :0x%x\n", val);
}
#else
static inline void qup_print_status(struct qup_i2c_dev *dev)
{
}
#endif
void i2c_gpio_cfg(unsigned base);
static irqreturn_t qup_i2c_interrupt(void)
{
struct qup_i2c_dev *dev = dev_addr;
if (!dev) {
dprintf(CRITICAL,
"dev_addr is NULL, that means i2c_qup_init failed...\n");
return IRQ_FAIL;
}
unsigned status = readl(dev->base + QUP_I2C_STATUS);
unsigned status1 = readl(dev->base + QUP_ERROR_FLAGS);
unsigned op_flgs = readl(dev->base + QUP_OPERATIONAL);
int err = 0;
if (!dev->msg)
return IRQ_HANDLED;
if (status & I2C_STATUS_ERROR_MASK) {
dprintf(CRITICAL, "QUP: I2C status flags :0x%x \n", status);
err = -status;
/* Clear Error interrupt if it's a level triggered interrupt */
if (dev->num_irqs == 1) {
writel(QUP_RESET_STATE, dev->base + QUP_STATE);
}
goto intr_done;
}
if (status1 & 0x7F) {
dprintf(CRITICAL, "QUP: QUP status flags :0x%x\n", status1);
err = -status1;
/* Clear Error interrupt if it's a level triggered interrupt */
if (dev->num_irqs == 1)
writel((status1 & QUP_STATUS_ERROR_FLAGS),
dev->base + QUP_ERROR_FLAGS);
goto intr_done;
}
if (op_flgs & QUP_OUT_SVC_FLAG)
writel(QUP_OUT_SVC_FLAG, dev->base + QUP_OPERATIONAL);
if (dev->msg->flags == I2C_M_RD) {
if ((op_flgs & QUP_MX_INPUT_DONE) || (op_flgs & QUP_IN_SVC_FLAG))
writel(QUP_IN_SVC_FLAG, dev->base + QUP_OPERATIONAL);
else
return IRQ_HANDLED;
}
intr_done:
dev->err = err;
return IRQ_HANDLED;
}
static int qup_i2c_poll_writeready(struct qup_i2c_dev *dev)
{
unsigned retries = 0;
while (retries != 2000) {
unsigned status = readl(dev->base + QUP_I2C_STATUS);
if (!(status & I2C_STATUS_WR_BUFFER_FULL)) {
if (!(status & I2C_STATUS_BUS_ACTIVE))
return 0;
else /* 1-bit delay before we check for bus busy */
udelay(dev->one_bit_t);
}
if (retries++ == 1000)
udelay(100);
}
qup_print_status(dev);
return -ETIMEDOUT;
}
static int qup_i2c_poll_state(struct qup_i2c_dev *dev, unsigned state)
{
unsigned retries = 0;
dprintf(CRITICAL, "Polling Status for state:0x%x\n", state);
while (retries != 2000) {
unsigned status = readl(dev->base + QUP_STATE);
if ((status & (QUP_STATE_VALID | state)) == (QUP_STATE_VALID | state))
return 0;
else if (retries++ == 1000)
udelay(100);
}
return -ETIMEDOUT;
}
#ifdef DEBUG
static void qup_verify_fifo(struct qup_i2c_dev *dev, unsigned val,
unsigned addr, int rdwr)
{
if (rdwr)
dprintf(INFO, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr);
else
dprintf(INFO, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr);
}
#else
static inline void qup_verify_fifo(struct qup_i2c_dev *dev, unsigned val,
unsigned addr, int rdwr)
{
}
#endif
static void
qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx,
unsigned carry_over)
{
uint16_t addr = (msg->addr << 1) | 1;
/* QUP limit 256 bytes per read. By HW design, 0 in the 8-bit field is
treated as 256 byte read. */
uint16_t rd_len = ((dev->cnt == 256) ? 0 : dev->cnt);
if (*idx % 4) {
writel(carry_over | ((QUP_OUT_START | addr) << 16),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, carry_over |
((QUP_OUT_START | addr) << 16), (unsigned)dev->base
+ QUP_OUT_FIFO_BASE + (*idx - 2), 1);
writel((QUP_OUT_REC | rd_len), dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
(unsigned)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2),
1);
} else {
writel(((QUP_OUT_REC | rd_len) << 16) |
QUP_OUT_START | addr, dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
QUP_OUT_START | addr,
(unsigned)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1);
}
*idx += 4;
}
static void
qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem,
int *idx, unsigned *carry_over)
{
int entries = dev->cnt;
int empty_sl = dev->wr_sz - ((*idx) >> 1);
int i = 0;
unsigned val = 0;
unsigned last_entry = 0;
uint16_t addr = msg->addr << 1;
if (dev->pos == 0) {
if (*idx % 4) {
writel(*carry_over | ((QUP_OUT_START | addr) << 16),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, *carry_over | QUP_OUT_DATA << 16 |
addr << 16, (unsigned)dev->base +
QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
} else
val = QUP_OUT_START | addr;
*idx += 2;
i++;
entries++;
} else {
/* Avoid setp time issue by adding 1 NOP when number of bytes are more
than FIFO/BLOCK size. setup time issue can't appear otherwise since
next byte to be written will always be ready */
val = (QUP_OUT_NOP | 1);
*idx += 2;
i++;
entries++;
}
if (entries > empty_sl)
entries = empty_sl;
for (; i < (entries - 1); i++) {
if (*idx % 4) {
writel(val | ((QUP_OUT_DATA |
msg->buf[dev->pos]) << 16),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
msg->buf[dev->pos] << 16, (unsigned)dev->base +
QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
} else
val = QUP_OUT_DATA | msg->buf[dev->pos];
(*idx) += 2;
dev->pos++;
}
if (dev->pos < (msg->len - 1))
last_entry = QUP_OUT_DATA;
else if (rem > 1) /* not last array entry */
last_entry = QUP_OUT_DATA;
else
last_entry = QUP_OUT_STOP;
if ((*idx % 4) == 0) {
/*
* If read-start and read-command end up in different fifos, it
* may result in extra-byte being read due to extra-read cycle.
* Avoid that by inserting NOP as the last entry of fifo only
* if write command(s) leave 1 space in fifo.
*/
if (rem > 1) {
struct i2c_msg *next = msg + 1;
if (next->addr == msg->addr && (next->flags | I2C_M_RD)
&& *idx == ((dev->wr_sz * 2) - 4)) {
writel(((last_entry | msg->buf[dev->pos]) |
((1 | QUP_OUT_NOP) << 16)),
dev->base + QUP_OUT_FIFO_BASE);
*idx += 2;
} else
*carry_over = (last_entry | msg->buf[dev->pos]);
} else {
writel((last_entry | msg->buf[dev->pos]),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
(unsigned)dev->base + QUP_OUT_FIFO_BASE +
(*idx), 0);
}
} else {
writel(val | ((last_entry | msg->buf[dev->pos]) << 16),
dev->base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, val | (last_entry << 16) |
(msg->buf[dev->pos] << 16), (unsigned)dev->base +
QUP_OUT_FIFO_BASE + (*idx) - 2, 0);
}
*idx += 2;
dev->pos++;
dev->cnt = msg->len - dev->pos;
}
static int qup_update_state(struct qup_i2c_dev *dev, unsigned state)
{
if (qup_i2c_poll_state(dev, 0) != 0)
return -EIO;
writel(state, dev->base + QUP_STATE);
if (qup_i2c_poll_state(dev, state) != 0)
return -EIO;
return 0;
}
static int qup_set_read_mode(struct qup_i2c_dev *dev, int rd_len)
{
unsigned wr_mode = (dev->wr_sz < dev->out_fifo_sz) ? QUP_WR_BLK_MODE : 0;
if (rd_len > 256) {
dprintf(INFO, "HW doesn't support READs > 256 bytes\n");
return -EPROTONOSUPPORT;
}
if (rd_len <= dev->in_fifo_sz) {
writel(wr_mode | QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
writel(rd_len, dev->base + QUP_MX_READ_CNT);
} else {
writel(wr_mode | QUP_RD_BLK_MODE |
QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
writel(rd_len, dev->base + QUP_MX_INPUT_CNT);
}
return 0;
}
static int qup_set_wr_mode(struct qup_i2c_dev *dev, int rem)
{
int total_len = 0;
int ret = 0;
if (dev->msg->len >= (dev->out_fifo_sz - 1)) {
total_len = dev->msg->len + 1 + (dev->msg->len / (dev->out_blk_sz - 1));
writel(QUP_WR_BLK_MODE | QUP_PACK_EN | QUP_UNPACK_EN,
dev->base + QUP_IO_MODE);
dev->wr_sz = dev->out_blk_sz;
} else
writel(QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE);
if (rem > 1) {
struct i2c_msg *next = dev->msg + 1;
if (next->addr == dev->msg->addr && next->flags == I2C_M_RD) {
ret = qup_set_read_mode(dev, next->len);
/* make sure read start & read command are in 1 blk */
if ((total_len % dev->out_blk_sz) == (dev->out_blk_sz - 1))
total_len += 3;
else
total_len += 2;
}
}
/* WRITE COUNT register valid/used only in block mode */
if (dev->wr_sz == dev->out_blk_sz)
writel(total_len, dev->base + QUP_MX_WR_CNT);
return ret;
}
int qup_i2c_xfer(struct qup_i2c_dev *dev, struct i2c_msg msgs[], int num)
{
int ret;
int rem = num;
int err;
if (dev->suspended) {
return -EIO;
}
/* Set the GSBIn_QUP_APPS_CLK to 24MHz, then below figure out what speed to
run I2C_MASTER_CORE at. */
if (dev->clk_state == 0) {
if (dev->clk_ctl == 0) {
set_i2c_clk(dev);
}
}
/* Initialize QUP registers during first transfer */
if (dev->clk_ctl == 0) {
int fs_div;
int hs_div;
unsigned fifo_reg;
/* Configure the GSBI Protocol Code for i2c */
writel((GSBI_PROTOCOL_CODE_I2C <<
GSBI_CTRL_REG_PROTOCOL_CODE_S), dev->base);
fs_div = ((dev->src_clk_freq / dev->clk_freq) / 2) - 3;
hs_div = 3;
dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff);
fifo_reg = readl(dev->base + QUP_IO_MODE);
if (fifo_reg & 0x3)
dev->out_blk_sz = (fifo_reg & 0x3) * 16;
else
dev->out_blk_sz = 16;
if (fifo_reg & 0x60)
dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16;
else
dev->in_blk_sz = 16;
/*
* The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag'
* associated with each byte written/received
*/
dev->out_blk_sz /= 2;
dev->in_blk_sz /= 2;
dev->out_fifo_sz = dev->out_blk_sz * (2 << ((fifo_reg & 0x1C) >> 2));
dev->in_fifo_sz = dev->in_blk_sz * (2 << ((fifo_reg & 0x380) >> 7));
dprintf(CRITICAL, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n",
dev->in_blk_sz, dev->in_fifo_sz,
dev->out_blk_sz, dev->out_fifo_sz);
}
unmask_interrupt(dev->qup_irq);
writel(1, dev->base + QUP_SW_RESET);
ret = qup_i2c_poll_state(dev, QUP_RESET_STATE);
if (ret) {
dprintf(INFO, "QUP Busy:Trying to recover\n");
goto out_err;
}
/* Initialize QUP registers */
writel(0, dev->base + QUP_CONFIG);
writel(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL);
writel(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN);
writel(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG);
/* Initialize I2C mini core registers */
writel(0, dev->base + QUP_I2C_CLK_CTL);
writel(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS);
dev->cnt = msgs->len;
dev->pos = 0;
dev->msg = msgs;
while (rem) {
int filled = FALSE;
dev->wr_sz = dev->out_fifo_sz;
dev->err = 0;
if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN) != 0) {
ret = -EIO;
goto out_err;
}
qup_print_status(dev);
/* HW limits Read upto 256 bytes in 1 read without stop */
if (dev->msg->flags & I2C_M_RD) {
ret = qup_set_read_mode(dev, dev->cnt);
if (ret != 0)
goto out_err;
} else {
ret = qup_set_wr_mode(dev, rem);
if (ret != 0)
goto out_err;
/* Don't fill block till we get interrupt */
if (dev->wr_sz == dev->out_blk_sz)
filled = TRUE;
}
err = qup_update_state(dev, QUP_RUN_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
qup_print_status(dev);
writel(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL);
do {
int idx = 0;
unsigned carry_over = 0;
/* Transition to PAUSE state only possible from RUN */
err = qup_update_state(dev, QUP_PAUSE_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
qup_print_status(dev);
/* This operation is Write, check the next operation and decide
mode */
while (filled == FALSE) {
if ((msgs->flags & I2C_M_RD) && (dev->cnt == msgs->len))
qup_issue_read(dev, msgs, &idx, carry_over);
else if (!(msgs->flags & I2C_M_RD))
qup_issue_write(dev, msgs, rem, &idx, &carry_over);
if (idx >= (dev->wr_sz << 1))
filled = TRUE;
/* Start new message */
if (filled == FALSE) {
if (msgs->flags & I2C_M_RD)
filled = TRUE;
else if (rem > 1) {
/* Only combine operations with same address */
struct i2c_msg *next = msgs + 1;
if (next->addr != msgs->addr || next->flags == 0)
filled = TRUE;
else {
rem--;
msgs++;
dev->msg = msgs;
dev->pos = 0;
dev->cnt = msgs->len;
}
} else
filled = TRUE;
}
}
err = qup_update_state(dev, QUP_RUN_STATE);
if (err < 0) {
ret = err;
goto out_err;
}
dprintf(CRITICAL, "idx:%d, rem:%d, num:%d, mode:%d\n",
idx, rem, num, dev->mode);
qup_print_status(dev);
if (dev->err) {
if (dev->err & QUP_I2C_NACK_FLAG) {
dprintf(CRITICAL,
"I2C slave addr:0x%x not connected\n",
dev->msg->addr);
} else {
dprintf(INFO, "QUP data xfer error %d\n", dev->err);
}
ret = dev->err;
goto out_err;
}
if (dev->msg->flags & I2C_M_RD) {
int i;
unsigned dval = 0;
for (i = 0; dev->pos < dev->msg->len; i++, dev->pos++) {
unsigned rd_status = readl(dev->base + QUP_OPERATIONAL);
if (i % 2 == 0) {
if ((rd_status & QUP_IN_NOT_EMPTY) == 0)
break;
dval = readl(dev->base + QUP_IN_FIFO_BASE);
dev->msg->buf[dev->pos] = dval & 0xFF;
} else
dev->msg->buf[dev->pos] = ((dval & 0xFF0000) >> 16);
}
dev->cnt -= i;
} else
filled = FALSE; /* refill output FIFO */
} while (dev->cnt > 0);
if (dev->cnt == 0) {
rem--;
msgs++;
if (rem) {
dev->pos = 0;
dev->cnt = msgs->len;
dev->msg = msgs;
}
}
/* Wait for I2C bus to be idle */
ret = qup_i2c_poll_writeready(dev);
if (ret) {
dprintf(INFO, "Error waiting for write ready\n");
goto out_err;
}
}
ret = num;
out_err:
dev->msg = NULL;
dev->pos = 0;
dev->err = 0;
dev->cnt = 0;
mask_interrupt(dev->qup_irq);
return ret;
}
static int set_gsbi_number(struct qup_i2c_dev *dev)
{
switch (dev->base) {
case GSBI1_QUP_BASE:
dev->gsbi_number = 1;
break;
case GSBI2_QUP_BASE:
dev->gsbi_number = 2;
break;
case GSBI3_QUP_BASE:
dev->gsbi_number = 3;
break;
case GSBI4_QUP_BASE:
dev->gsbi_number = 4;
break;
case GSBI5_QUP_BASE:
dev->gsbi_number = 5;
break;
case GSBI6_QUP_BASE:
dev->gsbi_number = 6;
break;
case GSBI7_QUP_BASE:
dev->gsbi_number = 7;
break;
case GSBI8_QUP_BASE:
dev->gsbi_number = 8;
break;
case GSBI9_QUP_BASE:
dev->gsbi_number = 9;
break;
case GSBI10_QUP_BASE:
dev->gsbi_number = 10;
break;
case GSBI11_QUP_BASE:
dev->gsbi_number = 11;
break;
case GSBI12_QUP_BASE:
dev->gsbi_number = 12;
break;
default:
return 1;
}
return 0;
}
static int set_qup_irq(struct qup_i2c_dev *dev)
{
switch (dev->base) {
case GSBI1_QUP_BASE:
dev->qup_irq = GSBI1_QUP_IRQ;
break;
case GSBI2_QUP_BASE:
dev->qup_irq = GSBI2_QUP_IRQ;
break;
case GSBI3_QUP_BASE:
dev->qup_irq = GSBI3_QUP_IRQ;
break;
case GSBI4_QUP_BASE:
dev->qup_irq = GSBI4_QUP_IRQ;
break;
case GSBI5_QUP_BASE:
dev->qup_irq = GSBI5_QUP_IRQ;
break;
case GSBI6_QUP_BASE:
dev->qup_irq = GSBI6_QUP_IRQ;
break;
case GSBI7_QUP_BASE:
dev->qup_irq = GSBI7_QUP_IRQ;
break;
case GSBI8_QUP_BASE:
dev->qup_irq = GSBI8_QUP_IRQ;
break;
case GSBI9_QUP_BASE:
dev->qup_irq = GSBI9_QUP_IRQ;
break;
case GSBI10_QUP_BASE:
dev->qup_irq = GSBI10_QUP_IRQ;
break;
case GSBI11_QUP_BASE:
dev->qup_irq = GSBI11_QUP_IRQ;
break;
case GSBI12_QUP_BASE:
dev->qup_irq = GSBI12_QUP_IRQ;
break;
default:
return 1;
}
return 0;
}
struct qup_i2c_dev *qup_i2c_init(unsigned base,
unsigned clk_freq, unsigned src_clk_freq)
{
struct qup_i2c_dev *dev;
if (dev_addr != NULL) {
return dev_addr;
}
dev = malloc(sizeof(struct qup_i2c_dev));
if (!dev) {
return NULL;
}
dev = memset(dev, 0, sizeof(struct qup_i2c_dev));
/* This must be done for qup_i2c_interrupt to work. */
dev_addr = dev;
/* Initialize the GPIO for GSBIn as i2c */
i2c_gpio_cfg(base);
/* Configure GSBIn in i2c mode */
writel(GSBI_CTL_PROTOCOL_CODE_I2C, base);
/* Set the base address for GSBIn QUP The reason we add 0x80000 is to make
the GSBIn base address be the GSBIn QUP base address, which is what the
i2c driver wants. */
dev->base = base + 0x80000;
/* Set clk_freq and src_clk_freq for i2c. */
dev->clk_freq = clk_freq;
dev->src_clk_freq = src_clk_freq;
dev->num_irqs = 1;
dev->one_bit_t = USEC_PER_SEC / dev->clk_freq;
dev->clk_ctl = 0;
/* Set the IRQ number for GSBIn_BASE address */
if (set_qup_irq(dev)) {
dprintf(INFO,
"Could not find a valid QUP IRQ value based on GSBIn_BASE: %d\n",
base);
dprintf(INFO, "Please double check the GSBIn_BASE address.\n");
return NULL;
}
/* Set the GSBI number based on GSBIn_BASE address */
if (set_gsbi_number(dev)) {
dprintf(INFO, "Could not find a valid GSBI # based on GSBIn_BASE: %d\n",
base);
dprintf(INFO, "Please double check the GSBIn_BASE address.\n");
return NULL;
}
/* Register the GSBIn QUP IRQ */
register_int_handler(dev->qup_irq, qup_i2c_interrupt, 0);
/* Then disable it */
mask_interrupt(dev->qup_irq);
return dev;
}
int qup_i2c_deinit(struct qup_i2c_dev *dev)
{
/* Disable the qup_irq */
mask_interrupt(dev->qup_irq);
/* Free the memory used for dev */
free(dev);
return 0;
}