Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: da9052/53 lockup fix

An issue has been reported where the PMIC either locks up or fails to
respond following a system Reset. This could result in a second write
in which the bus writes the current content of the write buffer to address
of the last I2C access.

The failure case is where this unwanted write transfers incorrect data to
a critical register.

This patch fixes this issue to by following any read or write with a dummy read
to a safe register address. A safe register address is one where the contents
will not affect the operation of the system.

Signed-off-by: Ashish Jangam <ashish.jangam@kpitcummins.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Ashish Jangam and committed by
Samuel Ortiz
0a8c290a ab4e8f8b

+126 -4
+61
drivers/mfd/da9052-i2c.c
··· 27 27 #include <linux/of_device.h> 28 28 #endif 29 29 30 + /* I2C safe register check */ 31 + static inline bool i2c_safe_reg(unsigned char reg) 32 + { 33 + switch (reg) { 34 + case DA9052_STATUS_A_REG: 35 + case DA9052_STATUS_B_REG: 36 + case DA9052_STATUS_C_REG: 37 + case DA9052_STATUS_D_REG: 38 + case DA9052_ADC_RES_L_REG: 39 + case DA9052_ADC_RES_H_REG: 40 + case DA9052_VDD_RES_REG: 41 + case DA9052_ICHG_AV_REG: 42 + case DA9052_TBAT_RES_REG: 43 + case DA9052_ADCIN4_RES_REG: 44 + case DA9052_ADCIN5_RES_REG: 45 + case DA9052_ADCIN6_RES_REG: 46 + case DA9052_TJUNC_RES_REG: 47 + case DA9052_TSI_X_MSB_REG: 48 + case DA9052_TSI_Y_MSB_REG: 49 + case DA9052_TSI_LSB_REG: 50 + case DA9052_TSI_Z_MSB_REG: 51 + return true; 52 + default: 53 + return false; 54 + } 55 + } 56 + 57 + /* 58 + * There is an issue with DA9052 and DA9053_AA/BA/BB PMIC where the PMIC 59 + * gets lockup up or fails to respond following a system reset. 60 + * This fix is to follow any read or write with a dummy read to a safe 61 + * register. 62 + */ 63 + int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) 64 + { 65 + int val; 66 + 67 + switch (da9052->chip_id) { 68 + case DA9052: 69 + case DA9053_AA: 70 + case DA9053_BA: 71 + case DA9053_BB: 72 + /* A dummy read to a safe register address. */ 73 + if (!i2c_safe_reg(reg)) 74 + return regmap_read(da9052->regmap, 75 + DA9052_PARK_REGISTER, 76 + &val); 77 + break; 78 + default: 79 + /* 80 + * For other chips parking of I2C register 81 + * to a safe place is not required. 82 + */ 83 + break; 84 + } 85 + 86 + return 0; 87 + } 88 + EXPORT_SYMBOL(da9052_i2c_fix); 89 + 30 90 static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) 31 91 { 32 92 int reg_val, ret; ··· 143 83 144 84 da9052->dev = &client->dev; 145 85 da9052->chip_irq = client->irq; 86 + da9052->fix_io = da9052_i2c_fix; 146 87 147 88 i2c_set_clientdata(client, da9052); 148 89
+62 -4
include/linux/mfd/da9052/da9052.h
··· 99 99 u8 chip_id; 100 100 101 101 int chip_irq; 102 + 103 + /* SOC I/O transfer related fixes for DA9052/53 */ 104 + int (*fix_io) (struct da9052 *da9052, unsigned char reg); 102 105 }; 103 106 104 107 /* ADC API */ ··· 116 113 ret = regmap_read(da9052->regmap, reg, &val); 117 114 if (ret < 0) 118 115 return ret; 116 + 117 + if (da9052->fix_io) { 118 + ret = da9052->fix_io(da9052, reg); 119 + if (ret < 0) 120 + return ret; 121 + } 122 + 119 123 return val; 120 124 } 121 125 122 126 static inline int da9052_reg_write(struct da9052 *da9052, unsigned char reg, 123 127 unsigned char val) 124 128 { 125 - return regmap_write(da9052->regmap, reg, val); 129 + int ret; 130 + 131 + ret = regmap_write(da9052->regmap, reg, val); 132 + if (ret < 0) 133 + return ret; 134 + 135 + if (da9052->fix_io) { 136 + ret = da9052->fix_io(da9052, reg); 137 + if (ret < 0) 138 + return ret; 139 + } 140 + 141 + return ret; 126 142 } 127 143 128 144 static inline int da9052_group_read(struct da9052 *da9052, unsigned char reg, 129 145 unsigned reg_cnt, unsigned char *val) 130 146 { 131 - return regmap_bulk_read(da9052->regmap, reg, val, reg_cnt); 147 + int ret; 148 + 149 + ret = regmap_bulk_read(da9052->regmap, reg, val, reg_cnt); 150 + if (ret < 0) 151 + return ret; 152 + 153 + if (da9052->fix_io) { 154 + ret = da9052->fix_io(da9052, reg); 155 + if (ret < 0) 156 + return ret; 157 + } 158 + 159 + return ret; 132 160 } 133 161 134 162 static inline int da9052_group_write(struct da9052 *da9052, unsigned char reg, 135 163 unsigned reg_cnt, unsigned char *val) 136 164 { 137 - return regmap_raw_write(da9052->regmap, reg, val, reg_cnt); 165 + int ret; 166 + 167 + ret = regmap_raw_write(da9052->regmap, reg, val, reg_cnt); 168 + if (ret < 0) 169 + return ret; 170 + 171 + if (da9052->fix_io) { 172 + ret = da9052->fix_io(da9052, reg); 173 + if (ret < 0) 174 + return ret; 175 + } 176 + 177 + return ret; 138 178 } 139 179 140 180 static inline int da9052_reg_update(struct da9052 *da9052, unsigned char reg, 141 181 unsigned char bit_mask, 142 182 unsigned char reg_val) 143 183 { 144 - return regmap_update_bits(da9052->regmap, reg, bit_mask, reg_val); 184 + int ret; 185 + 186 + ret = regmap_update_bits(da9052->regmap, reg, bit_mask, reg_val); 187 + if (ret < 0) 188 + return ret; 189 + 190 + if (da9052->fix_io) { 191 + ret = da9052->fix_io(da9052, reg); 192 + if (ret < 0) 193 + return ret; 194 + } 195 + 196 + return ret; 145 197 } 146 198 147 199 int da9052_device_init(struct da9052 *da9052, u8 chip_id);
+3
include/linux/mfd/da9052/reg.h
··· 34 34 #define DA9052_STATUS_C_REG 3 35 35 #define DA9052_STATUS_D_REG 4 36 36 37 + /* PARK REGISTER */ 38 + #define DA9052_PARK_REGISTER DA9052_STATUS_D_REG 39 + 37 40 /* EVENT REGISTERS */ 38 41 #define DA9052_EVENT_A_REG 5 39 42 #define DA9052_EVENT_B_REG 6