Re: [PATCH V1 1/3] i2c: imx: check busy bit when START/STOP

2009-10-20 Thread Richard Zhao
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

2009-10-20 Thread Darrick J. Wong
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

2009-10-20 Thread Crane Cai
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