Skip to content

Commit ddbc3e9

Browse files
Evann-atlSomasundaram Krishnasamy
authored andcommitted
i2c: algo: pca: Reapply i2c bus settings after reset
[ Upstream commit 0a355ae ] 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: Evan Nimmo <[email protected]> Reviewed-by: Chris Packham <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Wolfram Sang <[email protected]> Signed-off-by: Sasha Levin <[email protected]> (cherry picked from commit 30ce9a30a3881ae384e98aabd68310d053474e7e)
1 parent f03b686 commit ddbc3e9

File tree

2 files changed

+38
-12
lines changed

2 files changed

+38
-12
lines changed

drivers/i2c/algos/i2c-algo-pca.c

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,22 @@ static void pca_reset(struct i2c_algo_pca_data *adap)
5050
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET);
5151
pca_outw(adap, I2C_PCA_IND, 0xA5);
5252
pca_outw(adap, I2C_PCA_IND, 0x5A);
53+
54+
/*
55+
* After a reset we need to re-apply any configuration
56+
* (calculated in pca_init) to get the bus in a working state.
57+
*/
58+
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IMODE);
59+
pca_outw(adap, I2C_PCA_IND, adap->bus_settings.mode);
60+
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
61+
pca_outw(adap, I2C_PCA_IND, adap->bus_settings.tlow);
62+
pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
63+
pca_outw(adap, I2C_PCA_IND, adap->bus_settings.thi);
64+
65+
pca_set_con(adap, I2C_PCA_CON_ENSIO);
5366
} else {
5467
adap->reset_chip(adap->data);
68+
pca_set_con(adap, I2C_PCA_CON_ENSIO | adap->bus_settings.clock_freq);
5569
}
5670
}
5771

@@ -435,13 +449,14 @@ static int pca_init(struct i2c_adapter *adap)
435449
" Use the nominal frequency.\n", adap->name);
436450
}
437451

438-
pca_reset(pca_data);
439-
440452
clock = pca_clock(pca_data);
441453
printk(KERN_INFO "%s: Clock frequency is %dkHz\n",
442454
adap->name, freqs[clock]);
443455

444-
pca_set_con(pca_data, I2C_PCA_CON_ENSIO | clock);
456+
/* Store settings as these will be needed when the PCA chip is reset */
457+
pca_data->bus_settings.clock_freq = clock;
458+
459+
pca_reset(pca_data);
445460
} else {
446461
int clock;
447462
int mode;
@@ -508,19 +523,15 @@ static int pca_init(struct i2c_adapter *adap)
508523
thi = tlow * min_thi / min_tlow;
509524
}
510525

526+
/* Store settings as these will be needed when the PCA chip is reset */
527+
pca_data->bus_settings.mode = mode;
528+
pca_data->bus_settings.tlow = tlow;
529+
pca_data->bus_settings.thi = thi;
530+
511531
pca_reset(pca_data);
512532

513533
printk(KERN_INFO
514534
"%s: Clock frequency is %dHz\n", adap->name, clock * 100);
515-
516-
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IMODE);
517-
pca_outw(pca_data, I2C_PCA_IND, mode);
518-
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLL);
519-
pca_outw(pca_data, I2C_PCA_IND, tlow);
520-
pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ISCLH);
521-
pca_outw(pca_data, I2C_PCA_IND, thi);
522-
523-
pca_set_con(pca_data, I2C_PCA_CON_ENSIO);
524535
}
525536
udelay(500); /* 500 us for oscillator to stabilise */
526537

include/linux/i2c-algo-pca.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@
5353
#define I2C_PCA_CON_SI 0x08 /* Serial Interrupt */
5454
#define I2C_PCA_CON_CR 0x07 /* Clock Rate (MASK) */
5555

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+
*/
63+
struct pca_i2c_bus_settings {
64+
int mode;
65+
int tlow;
66+
int thi;
67+
int clock_freq;
68+
};
69+
5670
struct i2c_algo_pca_data {
5771
void *data; /* private low level data */
5872
void (*write_byte) (void *data, int reg, int val);
@@ -64,6 +78,7 @@ struct i2c_algo_pca_data {
6478
* For PCA9665, use the frequency you want here. */
6579
unsigned int i2c_clock;
6680
unsigned int chip;
81+
struct pca_i2c_bus_settings bus_settings;
6782
};
6883

6984
int i2c_pca_add_bus(struct i2c_adapter *);

0 commit comments

Comments
 (0)