Re: [PATCH V1 1/3] i2c: imx: check busy bit when START/STOP
On Sat, Oct 17, 2009 at 5:46 PM, Richard Zhao linux...@gmail.com wrote: The controller can't do anything else before it actually generates START/STOP. So we check busy bit to make sure START/STOP is successfully finished. If we don't check busy bit, START/STOP may fail on some fast CPUs. Signed-off-by: Richard Zhao linux...@gmail.com diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 4afba3e..6055e92 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -120,19 +120,25 @@ struct imx_i2c_struct { wait_queue_head_t queue; unsigned long i2csr; unsigned int disable_delay; + int stopped; }; /** Functions for IMX I2C adapter driver *** ***/ -static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx) +static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) { unsigned long orig_jiffies = jiffies; + unsigned int temp; dev_dbg(i2c_imx-adapter.dev, %s\n, __func__); - /* wait for bus not busy */ - while (readb(i2c_imx-base + IMX_I2C_I2SR) I2SR_IBB) { + while (1) { + temp = readb(i2c_imx-base + IMX_I2C_I2SR); + if (for_busy (temp I2SR_IBB)) + break; + if (!for_busy !(temp I2SR_IBB)) + break; if (signal_pending(current)) { dev_dbg(i2c_imx-adapter.dev, %s I2C Interrupted\n, __func__); @@ -179,39 +185,55 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) return 0; } -static void i2c_imx_start(struct imx_i2c_struct *i2c_imx) +static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; + int result; dev_dbg(i2c_imx-adapter.dev, %s\n, __func__); /* Enable I2C controller */ + writeb(0, i2c_imx-base + IMX_I2C_I2SR); writeb(I2CR_IEN, i2c_imx-base + IMX_I2C_I2CR); + + /* Wait controller to be stable */ + udelay(50); + /* Start I2C transaction */ temp = readb(i2c_imx-base + IMX_I2C_I2CR); temp |= I2CR_MSTA; writeb(temp, i2c_imx-base + IMX_I2C_I2CR); + result = i2c_imx_bus_busy(i2c_imx, 1); + if (result) + return result; + i2c_imx-stopped = 0; + temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; writeb(temp, i2c_imx-base + IMX_I2C_I2CR); + return result; } static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) { unsigned int temp = 0; - /* Stop I2C transaction */ - dev_dbg(i2c_imx-adapter.dev, %s\n, __func__); - temp = readb(i2c_imx-base + IMX_I2C_I2CR); - temp = ~I2CR_MSTA; - writeb(temp, i2c_imx-base + IMX_I2C_I2CR); - /* setup chip registers to defaults */ - writeb(I2CR_IEN, i2c_imx-base + IMX_I2C_I2CR); - writeb(0, i2c_imx-base + IMX_I2C_I2SR); + if (!i2c_imx-stopped) { + /* Stop I2C transaction */ + dev_dbg(i2c_imx-adapter.dev, %s\n, __func__); + temp = readb(i2c_imx-base + IMX_I2C_I2CR); + temp = ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, i2c_imx-base + IMX_I2C_I2CR); + i2c_imx-stopped = 1; + } /* * This delay caused by an i.MXL hardware bug. * If no (or too short) delay, no STOP bit will be generated. */ udelay(i2c_imx-disable_delay); + + if (!i2c_imx-stopped) + i2c_imx_bus_busy(i2c_imx, 0); + /* Disable I2C controller */ writeb(0, i2c_imx-base + IMX_I2C_I2CR); } @@ -341,11 +363,15 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) if (result) return result; if (i == (msgs-len - 1)) { + /* It must generate STOP before read I2DR to prevent + controller from generating another clock cycle */ dev_dbg(i2c_imx-adapter.dev, %s clear MSTA\n, __func__); temp = readb(i2c_imx-base + IMX_I2C_I2CR); - temp = ~I2CR_MSTA; + temp = ~(I2CR_MSTA | I2CR_MTX); writeb(temp, i2c_imx-base + IMX_I2C_I2CR); + i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx-stopped = 1; } else if (i == (msgs-len - 2)) { dev_dbg(i2c_imx-adapter.dev, %s set TXAK\n, __func__); @@ -370,14 +396,11 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, dev_dbg(i2c_imx-adapter.dev, %s\n, __func__); - /* Check if i2c bus is
[PATCH] i2c-scmi: Quirk to work on IBM machines with broken BIOSes
On some old IBM workstations and desktop computers, the BIOS presents in the DSDT an SMBus object that is missing the HID identifier that the i2c-scmi driver looks for. It also omits the leading _ in the method names (it should be _SBR, not SBR_). Modify the ACPI device scan code to insert the missing HID if it finds an IBM system with such an object, and modify the i2c-scmi driver to handle the odd method names. Affected machines: IntelliStation Z20/Z30. Note that the i2c-i801 driver no longer works on these machines because of ACPI resource conflicts. Signed-off-by: Darrick J. Wong djw...@us.ibm.com --- drivers/acpi/scan.c | 38 drivers/i2c/busses/i2c-scmi.c | 49 - include/acpi/acpi_drivers.h |1 + 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 14a7481..58cb324 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -8,6 +8,7 @@ #include linux/acpi.h #include linux/signal.h #include linux/kthread.h +#include linux/dmi.h #include acpi/acpi_drivers.h @@ -1014,6 +1015,41 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id) list_add_tail(id-list, device-pnp.ids); } +/* + * Old IBM workstations have a DSDT bug wherein the SMBus object + * lacks the SMBUS01 HID and the methods do not have the necessary _ + * prefix. Work around this. + */ +static int probe_ibm_smbus_device(struct acpi_device *device) +{ + acpi_handle h_dummy; + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; + int result; + + if (!dmi_name_in_vendors(IBM)) + return -ENODEV; + + /* Look for SMBS object */ + result = acpi_get_name(device-handle, ACPI_SINGLE_NAME, path); + if (result) + return result; + + if (strcmp(SMBS, path.pointer)) { + result = -ENODEV; + goto out; + } + + /* Does it have the necessary (but misnamed) methods? */ + result = -ENODEV; + if (ACPI_SUCCESS(acpi_get_handle(device-handle, SBI, h_dummy)) + ACPI_SUCCESS(acpi_get_handle(device-handle, SBR, h_dummy)) + ACPI_SUCCESS(acpi_get_handle(device-handle, SBW, h_dummy))) + result = 0; +out: + kfree(path.pointer); + return result; +} + static void acpi_device_set_id(struct acpi_device *device) { acpi_status status; @@ -1064,6 +1100,8 @@ static void acpi_device_set_id(struct acpi_device *device) acpi_add_id(device, ACPI_BAY_HID); else if (ACPI_SUCCESS(acpi_dock_match(device))) acpi_add_id(device, ACPI_DOCK_HID); + else if (!probe_ibm_smbus_device(device)) + acpi_add_id(device, ACPI_SMBUS_HID); break; case ACPI_BUS_TYPE_POWER: diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index b4a55d4..f0ad03d 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -33,6 +33,7 @@ struct acpi_smbus_cmi { u8 cap_info:1; u8 cap_read:1; u8 cap_write:1; + struct smbus_methods_t methods; }; static const struct smbus_methods_t smbus_methods = { @@ -41,8 +42,15 @@ static const struct smbus_methods_t smbus_methods = { .mt_sbw = _SBW, }; +/* Some IBM BIOSes omit the leading underscore */ +static const struct smbus_methods_t ibm_smbus_methods = { + .mt_info = SBI_, + .mt_sbr = SBR_, + .mt_sbw = SBW_, +}; + static const struct acpi_device_id acpi_smbus_cmi_ids[] = { - {SMBUS01, 0}, + {ACPI_SMBUS_HID, 0}, {, 0} }; @@ -150,11 +158,11 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, if (read_write == I2C_SMBUS_READ) { protocol |= ACPI_SMBUS_PRTCL_READ; - method = smbus_methods.mt_sbr; + method = smbus_cmi-methods.mt_sbr; input.count = 3; } else { protocol |= ACPI_SMBUS_PRTCL_WRITE; - method = smbus_methods.mt_sbw; + method = smbus_cmi-methods.mt_sbw; input.count = 5; } @@ -283,20 +291,21 @@ static const struct i2c_algorithm acpi_smbus_cmi_algorithm = { }; -static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi, - const char *name) +static int acpi_smbus_cmi_probe_cap(struct acpi_smbus_cmi *smbus_cmi, + const char *name, + const struct smbus_methods_t *methods) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; - if (!strcmp(name, smbus_methods.mt_info)) { + if (!strcmp(name, methods-mt_info)) { status =
Re: [PATCH] i2c-scmi: Quirk to work on IBM machines with broken BIOSes
Hi Darrick, On Tue, Oct 20, 2009 at 04:11:49PM -0700, Darrick J. Wong wrote: On some old IBM workstations and desktop computers, the BIOS presents in the DSDT an SMBus object that is missing the HID identifier that the i2c-scmi driver looks for. It also omits the leading _ in the method names (it should be _SBR, not SBR_). Modify the ACPI device scan code to insert the missing HID if it finds an IBM system with such an object, and modify the i2c-scmi driver to handle the odd method names. I have a suggestion: You can need not to add quirk in acpi part, instead you can add your ACPI device HID in i2c-scmi with your specificied methods set. -- Best Regards, - Crane -- To unsubscribe from this list: send the line unsubscribe linux-i2c in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html