diff options
author | Dale Farnsworth <dale@farnsworth.org> | 2007-08-14 12:37:14 -0400 |
---|---|---|
committer | Jean Delvare <khali@hyperion.delvare> | 2007-08-14 12:37:14 -0400 |
commit | a07ad1cc0300931bfd76bfcd2da3ddad743f4a89 (patch) | |
tree | d8b7eb634571649f9401d351801e5de87cc5c91d /drivers/i2c | |
parent | 5af0e07f87e7d9be2a9db514af1e338341240f6d (diff) |
i2c-mv64xxx: Reinitialize hw and driver on I2C bus hang
Under certain conditions, the mv64xxx I2C bus can hang preventing
further operation. To make the driver more robust, we now reset
the I2C hardware and the driver state machine when such hangs are
detected.
Signed-off-by: Dale Farnsworth <dale@farnsworth.org>
Acked-by: Mark A. Greer <mgreer@mvista.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-mv64xxx.c | 31 |
1 files changed, 17 insertions, 14 deletions
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 251154ae5d97..bb7bf68a7fb6 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c | |||
@@ -107,6 +107,21 @@ struct mv64xxx_i2c_data { | |||
107 | * | 107 | * |
108 | ***************************************************************************** | 108 | ***************************************************************************** |
109 | */ | 109 | */ |
110 | |||
111 | /* Reset hardware and initialize FSM */ | ||
112 | static void | ||
113 | mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) | ||
114 | { | ||
115 | writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET); | ||
116 | writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)), | ||
117 | drv_data->reg_base + MV64XXX_I2C_REG_BAUD); | ||
118 | writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR); | ||
119 | writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR); | ||
120 | writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP, | ||
121 | drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); | ||
122 | drv_data->state = MV64XXX_I2C_STATE_IDLE; | ||
123 | } | ||
124 | |||
110 | static void | 125 | static void |
111 | mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) | 126 | mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) |
112 | { | 127 | { |
@@ -203,7 +218,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) | |||
203 | drv_data->state, status, drv_data->msg->addr, | 218 | drv_data->state, status, drv_data->msg->addr, |
204 | drv_data->msg->flags); | 219 | drv_data->msg->flags); |
205 | drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; | 220 | drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; |
206 | drv_data->state = MV64XXX_I2C_STATE_IDLE; | 221 | mv64xxx_i2c_hw_init(drv_data); |
207 | drv_data->rc = -EIO; | 222 | drv_data->rc = -EIO; |
208 | } | 223 | } |
209 | } | 224 | } |
@@ -367,6 +382,7 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) | |||
367 | "mv64xxx: I2C bus locked, block: %d, " | 382 | "mv64xxx: I2C bus locked, block: %d, " |
368 | "time_left: %d\n", drv_data->block, | 383 | "time_left: %d\n", drv_data->block, |
369 | (int)time_left); | 384 | (int)time_left); |
385 | mv64xxx_i2c_hw_init(drv_data); | ||
370 | } | 386 | } |
371 | } else | 387 | } else |
372 | spin_unlock_irqrestore(&drv_data->lock, flags); | 388 | spin_unlock_irqrestore(&drv_data->lock, flags); |
@@ -443,19 +459,6 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = { | |||
443 | * | 459 | * |
444 | ***************************************************************************** | 460 | ***************************************************************************** |
445 | */ | 461 | */ |
446 | static void __devinit | ||
447 | mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data) | ||
448 | { | ||
449 | writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET); | ||
450 | writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)), | ||
451 | drv_data->reg_base + MV64XXX_I2C_REG_BAUD); | ||
452 | writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR); | ||
453 | writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR); | ||
454 | writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP, | ||
455 | drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); | ||
456 | drv_data->state = MV64XXX_I2C_STATE_IDLE; | ||
457 | } | ||
458 | |||
459 | static int __devinit | 462 | static int __devinit |
460 | mv64xxx_i2c_map_regs(struct platform_device *pd, | 463 | mv64xxx_i2c_map_regs(struct platform_device *pd, |
461 | struct mv64xxx_i2c_data *drv_data) | 464 | struct mv64xxx_i2c_data *drv_data) |