blob: 4b525b69488a7128fba1daddfc80a150f45d1bf6 [file] [log] [blame]
/* Copyright (c) 2010-2013, The Linux Foundation. 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 The Linux Foundation, 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 <dev/gpio.h>
#include <stdlib.h>
#include <string.h>
#include <gsbi.h>
#include <i2c_qup.h>
#include <platform/irqs.h>
#include <platform/iomap.h>
#include <platform/gpio.h>
#include <platform/clock.h>
#include <platform/timer.h>
#include <platform/interrupts.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,
};
#ifdef DEBUG_QUP
static void qup_print_status(struct qup_i2c_dev *dev)
{
unsigned val;
val = readl(dev->qup_base + QUP_CONFIG);
dprintf(INFO, "Qup config is :0x%x\n", val);
val = readl(dev->qup_base + QUP_STATE);
dprintf(INFO, "Qup state is :0x%x\n", val);
val = readl(dev->qup_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
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->qup_base + QUP_I2C_STATUS);
unsigned status1 = readl(dev->qup_base + QUP_ERROR_FLAGS);
unsigned op_flgs = readl(dev->qup_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->qup_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->qup_base + QUP_ERROR_FLAGS);
goto intr_done;
}
if (op_flgs & QUP_OUT_SVC_FLAG)
writel(QUP_OUT_SVC_FLAG, dev->qup_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->qup_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->qup_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(INFO, "Polling Status for state:0x%x\n", state);
while (retries != 2000) {
unsigned status = readl(dev->qup_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->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, carry_over |
((QUP_OUT_START | addr) << 16),
(unsigned)dev->qup_base + QUP_OUT_FIFO_BASE +
(*idx - 2), 1);
writel((QUP_OUT_REC | rd_len),
dev->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, (QUP_OUT_REC | rd_len),
(unsigned)dev->qup_base + QUP_OUT_FIFO_BASE +
(*idx + 2), 1);
} else {
writel(((QUP_OUT_REC | rd_len) << 16) |
QUP_OUT_START | addr, dev->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, QUP_OUT_REC << 16 | rd_len << 16 |
QUP_OUT_START | addr,
(unsigned)dev->qup_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->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, *carry_over | QUP_OUT_DATA << 16 |
addr << 16, (unsigned)dev->qup_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->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 |
msg->buf[dev->pos] << 16,
(unsigned)dev->qup_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->qup_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->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, last_entry | msg->buf[dev->pos],
(unsigned)dev->qup_base +
QUP_OUT_FIFO_BASE + (*idx), 0);
}
} else {
writel(val | ((last_entry | msg->buf[dev->pos]) << 16),
dev->qup_base + QUP_OUT_FIFO_BASE);
qup_verify_fifo(dev, val | (last_entry << 16) |
(msg->buf[dev->pos] << 16),
(unsigned)dev->qup_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->qup_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->qup_base + QUP_IO_MODE);
writel(rd_len, dev->qup_base + QUP_MX_READ_CNT);
} else {
writel(wr_mode | QUP_RD_BLK_MODE |
QUP_PACK_EN | QUP_UNPACK_EN,
dev->qup_base + QUP_IO_MODE);
writel(rd_len, dev->qup_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->qup_base + QUP_IO_MODE);
dev->wr_sz = dev->out_blk_sz;
} else
writel(QUP_PACK_EN | QUP_UNPACK_EN,
dev->qup_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->qup_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 !PERIPH_BLK_BLSP
if (dev->clk_state == 0) {
if (dev->clk_ctl == 0) {
clock_config_i2c(dev->gsbi_number, dev->src_clk_freq);
}
}
#endif
/* Initialize QUP registers during first transfer */
if (dev->clk_ctl == 0) {
int fs_div;
int hs_div;
unsigned fifo_reg;
#if !PERIPH_BLK_BLSP
/* Configure the GSBI Protocol Code for i2c */
writel((GSBI_PROTOCOL_CODE_I2C <<
GSBI_CTRL_REG_PROTOCOL_CODE_S),
GSBI_CTRL_REG(dev->gsbi_base));
#endif
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->qup_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(INFO, "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->qup_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->qup_base + QUP_CONFIG);
writel(QUP_OPERATIONAL_RESET, dev->qup_base + QUP_OPERATIONAL);
writel(QUP_STATUS_ERROR_FLAGS, dev->qup_base + QUP_ERROR_FLAGS_EN);
writel(I2C_MINI_CORE | I2C_N_VAL, dev->qup_base + QUP_CONFIG);
/* Initialize I2C mini core registers */
writel(0, dev->qup_base + QUP_I2C_CLK_CTL);
writel(QUP_I2C_STATUS_RESET, dev->qup_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->qup_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(INFO, "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->qup_base +
QUP_OPERATIONAL);
if (i % 2 == 0) {
if ((rd_status &
QUP_IN_NOT_EMPTY) == 0)
break;
dval =
readl(dev->qup_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;
}
void qup_i2c_sec_init(struct qup_i2c_dev *dev, uint32_t clk_freq,
uint32_t src_clk_freq)
{
/* 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;
/* Register the GSBIn QUP IRQ */
register_int_handler(dev->qup_irq, (int_handler) qup_i2c_interrupt, 0);
/* Then disable it */
mask_interrupt(dev->qup_irq);
}
struct qup_i2c_dev *qup_i2c_init(uint8_t gsbi_id, 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));
/*
* Platform uses gsbi, setup base addresses and
* irq based on gsbi_id
*/
dev->qup_irq = GSBI_QUP_IRQ(gsbi_id);
dev->qup_base = QUP_BASE(gsbi_id);
dev->gsbi_base = GSBI_BASE(gsbi_id);
dev->gsbi_number = gsbi_id;
/* This must be done for qup_i2c_interrupt to work. */
dev_addr = dev;
/* Initialize the GPIO for GSBIn as i2c */
gpio_config_i2c(dev->gsbi_number);
/* Configure the GSBI Protocol Code for i2c */
writel((GSBI_PROTOCOL_CODE_I2C <<
GSBI_CTRL_REG_PROTOCOL_CODE_S), GSBI_CTRL_REG(dev->gsbi_base));
qup_i2c_sec_init(dev, clk_freq, src_clk_freq);
return dev;
}
struct qup_i2c_dev *qup_blsp_i2c_init(uint8_t blsp_id, uint8_t qup_id,
uint32_t clk_freq, uint32_t 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));
/* Platform uses BLSP */
dev->qup_irq = BLSP_QUP_IRQ(blsp_id, qup_id);
dev->qup_base = BLSP_QUP_BASE(blsp_id, qup_id);
/* This must be done for qup_i2c_interrupt to work. */
dev_addr = dev;
/* Initialize the GPIO for BLSP i2c */
gpio_config_blsp_i2c(blsp_id, qup_id);
clock_config_blsp_i2c(blsp_id, qup_id);
qup_i2c_sec_init(dev, clk_freq, src_clk_freq);
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;
}