History

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.

Evan Nimmo committed 6 years ago

Commit 0a355ae1 parent 0065ec02 files · +38 −12Browse files

Unified · Split

2 files
  1. drivers/i2c/algos/i2c-algo-pca.c +23 −12
  2. include/linux/i2c-algo-pca.h +15 −0
drivers/i2c/algos/i2c-algo-pca.c+23−12
@@ -41,8 +41,22 @@ static void pca_reset(struct i2c_algo_pca_data *adap) · 40 unmodified lines
41 pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);41 pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);
42 pca_outw(adap, I2C_PCA_IND, 0xA5);42 pca_outw(adap, I2C_PCA_IND, 0xA5);
43 pca_outw(adap, I2C_PCA_IND, 0x5A);43 pca_outw(adap, I2C_PCA_IND, 0x5A);
44
45 /*
46 * After a reset we need to re-apply any configuration
47 * (calculated in pca_init) to get the bus in a working state.
48 */
49 pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IMODE);
50 pca_outw(adap, I2C_PCA_IND, adap->bus_settings.mode);
51 pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
52 pca_outw(adap, I2C_PCA_IND, adap->bus_settings.tlow);
53 pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
54 pca_outw(adap, I2C_PCA_IND, adap->bus_settings.thi);
55
56 pca_set_con(adap, I2C_PCA_CON_ENSIO);
44 } else {57 } else {
45 adap->reset_chip(adap->data);58 adap->reset_chip(adap->data);
59 pca_set_con(adap, I2C_PCA_CON_ENSIO | adap->bus_settings.clock_freq);
46 }60 }
47}61}
48 62
@@ -423,13 +437,14 @@ static int pca_init(struct i2c_adapter *adap) · 374 unmodified lines
423 " Use the nominal frequency.\n", adap->name);437 " Use the nominal frequency.\n", adap->name);
424 }438 }
425 439
426 pca_reset(pca_data);
427
428 clock = pca_clock(pca_data);440 clock = pca_clock(pca_data);
429 printk(KERN_INFO "%s: Clock frequency is %dkHz\n",441 printk(KERN_INFO "%s: Clock frequency is %dkHz\n",
430 adap->name, freqs[clock]);442 adap->name, freqs[clock]);
431 443
432 pca_set_con(pca_data, I2C_PCA_CON_ENSIO | clock);444 /* Store settings as these will be needed when the PCA chip is reset */
445 pca_data->bus_settings.clock_freq = clock;
446
447 pca_reset(pca_data);
433 } else {448 } else {
434 int clock;449 int clock;
435 int mode;450 int mode;
@@ -496,19 +511,15 @@ static int pca_init(struct i2c_adapter *adap) · 60 unmodified lines
496 thi = tlow * min_thi / min_tlow;511 thi = tlow * min_thi / min_tlow;
497 }512 }
498 513
514 /* Store settings as these will be needed when the PCA chip is reset */
515 pca_data->bus_settings.mode = mode;
516 pca_data->bus_settings.tlow = tlow;
517 pca_data->bus_settings.thi = thi;
518
499 pca_reset(pca_data);519 pca_reset(pca_data);
500 520
501 printk(KERN_INFO521 printk(KERN_INFO
502 "%s: Clock frequency is %dHz\n", adap->name, clock * 100);522 "%s: Clock frequency is %dHz\n", adap->name, clock * 100);
503
504 pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IMODE);
505 pca_outw(pca_data, I2C_PCA_IND, mode);
506 pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
507 pca_outw(pca_data, I2C_PCA_IND, tlow);
508 pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
509 pca_outw(pca_data, I2C_PCA_IND, thi);
510
511 pca_set_con(pca_data, I2C_PCA_CON_ENSIO);
512 }523 }
513 udelay(500); /* 500 us for oscillator to stabilise */524 udelay(500); /* 500 us for oscillator to stabilise */
514 525
include/linux/i2c-algo-pca.h+15−0
@@ -53,6 +53,20 @@ · 52 unmodified lines
53#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */53#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */
54#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */54#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */
55 55
56/**
57 * struct pca_i2c_bus_settings - The configured PCA i2c bus settings
58 * @mode: Configured i2c bus mode
59 * @tlow: Configured SCL LOW period
60 * @thi: Configured SCL HIGH period
61 * @clock_freq: The configured clock frequency
62 */
63struct pca_i2c_bus_settings {
64 int mode;
65 int tlow;
66 int thi;
67 int clock_freq;
68};
69
56struct i2c_algo_pca_data {70struct i2c_algo_pca_data {
57 void *data; /* private low level data */71 void *data; /* private low level data */
58 void (*write_byte) (void *data, int reg, int val);72 void (*write_byte) (void *data, int reg, int val);
@@ -64,6 +78,7 @@ struct i2c_algo_pca_data { · 5 unmodified lines
64 * For PCA9665, use the frequency you want here. */78 * For PCA9665, use the frequency you want here. */
65 unsigned int i2c_clock;79 unsigned int i2c_clock;
66 unsigned int chip;80 unsigned int chip;
81 struct pca_i2c_bus_settings bus_settings;
67};82};
68 83
69int i2c_pca_add_bus(struct i2c_adapter *);84int i2c_pca_add_bus(struct i2c_adapter *);