Commit 0a355aeb authored by Evan Nimmo's avatar Evan Nimmo Committed by Wolfram Sang

i2c: algo: pca: Reapply i2c bus settings after reset

If something goes wrong (such as the SCL being stuck low) then we need
to reset the PCA chip. The issue with this is that on reset we lose all
config settings and the chip ends up in a disabled state which results
in a lock up/high CPU usage. We need to re-apply any configuration that
had previously been set and re-enable the chip.
Signed-off-by: default avatarEvan Nimmo <evan.nimmo@alliedtelesis.co.nz>
Reviewed-by: default avatarChris Packham <chris.packham@alliedtelesis.co.nz>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 0065ec00
...@@ -41,8 +41,22 @@ static void pca_reset(struct i2c_algo_pca_data *adap) ...@@ -41,8 +41,22 @@ static void pca_reset(struct i2c_algo_pca_data *adap)
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET); pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);
pca_outw(adap, I2C_PCA_IND, 0xA5); pca_outw(adap, I2C_PCA_IND, 0xA5);
pca_outw(adap, I2C_PCA_IND, 0x5A); pca_outw(adap, I2C_PCA_IND, 0x5A);
/*
* After a reset we need to re-apply any configuration
* (calculated in pca_init) to get the bus in a working state.
*/
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IMODE);
pca_outw(adap, I2C_PCA_IND, adap->bus_settings.mode);
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
pca_outw(adap, I2C_PCA_IND, adap->bus_settings.tlow);
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
pca_outw(adap, I2C_PCA_IND, adap->bus_settings.thi);
pca_set_con(adap, I2C_PCA_CON_ENSIO);
} else { } else {
adap->reset_chip(adap->data); adap->reset_chip(adap->data);
pca_set_con(adap, I2C_PCA_CON_ENSIO | adap->bus_settings.clock_freq);
} }
} }
...@@ -423,13 +437,14 @@ static int pca_init(struct i2c_adapter *adap) ...@@ -423,13 +437,14 @@ static int pca_init(struct i2c_adapter *adap)
" Use the nominal frequency.\n", adap->name); " Use the nominal frequency.\n", adap->name);
} }
pca_reset(pca_data);
clock = pca_clock(pca_data); clock = pca_clock(pca_data);
printk(KERN_INFO "%s: Clock frequency is %dkHz\n", printk(KERN_INFO "%s: Clock frequency is %dkHz\n",
adap->name, freqs[clock]); adap->name, freqs[clock]);
pca_set_con(pca_data, I2C_PCA_CON_ENSIO | clock); /* Store settings as these will be needed when the PCA chip is reset */
pca_data->bus_settings.clock_freq = clock;
pca_reset(pca_data);
} else { } else {
int clock; int clock;
int mode; int mode;
...@@ -496,19 +511,15 @@ static int pca_init(struct i2c_adapter *adap) ...@@ -496,19 +511,15 @@ static int pca_init(struct i2c_adapter *adap)
thi = tlow * min_thi / min_tlow; thi = tlow * min_thi / min_tlow;
} }
/* Store settings as these will be needed when the PCA chip is reset */
pca_data->bus_settings.mode = mode;
pca_data->bus_settings.tlow = tlow;
pca_data->bus_settings.thi = thi;
pca_reset(pca_data); pca_reset(pca_data);
printk(KERN_INFO printk(KERN_INFO
"%s: Clock frequency is %dHz\n", adap->name, clock * 100); "%s: Clock frequency is %dHz\n", adap->name, clock * 100);
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IMODE);
pca_outw(pca_data, I2C_PCA_IND, mode);
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
pca_outw(pca_data, I2C_PCA_IND, tlow);
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
pca_outw(pca_data, I2C_PCA_IND, thi);
pca_set_con(pca_data, I2C_PCA_CON_ENSIO);
} }
udelay(500); /* 500 us for oscillator to stabilise */ udelay(500); /* 500 us for oscillator to stabilise */
......
...@@ -53,6 +53,20 @@ ...@@ -53,6 +53,20 @@
#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */ #define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */
#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */ #define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */
/**
* struct pca_i2c_bus_settings - The configured PCA i2c bus settings
* @mode: Configured i2c bus mode
* @tlow: Configured SCL LOW period
* @thi: Configured SCL HIGH period
* @clock_freq: The configured clock frequency
*/
struct pca_i2c_bus_settings {
int mode;
int tlow;
int thi;
int clock_freq;
};
struct i2c_algo_pca_data { struct i2c_algo_pca_data {
void *data; /* private low level data */ void *data; /* private low level data */
void (*write_byte) (void *data, int reg, int val); void (*write_byte) (void *data, int reg, int val);
...@@ -64,6 +78,7 @@ struct i2c_algo_pca_data { ...@@ -64,6 +78,7 @@ struct i2c_algo_pca_data {
* For PCA9665, use the frequency you want here. */ * For PCA9665, use the frequency you want here. */
unsigned int i2c_clock; unsigned int i2c_clock;
unsigned int chip; unsigned int chip;
struct pca_i2c_bus_settings bus_settings;
}; };
int i2c_pca_add_bus(struct i2c_adapter *); int i2c_pca_add_bus(struct i2c_adapter *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment