Skip to content

Commit

Permalink
Merge branch 'i2c-embedded/for-current' of git://git.pengutronix.de/g…
Browse files Browse the repository at this point in the history
…it/wsa/linux

Pull i2c embedded fixes from Wolfram Sang:
 "Two patches are usual stuff.

  The bigger patch is needed to correct a wrong decision made in this
  merge window.  We hoped to get the PIOQUEUE mode in the mxs driver
  working with DMA, but it turned out to be too broken (leading to data
  loss), so we now think it is best to remove it entirely and work only
  with DMA now.  The patch should be in 3.7.  IMO, so users never get
  the chance to use both modes in parallel."

* 'i2c-embedded/for-current' of git://git.pengutronix.de/git/wsa/linux:
  i2c: tegra: set irq name as device name
  i2c-nomadik: Fixup clock handling
  i2c: mxs: remove broken PIOQUEUE support
  • Loading branch information
Linus Torvalds committed Nov 3, 2012
2 parents 53f9313 + 91b370a commit 209c510
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 175 deletions.
186 changes: 14 additions & 172 deletions drivers/i2c/busses/i2c-mxs.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Freescale MXS I2C bus driver
*
* Copyright (C) 2011 Wolfram Sang, Pengutronix e.K.
* Copyright (C) 2011-2012 Wolfram Sang, Pengutronix e.K.
*
* based on a (non-working) driver which was:
*
Expand Down Expand Up @@ -35,10 +35,6 @@

#define DRIVER_NAME "mxs-i2c"

static bool use_pioqueue;
module_param(use_pioqueue, bool, 0);
MODULE_PARM_DESC(use_pioqueue, "Use PIOQUEUE mode for transfer instead of DMA");

#define MXS_I2C_CTRL0 (0x00)
#define MXS_I2C_CTRL0_SET (0x04)

Expand Down Expand Up @@ -75,23 +71,6 @@ MODULE_PARM_DESC(use_pioqueue, "Use PIOQUEUE mode for transfer instead of DMA");
MXS_I2C_CTRL1_SLAVE_STOP_IRQ | \
MXS_I2C_CTRL1_SLAVE_IRQ)

#define MXS_I2C_QUEUECTRL (0x60)
#define MXS_I2C_QUEUECTRL_SET (0x64)
#define MXS_I2C_QUEUECTRL_CLR (0x68)

#define MXS_I2C_QUEUECTRL_QUEUE_RUN 0x20
#define MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE 0x04

#define MXS_I2C_QUEUESTAT (0x70)
#define MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY 0x00002000
#define MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK 0x0000001F

#define MXS_I2C_QUEUECMD (0x80)

#define MXS_I2C_QUEUEDATA (0x90)

#define MXS_I2C_DATA (0xa0)


#define MXS_CMD_I2C_SELECT (MXS_I2C_CTRL0_RETAIN_CLOCK | \
MXS_I2C_CTRL0_PRE_SEND_START | \
Expand Down Expand Up @@ -153,7 +132,6 @@ struct mxs_i2c_dev {
const struct mxs_i2c_speed_config *speed;

/* DMA support components */
bool dma_mode;
int dma_channel;
struct dma_chan *dmach;
struct mxs_dma_data dma_data;
Expand All @@ -172,99 +150,6 @@ static void mxs_i2c_reset(struct mxs_i2c_dev *i2c)
writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2);

writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
if (i2c->dma_mode)
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
i2c->regs + MXS_I2C_QUEUECTRL_CLR);
else
writel(MXS_I2C_QUEUECTRL_PIO_QUEUE_MODE,
i2c->regs + MXS_I2C_QUEUECTRL_SET);
}

static void mxs_i2c_pioq_setup_read(struct mxs_i2c_dev *i2c, u8 addr, int len,
int flags)
{
u32 data;

writel(MXS_CMD_I2C_SELECT, i2c->regs + MXS_I2C_QUEUECMD);

data = (addr << 1) | I2C_SMBUS_READ;
writel(data, i2c->regs + MXS_I2C_DATA);

data = MXS_CMD_I2C_READ | MXS_I2C_CTRL0_XFER_COUNT(len) | flags;
writel(data, i2c->regs + MXS_I2C_QUEUECMD);
}

static void mxs_i2c_pioq_setup_write(struct mxs_i2c_dev *i2c,
u8 addr, u8 *buf, int len, int flags)
{
u32 data;
int i, shifts_left;

data = MXS_CMD_I2C_WRITE | MXS_I2C_CTRL0_XFER_COUNT(len + 1) | flags;
writel(data, i2c->regs + MXS_I2C_QUEUECMD);

/*
* We have to copy the slave address (u8) and buffer (arbitrary number
* of u8) into the data register (u32). To achieve that, the u8 are put
* into the MSBs of 'data' which is then shifted for the next u8. When
* appropriate, 'data' is written to MXS_I2C_DATA. So, the first u32
* looks like this:
*
* 3 2 1 0
* 10987654|32109876|54321098|76543210
* --------+--------+--------+--------
* buffer+2|buffer+1|buffer+0|slave_addr
*/

data = ((addr << 1) | I2C_SMBUS_WRITE) << 24;

for (i = 0; i < len; i++) {
data >>= 8;
data |= buf[i] << 24;
if ((i & 3) == 2)
writel(data, i2c->regs + MXS_I2C_DATA);
}

/* Write out the remaining bytes if any */
shifts_left = 24 - (i & 3) * 8;
if (shifts_left)
writel(data >> shifts_left, i2c->regs + MXS_I2C_DATA);
}

/*
* TODO: should be replaceable with a waitqueue and RD_QUEUE_IRQ (setting the
* rd_threshold to 1). Couldn't get this to work, though.
*/
static int mxs_i2c_wait_for_data(struct mxs_i2c_dev *i2c)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);

while (readl(i2c->regs + MXS_I2C_QUEUESTAT)
& MXS_I2C_QUEUESTAT_RD_QUEUE_EMPTY) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
cond_resched();
}

return 0;
}

static int mxs_i2c_finish_read(struct mxs_i2c_dev *i2c, u8 *buf, int len)
{
u32 uninitialized_var(data);
int i;

for (i = 0; i < len; i++) {
if ((i & 3) == 0) {
if (mxs_i2c_wait_for_data(i2c))
return -ETIMEDOUT;
data = readl(i2c->regs + MXS_I2C_QUEUEDATA);
}
buf[i] = data & 0xff;
data >>= 8;
}

return 0;
}

static void mxs_i2c_dma_finish(struct mxs_i2c_dev *i2c)
Expand Down Expand Up @@ -432,48 +317,25 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
init_completion(&i2c->cmd_complete);
i2c->cmd_err = 0;

if (i2c->dma_mode) {
ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
if (ret)
return ret;
} else {
if (msg->flags & I2C_M_RD) {
mxs_i2c_pioq_setup_read(i2c, msg->addr,
msg->len, flags);
} else {
mxs_i2c_pioq_setup_write(i2c, msg->addr, msg->buf,
msg->len, flags);
}

writel(MXS_I2C_QUEUECTRL_QUEUE_RUN,
i2c->regs + MXS_I2C_QUEUECTRL_SET);
}
ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
if (ret)
return ret;

ret = wait_for_completion_timeout(&i2c->cmd_complete,
msecs_to_jiffies(1000));
if (ret == 0)
goto timeout;

if (!i2c->dma_mode && !i2c->cmd_err && (msg->flags & I2C_M_RD)) {
ret = mxs_i2c_finish_read(i2c, msg->buf, msg->len);
if (ret)
goto timeout;
}

if (i2c->cmd_err == -ENXIO)
mxs_i2c_reset(i2c);
else
writel(MXS_I2C_QUEUECTRL_QUEUE_RUN,
i2c->regs + MXS_I2C_QUEUECTRL_CLR);

dev_dbg(i2c->dev, "Done with err=%d\n", i2c->cmd_err);

return i2c->cmd_err;

timeout:
dev_dbg(i2c->dev, "Timeout!\n");
if (i2c->dma_mode)
mxs_i2c_dma_finish(i2c);
mxs_i2c_dma_finish(i2c);
mxs_i2c_reset(i2c);
return -ETIMEDOUT;
}
Expand Down Expand Up @@ -502,7 +364,6 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
{
struct mxs_i2c_dev *i2c = dev_id;
u32 stat = readl(i2c->regs + MXS_I2C_CTRL1) & MXS_I2C_IRQ_MASK;
bool is_last_cmd;

if (!stat)
return IRQ_NONE;
Expand All @@ -515,14 +376,6 @@ static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
/* MXS_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ is only for slaves */
i2c->cmd_err = -EIO;

if (!i2c->dma_mode) {
is_last_cmd = (readl(i2c->regs + MXS_I2C_QUEUESTAT) &
MXS_I2C_QUEUESTAT_WRITE_QUEUE_CNT_MASK) == 0;

if (is_last_cmd || i2c->cmd_err)
complete(&i2c->cmd_complete);
}

writel(stat, i2c->regs + MXS_I2C_CTRL1_CLR);

return IRQ_HANDLED;
Expand Down Expand Up @@ -555,24 +408,15 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
struct device_node *node = dev->of_node;
int ret;

/*
* The MXS I2C DMA mode is prefered and enabled by default.
* The PIO mode is still supported, but should be used only
* for debuging purposes etc.
*/
i2c->dma_mode = !use_pioqueue;
if (!i2c->dma_mode)
dev_info(dev, "Using PIOQUEUE mode for I2C transfers!\n");

/*
* TODO: This is a temporary solution and should be changed
* to use generic DMA binding later when the helpers get in.
*/
ret = of_property_read_u32(node, "fsl,i2c-dma-channel",
&i2c->dma_channel);
if (ret) {
dev_warn(dev, "Failed to get DMA channel, using PIOQUEUE!\n");
i2c->dma_mode = 0;
dev_err(dev, "Failed to get DMA channel!\n");
return -ENODEV;
}

ret = of_property_read_u32(node, "clock-frequency", &speed);
Expand Down Expand Up @@ -634,15 +478,13 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
}

/* Setup the DMA */
if (i2c->dma_mode) {
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
i2c->dma_data.chan_irq = dmairq;
i2c->dmach = dma_request_channel(mask, mxs_i2c_dma_filter, i2c);
if (!i2c->dmach) {
dev_err(dev, "Failed to request dma\n");
return -ENODEV;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
i2c->dma_data.chan_irq = dmairq;
i2c->dmach = dma_request_channel(mask, mxs_i2c_dma_filter, i2c);
if (!i2c->dmach) {
dev_err(dev, "Failed to request dma\n");
return -ENODEV;
}

platform_set_drvdata(pdev, i2c);
Expand Down
9 changes: 7 additions & 2 deletions drivers/i2c/busses/i2c-nomadik.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,11 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,

pm_runtime_get_sync(&dev->adev->dev);

clk_enable(dev->clk);
status = clk_prepare_enable(dev->clk);
if (status) {
dev_err(&dev->adev->dev, "can't prepare_enable clock\n");
goto out_clk;
}

status = init_hw(dev);
if (status)
Expand All @@ -671,7 +675,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
}

out:
clk_disable(dev->clk);
clk_disable_unprepare(dev->clk);
out_clk:
pm_runtime_put_sync(&dev->adev->dev);

dev->busy = false;
Expand Down
2 changes: 1 addition & 1 deletion drivers/i2c/busses/i2c-tegra.c
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
}

ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
tegra_i2c_isr, 0, pdev->name, i2c_dev);
tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
return ret;
Expand Down

0 comments on commit 209c510

Please sign in to comment.