Merge branch 'for-linus/2640/i2c' of git://git.fluff.org/bjdooks/linux
* 'for-linus/2640/i2c' of git://git.fluff.org/bjdooks/linux: (21 commits) mach-ux500: set proper I2C platform data from MOP500s i2c-nomadik: break out single messsage transmission i2c-nomadik: reset the hw after status check i2c-nomadik: remove the unnecessary delay i2c-nomadik: change the TX and RX threshold i2c-nomadik: add code to retry on timeout failure i2c-nomadik: use pm_runtime API i2c-nomadik: print abort cause only on abort tag i2c-nomadik: correct adapter timeout initialization i2c-nomadik: remove the redundant error message i2c-nomadik: corrrect returned error numbers i2c-nomadik: fix speed enumerator i2c-nomadik: make i2c timeout specific per i2c bus i2c-nomadik: add regulator support i2c: i2c-sh_mobile bus speed platform data V2 i2c: i2c-sh_mobile clock string removal i2c-eg20t: Support new device ML7223 IOH i2c: tegra: Add de-bounce cycles. i2c: tegra: fix repeated start handling i2c: tegra: recover from spurious interrupt storm ...
This commit is contained in:
commit
4a7df24ddc
@ -204,7 +204,7 @@ static struct i2c_board_info __initdata mop500_i2c2_devices[] = {
|
||||
},
|
||||
};
|
||||
|
||||
#define U8500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, _sm) \
|
||||
#define U8500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, t_out, _sm) \
|
||||
static struct nmk_i2c_controller u8500_i2c##id##_data = { \
|
||||
/* \
|
||||
* slave data setup time, which is \
|
||||
@ -219,19 +219,21 @@ static struct nmk_i2c_controller u8500_i2c##id##_data = { \
|
||||
.rft = _rft, \
|
||||
/* std. mode operation */ \
|
||||
.clk_freq = clk, \
|
||||
/* Slave response timeout(ms) */\
|
||||
.timeout = t_out, \
|
||||
.sm = _sm, \
|
||||
}
|
||||
|
||||
/*
|
||||
* The board uses 4 i2c controllers, initialize all of
|
||||
* them with slave data setup time of 250 ns,
|
||||
* Tx & Rx FIFO threshold values as 1 and standard
|
||||
* Tx & Rx FIFO threshold values as 8 and standard
|
||||
* mode of operation
|
||||
*/
|
||||
U8500_I2C_CONTROLLER(0, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
U8500_I2C_CONTROLLER(1, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
U8500_I2C_CONTROLLER(2, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
U8500_I2C_CONTROLLER(3, 0xe, 1, 1, 100000, I2C_FREQ_MODE_STANDARD);
|
||||
U8500_I2C_CONTROLLER(0, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
|
||||
U8500_I2C_CONTROLLER(1, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
|
||||
U8500_I2C_CONTROLLER(2, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
|
||||
U8500_I2C_CONTROLLER(3, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
|
||||
|
||||
static void __init mop500_i2c_init(void)
|
||||
{
|
||||
|
@ -11,8 +11,8 @@
|
||||
enum i2c_freq_mode {
|
||||
I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */
|
||||
I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */
|
||||
I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */
|
||||
I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */
|
||||
I2C_FREQ_MODE_HIGH_SPEED /* up to 3.4 Mb/s */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -24,13 +24,15 @@ enum i2c_freq_mode {
|
||||
* to the values of 14, 6, 2 for a 48 MHz i2c clk
|
||||
* @tft: Tx FIFO Threshold in bytes
|
||||
* @rft: Rx FIFO Threshold in bytes
|
||||
* @timeout Slave response timeout(ms)
|
||||
* @sm: speed mode
|
||||
*/
|
||||
struct nmk_i2c_controller {
|
||||
unsigned long clk_freq;
|
||||
unsigned short slsu;
|
||||
unsigned char tft;
|
||||
unsigned char rft;
|
||||
unsigned char tft;
|
||||
unsigned char rft;
|
||||
int timeout;
|
||||
enum i2c_freq_mode sm;
|
||||
};
|
||||
|
||||
|
@ -673,15 +673,19 @@ config I2C_XILINX
|
||||
will be called xilinx_i2c.
|
||||
|
||||
config I2C_EG20T
|
||||
tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH"
|
||||
tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223)"
|
||||
depends on PCI
|
||||
help
|
||||
This driver is for PCH(Platform controller Hub) I2C of EG20T which
|
||||
is an IOH(Input/Output Hub) for x86 embedded processor.
|
||||
This driver can access PCH I2C bus device.
|
||||
|
||||
This driver also supports the ML7213, a companion chip for the
|
||||
Atom E6xx series and compatible with the Intel EG20T PCH.
|
||||
This driver also can be used for OKI SEMICONDUCTOR IOH(Input/
|
||||
Output Hub), ML7213 and ML7223.
|
||||
ML7213 IOH is for IVI(In-Vehicle Infotainment) use and ML7223 IOH is
|
||||
for MP(Media Phone) use.
|
||||
ML7213/ML7223 is companion chip for Intel Atom E6xx series.
|
||||
ML7213/ML7223 is completely compatible for Intel EG20T PCH.
|
||||
|
||||
comment "External I2C/SMBus adapter drivers"
|
||||
|
||||
|
@ -182,10 +182,12 @@ static DEFINE_MUTEX(pch_mutex);
|
||||
/* Definition for ML7213 by OKI SEMICONDUCTOR */
|
||||
#define PCI_VENDOR_ID_ROHM 0x10DB
|
||||
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
|
||||
#define PCI_DEVICE_ID_ML7223_I2C 0x8010
|
||||
|
||||
static struct pci_device_id __devinitdata pch_pcidev_id[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, },
|
||||
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, },
|
||||
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7223_I2C), 1, },
|
||||
{0,}
|
||||
};
|
||||
|
||||
|
@ -15,13 +15,14 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <plat/i2c.h>
|
||||
|
||||
@ -103,9 +104,6 @@
|
||||
/* maximum threshold value */
|
||||
#define MAX_I2C_FIFO_THRESHOLD 15
|
||||
|
||||
/* per-transfer delay, required for the hardware to stabilize */
|
||||
#define I2C_DELAY 150
|
||||
|
||||
enum i2c_status {
|
||||
I2C_NOP,
|
||||
I2C_ON_GOING,
|
||||
@ -120,9 +118,6 @@ enum i2c_operation {
|
||||
I2C_READ = 0x01
|
||||
};
|
||||
|
||||
/* controller response timeout in ms */
|
||||
#define I2C_TIMEOUT_MS 2000
|
||||
|
||||
/**
|
||||
* struct i2c_nmk_client - client specific data
|
||||
* @slave_adr: 7-bit slave address
|
||||
@ -151,6 +146,7 @@ struct i2c_nmk_client {
|
||||
* @stop: stop condition
|
||||
* @xfer_complete: acknowledge completion for a I2C message
|
||||
* @result: controller propogated result
|
||||
* @busy: Busy doing transfer
|
||||
*/
|
||||
struct nmk_i2c_dev {
|
||||
struct platform_device *pdev;
|
||||
@ -163,6 +159,8 @@ struct nmk_i2c_dev {
|
||||
int stop;
|
||||
struct completion xfer_complete;
|
||||
int result;
|
||||
struct regulator *regulator;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
/* controller's abort causes */
|
||||
@ -209,7 +207,7 @@ static int flush_i2c_fifo(struct nmk_i2c_dev *dev)
|
||||
writel((I2C_CR_FTX | I2C_CR_FRX), dev->virtbase + I2C_CR);
|
||||
|
||||
for (i = 0; i < LOOP_ATTEMPTS; i++) {
|
||||
timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MS);
|
||||
timeout = jiffies + dev->adap.timeout;
|
||||
|
||||
while (!time_after(jiffies, timeout)) {
|
||||
if ((readl(dev->virtbase + I2C_CR) &
|
||||
@ -253,11 +251,9 @@ static int init_hw(struct nmk_i2c_dev *dev)
|
||||
{
|
||||
int stat;
|
||||
|
||||
clk_enable(dev->clk);
|
||||
|
||||
stat = flush_i2c_fifo(dev);
|
||||
if (stat)
|
||||
return stat;
|
||||
goto exit;
|
||||
|
||||
/* disable the controller */
|
||||
i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
|
||||
@ -268,10 +264,8 @@ static int init_hw(struct nmk_i2c_dev *dev)
|
||||
|
||||
dev->cli.operation = I2C_NO_OPERATION;
|
||||
|
||||
clk_disable(dev->clk);
|
||||
|
||||
udelay(I2C_DELAY);
|
||||
return 0;
|
||||
exit:
|
||||
return stat;
|
||||
}
|
||||
|
||||
/* enable peripheral, master mode operation */
|
||||
@ -424,7 +418,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
|
||||
dev->virtbase + I2C_IMSCR);
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(
|
||||
&dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
|
||||
&dev->xfer_complete, dev->adap.timeout);
|
||||
|
||||
if (timeout < 0) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
@ -434,14 +428,32 @@ static int read_i2c(struct nmk_i2c_dev *dev)
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
/* controller has timedout, re-init the h/w */
|
||||
dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
|
||||
(void) init_hw(dev);
|
||||
/* Controller timed out */
|
||||
dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n",
|
||||
dev->cli.slave_adr);
|
||||
status = -ETIMEDOUT;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void fill_tx_fifo(struct nmk_i2c_dev *dev, int no_bytes)
|
||||
{
|
||||
int count;
|
||||
|
||||
for (count = (no_bytes - 2);
|
||||
(count > 0) &&
|
||||
(dev->cli.count != 0);
|
||||
count--) {
|
||||
/* write to the Tx FIFO */
|
||||
writeb(*dev->cli.buffer,
|
||||
dev->virtbase + I2C_TFR);
|
||||
dev->cli.buffer++;
|
||||
dev->cli.count--;
|
||||
dev->cli.xfer_bytes++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* write_i2c() - Write data to I2C client.
|
||||
* @dev: private data of I2C Driver
|
||||
@ -469,8 +481,13 @@ static int write_i2c(struct nmk_i2c_dev *dev)
|
||||
init_completion(&dev->xfer_complete);
|
||||
|
||||
/* enable interrupts by settings the masks */
|
||||
irq_mask = (I2C_IT_TXFNE | I2C_IT_TXFOVR |
|
||||
I2C_IT_MAL | I2C_IT_BERR);
|
||||
irq_mask = (I2C_IT_TXFOVR | I2C_IT_MAL | I2C_IT_BERR);
|
||||
|
||||
/* Fill the TX FIFO with transmit data */
|
||||
fill_tx_fifo(dev, MAX_I2C_FIFO_THRESHOLD);
|
||||
|
||||
if (dev->cli.count != 0)
|
||||
irq_mask |= I2C_IT_TXFNE;
|
||||
|
||||
/*
|
||||
* check if we want to transfer a single or multiple bytes, if so
|
||||
@ -488,7 +505,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
|
||||
dev->virtbase + I2C_IMSCR);
|
||||
|
||||
timeout = wait_for_completion_interruptible_timeout(
|
||||
&dev->xfer_complete, msecs_to_jiffies(I2C_TIMEOUT_MS));
|
||||
&dev->xfer_complete, dev->adap.timeout);
|
||||
|
||||
if (timeout < 0) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
@ -498,15 +515,60 @@ static int write_i2c(struct nmk_i2c_dev *dev)
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
/* controller has timedout, re-init the h/w */
|
||||
dev_err(&dev->pdev->dev, "controller timed out, re-init h/w\n");
|
||||
(void) init_hw(dev);
|
||||
/* Controller timed out */
|
||||
dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n",
|
||||
dev->cli.slave_adr);
|
||||
status = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* nmk_i2c_xfer_one() - transmit a single I2C message
|
||||
* @dev: device with a message encoded into it
|
||||
* @flags: message flags
|
||||
*/
|
||||
static int nmk_i2c_xfer_one(struct nmk_i2c_dev *dev, u16 flags)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (flags & I2C_M_RD) {
|
||||
/* read operation */
|
||||
dev->cli.operation = I2C_READ;
|
||||
status = read_i2c(dev);
|
||||
} else {
|
||||
/* write operation */
|
||||
dev->cli.operation = I2C_WRITE;
|
||||
status = write_i2c(dev);
|
||||
}
|
||||
|
||||
if (status || (dev->result)) {
|
||||
u32 i2c_sr;
|
||||
u32 cause;
|
||||
|
||||
i2c_sr = readl(dev->virtbase + I2C_SR);
|
||||
/*
|
||||
* Check if the controller I2C operation status
|
||||
* is set to ABORT(11b).
|
||||
*/
|
||||
if (((i2c_sr >> 2) & 0x3) == 0x3) {
|
||||
/* get the abort cause */
|
||||
cause = (i2c_sr >> 4) & 0x7;
|
||||
dev_err(&dev->pdev->dev, "%s\n", cause
|
||||
>= ARRAY_SIZE(abort_causes) ?
|
||||
"unknown reason" :
|
||||
abort_causes[cause]);
|
||||
}
|
||||
|
||||
(void) init_hw(dev);
|
||||
|
||||
status = status ? status : dev->result;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* nmk_i2c_xfer() - I2C transfer function used by kernel framework
|
||||
* @i2c_adap: Adapter pointer to the controller
|
||||
@ -559,53 +621,55 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
{
|
||||
int status;
|
||||
int i;
|
||||
u32 cause;
|
||||
struct nmk_i2c_dev *dev = i2c_get_adapdata(i2c_adap);
|
||||
int j;
|
||||
|
||||
status = init_hw(dev);
|
||||
if (status)
|
||||
return status;
|
||||
dev->busy = true;
|
||||
|
||||
if (dev->regulator)
|
||||
regulator_enable(dev->regulator);
|
||||
pm_runtime_get_sync(&dev->pdev->dev);
|
||||
|
||||
clk_enable(dev->clk);
|
||||
|
||||
/* setup the i2c controller */
|
||||
setup_i2c_controller(dev);
|
||||
status = init_hw(dev);
|
||||
if (status)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < num_msgs; i++) {
|
||||
if (unlikely(msgs[i].flags & I2C_M_TEN)) {
|
||||
dev_err(&dev->pdev->dev, "10 bit addressing"
|
||||
"not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev->cli.slave_adr = msgs[i].addr;
|
||||
dev->cli.buffer = msgs[i].buf;
|
||||
dev->cli.count = msgs[i].len;
|
||||
dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
|
||||
dev->result = 0;
|
||||
/* Attempt three times to send the message queue */
|
||||
for (j = 0; j < 3; j++) {
|
||||
/* setup the i2c controller */
|
||||
setup_i2c_controller(dev);
|
||||
|
||||
if (msgs[i].flags & I2C_M_RD) {
|
||||
/* it is a read operation */
|
||||
dev->cli.operation = I2C_READ;
|
||||
status = read_i2c(dev);
|
||||
} else {
|
||||
/* write operation */
|
||||
dev->cli.operation = I2C_WRITE;
|
||||
status = write_i2c(dev);
|
||||
for (i = 0; i < num_msgs; i++) {
|
||||
if (unlikely(msgs[i].flags & I2C_M_TEN)) {
|
||||
dev_err(&dev->pdev->dev, "10 bit addressing"
|
||||
"not supported\n");
|
||||
|
||||
status = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
dev->cli.slave_adr = msgs[i].addr;
|
||||
dev->cli.buffer = msgs[i].buf;
|
||||
dev->cli.count = msgs[i].len;
|
||||
dev->stop = (i < (num_msgs - 1)) ? 0 : 1;
|
||||
dev->result = 0;
|
||||
|
||||
status = nmk_i2c_xfer_one(dev, msgs[i].flags);
|
||||
if (status != 0)
|
||||
break;
|
||||
}
|
||||
if (status || (dev->result)) {
|
||||
/* get the abort cause */
|
||||
cause = (readl(dev->virtbase + I2C_SR) >> 4) & 0x7;
|
||||
dev_err(&dev->pdev->dev, "error during I2C"
|
||||
"message xfer: %d\n", cause);
|
||||
dev_err(&dev->pdev->dev, "%s\n",
|
||||
cause >= ARRAY_SIZE(abort_causes)
|
||||
? "unknown reason" : abort_causes[cause]);
|
||||
clk_disable(dev->clk);
|
||||
return status;
|
||||
}
|
||||
udelay(I2C_DELAY);
|
||||
if (status == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
clk_disable(dev->clk);
|
||||
pm_runtime_put_sync(&dev->pdev->dev);
|
||||
if (dev->regulator)
|
||||
regulator_disable(dev->regulator);
|
||||
|
||||
dev->busy = false;
|
||||
|
||||
/* return the no. messages processed */
|
||||
if (status)
|
||||
@ -666,17 +730,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
*/
|
||||
disable_interrupts(dev, I2C_IT_TXFNE);
|
||||
} else {
|
||||
for (count = (MAX_I2C_FIFO_THRESHOLD - tft - 2);
|
||||
(count > 0) &&
|
||||
(dev->cli.count != 0);
|
||||
count--) {
|
||||
/* write to the Tx FIFO */
|
||||
writeb(*dev->cli.buffer,
|
||||
dev->virtbase + I2C_TFR);
|
||||
dev->cli.buffer++;
|
||||
dev->cli.count--;
|
||||
dev->cli.xfer_bytes++;
|
||||
}
|
||||
fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft));
|
||||
/*
|
||||
* if done, close the transfer by disabling the
|
||||
* corresponding TXFNE interrupt
|
||||
@ -729,16 +783,11 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTD);
|
||||
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MTDWS);
|
||||
|
||||
disable_interrupts(dev,
|
||||
(I2C_IT_TXFNE | I2C_IT_TXFE | I2C_IT_TXFF
|
||||
| I2C_IT_TXFOVR | I2C_IT_RXFNF
|
||||
| I2C_IT_RXFF | I2C_IT_RXFE));
|
||||
disable_all_interrupts(dev);
|
||||
clear_all_interrupts(dev);
|
||||
|
||||
if (dev->cli.count) {
|
||||
dev->result = -1;
|
||||
dev->result = -EIO;
|
||||
dev_err(&dev->pdev->dev, "%lu bytes still remain to be"
|
||||
"xfered\n", dev->cli.count);
|
||||
(void) init_hw(dev);
|
||||
@ -749,7 +798,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
|
||||
/* Master Arbitration lost interrupt */
|
||||
case I2C_IT_MAL:
|
||||
dev->result = -1;
|
||||
dev->result = -EIO;
|
||||
(void) init_hw(dev);
|
||||
|
||||
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL);
|
||||
@ -763,7 +812,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
* during the transaction.
|
||||
*/
|
||||
case I2C_IT_BERR:
|
||||
dev->result = -1;
|
||||
dev->result = -EIO;
|
||||
/* get the status */
|
||||
if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT)
|
||||
(void) init_hw(dev);
|
||||
@ -779,7 +828,7 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
* the Tx FIFO is full.
|
||||
*/
|
||||
case I2C_IT_TXFOVR:
|
||||
dev->result = -1;
|
||||
dev->result = -EIO;
|
||||
(void) init_hw(dev);
|
||||
|
||||
dev_err(&dev->pdev->dev, "Tx Fifo Over run\n");
|
||||
@ -805,6 +854,38 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int nmk_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
|
||||
|
||||
if (nmk_i2c->busy)
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nmk_i2c_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define nmk_i2c_suspend NULL
|
||||
#define nmk_i2c_resume NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We use noirq so that we suspend late and resume before the wakeup interrupt
|
||||
* to ensure that we do the !pm_runtime_suspended() check in resume before
|
||||
* there has been a regular pm runtime resume (via pm_runtime_get_sync()).
|
||||
*/
|
||||
static const struct dev_pm_ops nmk_i2c_pm = {
|
||||
.suspend_noirq = nmk_i2c_suspend,
|
||||
.resume_noirq = nmk_i2c_resume,
|
||||
};
|
||||
|
||||
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
@ -830,7 +911,7 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
|
||||
ret = -ENOMEM;
|
||||
goto err_no_mem;
|
||||
}
|
||||
|
||||
dev->busy = false;
|
||||
dev->pdev = pdev;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
@ -860,6 +941,15 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
dev->regulator = regulator_get(&pdev->dev, "v-i2c");
|
||||
if (IS_ERR(dev->regulator)) {
|
||||
dev_warn(&pdev->dev, "could not get i2c regulator\n");
|
||||
dev->regulator = NULL;
|
||||
}
|
||||
|
||||
pm_suspend_ignore_children(&pdev->dev, true);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
dev->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(dev->clk)) {
|
||||
dev_err(&pdev->dev, "could not get i2c clock\n");
|
||||
@ -872,6 +962,8 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
adap->algo = &nmk_i2c_algo;
|
||||
adap->timeout = pdata->timeout ? msecs_to_jiffies(pdata->timeout) :
|
||||
msecs_to_jiffies(20000);
|
||||
snprintf(adap->name, sizeof(adap->name),
|
||||
"Nomadik I2C%d at %lx", pdev->id, (unsigned long)res->start);
|
||||
|
||||
@ -887,12 +979,6 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
i2c_set_adapdata(adap, dev);
|
||||
|
||||
ret = init_hw(dev);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "error in initializing i2c hardware\n");
|
||||
goto err_init_hw;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "initialize %s on virtual "
|
||||
"base %p\n", adap->name, dev->virtbase);
|
||||
|
||||
@ -904,10 +990,12 @@ static int __devinit nmk_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_init_hw:
|
||||
err_add_adap:
|
||||
clk_put(dev->clk);
|
||||
err_no_clk:
|
||||
if (dev->regulator)
|
||||
regulator_put(dev->regulator);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
free_irq(dev->irq, dev);
|
||||
err_irq:
|
||||
iounmap(dev->virtbase);
|
||||
@ -938,6 +1026,9 @@ static int __devexit nmk_i2c_remove(struct platform_device *pdev)
|
||||
if (res)
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
clk_put(dev->clk);
|
||||
if (dev->regulator)
|
||||
regulator_put(dev->regulator);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(dev);
|
||||
|
||||
@ -948,6 +1039,7 @@ static struct platform_driver nmk_i2c_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &nmk_i2c_pm,
|
||||
},
|
||||
.probe = nmk_i2c_probe,
|
||||
.remove = __devexit_p(nmk_i2c_remove),
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c/i2c-sh_mobile.h>
|
||||
|
||||
/* Transmit operation: */
|
||||
/* */
|
||||
@ -117,7 +118,7 @@ struct sh_mobile_i2c_data {
|
||||
struct device *dev;
|
||||
void __iomem *reg;
|
||||
struct i2c_adapter adap;
|
||||
|
||||
unsigned long bus_speed;
|
||||
struct clk *clk;
|
||||
u_int8_t icic;
|
||||
u_int8_t iccl;
|
||||
@ -205,7 +206,7 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
|
||||
* We also round off the result.
|
||||
*/
|
||||
num = i2c_clk * 5;
|
||||
denom = NORMAL_SPEED * 9;
|
||||
denom = pd->bus_speed * 9;
|
||||
tmp = num * 10 / denom;
|
||||
if (tmp % 10 >= 5)
|
||||
pd->iccl = (u_int8_t)((num/denom) + 1);
|
||||
@ -574,10 +575,10 @@ static int sh_mobile_i2c_hook_irqs(struct platform_device *dev, int hook)
|
||||
|
||||
static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||
{
|
||||
struct i2c_sh_mobile_platform_data *pdata = dev->dev.platform_data;
|
||||
struct sh_mobile_i2c_data *pd;
|
||||
struct i2c_adapter *adap;
|
||||
struct resource *res;
|
||||
char clk_name[8];
|
||||
int size;
|
||||
int ret;
|
||||
|
||||
@ -587,10 +588,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "i2c%d", dev->id);
|
||||
pd->clk = clk_get(&dev->dev, clk_name);
|
||||
pd->clk = clk_get(&dev->dev, NULL);
|
||||
if (IS_ERR(pd->clk)) {
|
||||
dev_err(&dev->dev, "cannot get clock \"%s\"\n", clk_name);
|
||||
dev_err(&dev->dev, "cannot get clock\n");
|
||||
ret = PTR_ERR(pd->clk);
|
||||
goto err;
|
||||
}
|
||||
@ -620,6 +620,11 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
/* Use platformd data bus speed or NORMAL_SPEED */
|
||||
pd->bus_speed = NORMAL_SPEED;
|
||||
if (pdata && pdata->bus_speed)
|
||||
pd->bus_speed = pdata->bus_speed;
|
||||
|
||||
/* The IIC blocks on SH-Mobile ARM processors
|
||||
* come with two new bits in ICIC.
|
||||
*/
|
||||
@ -660,6 +665,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
|
||||
goto err_all;
|
||||
}
|
||||
|
||||
dev_info(&dev->dev, "I2C adapter %d with bus speed %lu Hz\n",
|
||||
adap->nr, pd->bus_speed);
|
||||
return 0;
|
||||
|
||||
err_all:
|
||||
|
@ -35,8 +35,10 @@
|
||||
#define BYTES_PER_FIFO_WORD 4
|
||||
|
||||
#define I2C_CNFG 0x000
|
||||
#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
|
||||
#define I2C_CNFG_PACKET_MODE_EN (1<<10)
|
||||
#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
|
||||
#define I2C_STATUS 0x01C
|
||||
#define I2C_SL_CNFG 0x020
|
||||
#define I2C_SL_CNFG_NEWSL (1<<2)
|
||||
#define I2C_SL_ADDR1 0x02c
|
||||
@ -77,6 +79,7 @@
|
||||
#define I2C_ERR_NONE 0x00
|
||||
#define I2C_ERR_NO_ACK 0x01
|
||||
#define I2C_ERR_ARBITRATION_LOST 0x02
|
||||
#define I2C_ERR_UNKNOWN_INTERRUPT 0x04
|
||||
|
||||
#define PACKET_HEADER0_HEADER_SIZE_SHIFT 28
|
||||
#define PACKET_HEADER0_PACKET_ID_SHIFT 16
|
||||
@ -121,6 +124,7 @@ struct tegra_i2c_dev {
|
||||
void __iomem *base;
|
||||
int cont_id;
|
||||
int irq;
|
||||
bool irq_disabled;
|
||||
int is_dvc;
|
||||
struct completion msg_complete;
|
||||
int msg_err;
|
||||
@ -325,11 +329,17 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
||||
if (i2c_dev->is_dvc)
|
||||
tegra_dvc_init(i2c_dev);
|
||||
|
||||
val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN;
|
||||
val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
|
||||
(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
|
||||
i2c_writel(i2c_dev, val, I2C_CNFG);
|
||||
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
|
||||
clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
|
||||
|
||||
if (!i2c_dev->is_dvc) {
|
||||
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
|
||||
i2c_writel(i2c_dev, sl_cfg | I2C_SL_CNFG_NEWSL, I2C_SL_CNFG);
|
||||
}
|
||||
|
||||
val = 7 << I2C_FIFO_CONTROL_TX_TRIG_SHIFT |
|
||||
0 << I2C_FIFO_CONTROL_RX_TRIG_SHIFT;
|
||||
i2c_writel(i2c_dev, val, I2C_FIFO_CONTROL);
|
||||
@ -338,6 +348,12 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
clk_disable(i2c_dev->clk);
|
||||
|
||||
if (i2c_dev->irq_disabled) {
|
||||
i2c_dev->irq_disabled = 0;
|
||||
enable_irq(i2c_dev->irq);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -350,8 +366,19 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
|
||||
status = i2c_readl(i2c_dev, I2C_INT_STATUS);
|
||||
|
||||
if (status == 0) {
|
||||
dev_warn(i2c_dev->dev, "interrupt with no status\n");
|
||||
return IRQ_NONE;
|
||||
dev_warn(i2c_dev->dev, "irq status 0 %08x %08x %08x\n",
|
||||
i2c_readl(i2c_dev, I2C_PACKET_TRANSFER_STATUS),
|
||||
i2c_readl(i2c_dev, I2C_STATUS),
|
||||
i2c_readl(i2c_dev, I2C_CNFG));
|
||||
i2c_dev->msg_err |= I2C_ERR_UNKNOWN_INTERRUPT;
|
||||
|
||||
if (!i2c_dev->irq_disabled) {
|
||||
disable_irq_nosync(i2c_dev->irq);
|
||||
i2c_dev->irq_disabled = 1;
|
||||
}
|
||||
|
||||
complete(&i2c_dev->msg_complete);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (unlikely(status & status_err)) {
|
||||
@ -391,6 +418,8 @@ static irqreturn_t tegra_i2c_isr(int irq, void *dev_id)
|
||||
I2C_INT_PACKET_XFER_COMPLETE | I2C_INT_TX_FIFO_DATA_REQ |
|
||||
I2C_INT_RX_FIFO_DATA_REQ);
|
||||
i2c_writel(i2c_dev, status, I2C_INT_STATUS);
|
||||
if (i2c_dev->is_dvc)
|
||||
dvc_writel(i2c_dev, DVC_STATUS_I2C_DONE_INTR, DVC_STATUS);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -424,12 +453,12 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
|
||||
|
||||
packet_header = msg->addr << I2C_HEADER_SLAVE_ADDR_SHIFT;
|
||||
packet_header |= I2C_HEADER_IE_ENABLE;
|
||||
if (!stop)
|
||||
packet_header |= I2C_HEADER_REPEAT_START;
|
||||
if (msg->flags & I2C_M_TEN)
|
||||
packet_header |= I2C_HEADER_10BIT_ADDR;
|
||||
if (msg->flags & I2C_M_IGNORE_NAK)
|
||||
packet_header |= I2C_HEADER_CONT_ON_NAK;
|
||||
if (msg->flags & I2C_M_NOSTART)
|
||||
packet_header |= I2C_HEADER_REPEAT_START;
|
||||
if (msg->flags & I2C_M_RD)
|
||||
packet_header |= I2C_HEADER_READ;
|
||||
i2c_writel(i2c_dev, packet_header, I2C_TX_FIFO);
|
||||
|
10
include/linux/i2c/i2c-sh_mobile.h
Normal file
10
include/linux/i2c/i2c-sh_mobile.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __I2C_SH_MOBILE_H__
|
||||
#define __I2C_SH_MOBILE_H__
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct i2c_sh_mobile_platform_data {
|
||||
unsigned long bus_speed;
|
||||
};
|
||||
|
||||
#endif /* __I2C_SH_MOBILE_H__ */
|
Loading…
Reference in New Issue
Block a user