Commit 60162e49 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Paul Mackerras

[PATCH] powermac: Fix i2c on keywest based chips

The new i2c implementation for PowerMac has a regression that causes the
hardware to go out of state when probing non-existent devices. While
fixing that, I also found & fixed a couple of other corner cases. This
fixes booting with a pbbuttons version that scans the i2c bus for an LMU
controller among others. Tested on a dual G5 with thermal control (which
has heavy i2c activity) with no problem so far.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 28897731
...@@ -231,6 +231,14 @@ static u8 kw_i2c_wait_interrupt(struct pmac_i2c_host_kw *host) ...@@ -231,6 +231,14 @@ static u8 kw_i2c_wait_interrupt(struct pmac_i2c_host_kw *host)
return isr; return isr;
} }
static void kw_i2c_do_stop(struct pmac_i2c_host_kw *host, int result)
{
kw_write_reg(reg_control, KW_I2C_CTL_STOP);
host->state = state_stop;
host->result = result;
}
static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr) static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr)
{ {
u8 ack; u8 ack;
...@@ -246,42 +254,36 @@ static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr) ...@@ -246,42 +254,36 @@ static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr)
} }
if (isr == 0) { if (isr == 0) {
printk(KERN_WARNING "low_i2c: Timeout in i2c transfer"
" on keywest !\n");
if (host->state != state_stop) { if (host->state != state_stop) {
DBG_LOW("KW: Timeout !\n"); kw_i2c_do_stop(host, -EIO);
host->result = -EIO; return;
goto stop;
}
if (host->state == state_stop) {
ack = kw_read_reg(reg_status);
if (ack & KW_I2C_STAT_BUSY)
kw_write_reg(reg_status, 0);
host->state = state_idle;
kw_write_reg(reg_ier, 0x00);
if (!host->polled)
complete(&host->complete);
} }
ack = kw_read_reg(reg_status);
if (ack & KW_I2C_STAT_BUSY)
kw_write_reg(reg_status, 0);
host->state = state_idle;
kw_write_reg(reg_ier, 0x00);
if (!host->polled)
complete(&host->complete);
return; return;
} }
if (isr & KW_I2C_IRQ_ADDR) { if (isr & KW_I2C_IRQ_ADDR) {
ack = kw_read_reg(reg_status); ack = kw_read_reg(reg_status);
if (host->state != state_addr) { if (host->state != state_addr) {
kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR);
WRONG_STATE("KW_I2C_IRQ_ADDR"); WRONG_STATE("KW_I2C_IRQ_ADDR");
host->result = -EIO; kw_i2c_do_stop(host, -EIO);
goto stop;
} }
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
host->result = -ENODEV; host->result = -ENXIO;
DBG_LOW("KW: NAK on address\n");
host->state = state_stop; host->state = state_stop;
return; DBG_LOW("KW: NAK on address\n");
} else { } else {
if (host->len == 0) { if (host->len == 0)
kw_write_reg(reg_isr, KW_I2C_IRQ_ADDR); kw_i2c_do_stop(host, 0);
goto stop; else if (host->rw) {
}
if (host->rw) {
host->state = state_read; host->state = state_read;
if (host->len > 1) if (host->len > 1)
kw_write_reg(reg_control, kw_write_reg(reg_control,
...@@ -308,25 +310,19 @@ static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr) ...@@ -308,25 +310,19 @@ static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr)
ack = kw_read_reg(reg_status); ack = kw_read_reg(reg_status);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
DBG_LOW("KW: nack on data write\n"); DBG_LOW("KW: nack on data write\n");
host->result = -EIO; host->result = -EFBIG;
goto stop; host->state = state_stop;
} else if (host->len) { } else if (host->len) {
kw_write_reg(reg_data, *(host->data++)); kw_write_reg(reg_data, *(host->data++));
host->len--; host->len--;
} else { } else
kw_write_reg(reg_control, KW_I2C_CTL_STOP); kw_i2c_do_stop(host, 0);
host->state = state_stop;
host->result = 0;
}
kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
} else { } else {
kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
WRONG_STATE("KW_I2C_IRQ_DATA"); WRONG_STATE("KW_I2C_IRQ_DATA");
if (host->state != state_stop) { if (host->state != state_stop)
host->result = -EIO; kw_i2c_do_stop(host, -EIO);
goto stop;
}
} }
kw_write_reg(reg_isr, KW_I2C_IRQ_DATA);
} }
if (isr & KW_I2C_IRQ_STOP) { if (isr & KW_I2C_IRQ_STOP) {
...@@ -340,14 +336,10 @@ static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr) ...@@ -340,14 +336,10 @@ static void kw_i2c_handle_interrupt(struct pmac_i2c_host_kw *host, u8 isr)
complete(&host->complete); complete(&host->complete);
} }
/* Below should only happen in manual mode which we don't use ... */
if (isr & KW_I2C_IRQ_START) if (isr & KW_I2C_IRQ_START)
kw_write_reg(reg_isr, KW_I2C_IRQ_START); kw_write_reg(reg_isr, KW_I2C_IRQ_START);
return;
stop:
kw_write_reg(reg_control, KW_I2C_CTL_STOP);
host->state = state_stop;
return;
} }
/* Interrupt handler */ /* Interrupt handler */
...@@ -544,11 +536,11 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) ...@@ -544,11 +536,11 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np)
return NULL; return NULL;
} }
/* Make sure IRA is disabled */ /* Make sure IRQ is disabled */
kw_write_reg(reg_ier, 0); kw_write_reg(reg_ier, 0);
/* Request chip interrupt */ /* Request chip interrupt */
if (request_irq(host->irq, kw_i2c_irq, SA_SHIRQ, "keywest i2c", host)) if (request_irq(host->irq, kw_i2c_irq, 0, "keywest i2c", host))
host->irq = NO_IRQ; host->irq = NO_IRQ;
printk(KERN_INFO "KeyWest i2c @0x%08x irq %d %s\n", printk(KERN_INFO "KeyWest i2c @0x%08x irq %d %s\n",
......
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