PeterChan

enable on-board accelometer MMA7456 on MX23 EVK

Discussion created by PeterChan Employee on May 24, 2012

On MX23 EVK board, there is an accelometer MMA7456 which is controlled via the i2C bus. To enable this accelometer in ER 0912 BSP release*, here are the changes I made.

 

*Notice: ER0912 BSP is the first ER release for MX23 and this page is for the small amount of developers who are still using the release. The change describes here is not applicable to latest MX23 BSP.

===============================================================

diff --git a/arch/arm/mach-stmp378x/stmp378x.c b/arch/arm/mach-stmp378x/stmp378x.c
index df6e6e0..90d0f7b 100644
--- a/arch/arm/mach-stmp378x/stmp378x.c
+++ b/arch/arm/mach-stmp378x/stmp378x.c
@@ -279,6 +279,10 @@ static struct resource i2c_resources[] = {
   .start = IRQ_I2C_ERROR,
   .end = IRQ_I2C_ERROR,
  }, {
+  .flags = IORESOURCE_IRQ,
+  .start = IRQ_I2C_DMA,
+  .end = IRQ_I2C_DMA,
+ }, {
   .flags = IORESOURCE_MEM,
   .start = REGS_I2C_PHYS,
   .end = REGS_I2C_PHYS + REGS_I2C_SIZE - 1,
@@ -290,7 +294,7 @@ static struct resource i2c_resources[] = {
 };
 
 struct platform_device stmp378x_i2c = {
- .name = "i2c_stmp3xxx",
+ .name = "i2c_stmp",
  .id = 0,
  .dev = {
   .release = mxc_nop_release,
diff --git a/arch/arm/mach-stmp378x/stmp378x_devb.c b/arch/arm/mach-stmp378x/stmp378x_devb.c
index 56a9743..0ff425b 100644
--- a/arch/arm/mach-stmp378x/stmp378x_devb.c
+++ b/arch/arm/mach-stmp378x/stmp378x_devb.c
@@ -40,6 +40,15 @@
 
 #include "stmp378x.h"
 
+struct mxc_mma7450_platform_data {
+    char *reg_dvdd_io;
+    char *reg_avdd;
+    void (*gpio_pin_get) (void);
+    void (*gpio_pin_put) (void);
+    int int1;
+    int int2;
+};
+
 static struct platform_device *devices[] = {
  &stmp3xxx_dbguart,
  &stmp3xxx_appuart,
@@ -363,6 +372,46 @@ static struct stmpkbd_keypair keyboard_data[] = {
  { -1, 0 },
 };
 
+void mma7450_gpio_pin_get (void);
+void mma7450_gpio_pin_put (void);
+
+static struct mxc_mma7450_platform_data mma7450_data = {
+ .reg_dvdd_io = NULL,
+ .reg_avdd = NULL,
+ .gpio_pin_get = mma7450_gpio_pin_get,
+ .gpio_pin_put = mma7450_gpio_pin_put,
+};
+
+static struct i2c_board_info __initdata stmp3xxx_i2c_devices[] = {
+    {
+        I2C_BOARD_INFO("mma7450", 0x3A),
+        .platform_data = (void *)&mma7450_data,
+    },
+};
+
+static const char mma7450_label[] = "MMA7450";
+
+void mma7450_gpio_pin_get (void)
+{
+// stmp3xxx_request_pin(PINID_GPMI_D14, PIN_GPIO, mma7450_label);
+// stmp3xxx_request_pin(PINID_GPMI_D15, PIN_GPIO, mma7450_label);
+
+    gpio_request(PINID_GPMI_D14, "int1");
+    gpio_direction_input(PINID_GPMI_D14);
+
+    gpio_request(PINID_GPMI_D15, "int2");
+    gpio_direction_input(PINID_GPMI_D15);
+}
+
+void mma7450_gpio_pin_put (void)
+{
+    gpio_free(PINID_GPMI_D14);
+    gpio_free(PINID_GPMI_D15);
+
+// stmp3xxx_release_pin(PINID_GPMI_D14, mma7450_label);
+// stmp3xxx_release_pin(PINID_GPMI_D15, mma7450_label);
+}
+
 static void __init stmp378x_devb_init(void)
 {
  stmp3xxx_pinmux_init(NR_REAL_IRQS);
@@ -380,6 +429,11 @@ static void __init stmp378x_devb_init(void)
  stmp3xxx_battery.dev.platform_data = &battery_data;
  stmp3xxx_keyboard.dev.platform_data = &keyboard_data;
 
+ /* register i2c devices */
+ mma7450_data.int1 = gpio_to_irq(PINID_GPMI_D14);
+ mma7450_data.int2 = gpio_to_irq(PINID_GPMI_D15);
+ i2c_register_board_info(0, stmp3xxx_i2c_devices, ARRAY_SIZE(stmp3xxx_i2c_devices));
+
  /* register spi devices */
  spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
 
diff --git a/arch/arm/mach-stmp378x/stmp378x_i2c.c b/arch/arm/mach-stmp378x/stmp378x_i2c.c
index f5e96fc..204c952 100644
--- a/arch/arm/mach-stmp378x/stmp378x_i2c.c
+++ b/arch/arm/mach-stmp378x/stmp378x_i2c.c
@@ -52,7 +52,7 @@ u32 cmd_i2c_select[4] = {
   BF(1, APBX_CHn_CMD_CMDWORDS)   |
   BM_APBX_CHn_CMD_WAIT4ENDCMD   |
   BM_APBX_CHn_CMD_CHAIN  |
-  BM_APBX_CHn_CMD_IRQONCMPLT    | /* For debug*/
+  /*BM_APBX_CHn_CMD_IRQONCMPLT    |*/ /* For debug*/
   BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND)),
 
  0, /* dma handler */
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index cacaa13..26d0549 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1028,8 +1028,8 @@ config HWMON_DEBUG_CHIP
 
 config MXC_MMA7450
  tristate "MMA7450 device driver"
- depends on MACH_MX31_3DS
- default n
+ depends on MACH_MX31_3DS || MACH_STMP378X
+ default y
 
 config SENSORS_ISL29003
  tristate "ISL29003 Light Sensor"
diff --git a/drivers/hwmon/mxc_mma7450.c b/drivers/hwmon/mxc_mma7450.c
index 2ee2320..de42468 100644
--- a/drivers/hwmon/mxc_mma7450.c
+++ b/drivers/hwmon/mxc_mma7450.c
@@ -87,12 +87,26 @@ struct mma7450_status {
  u8 ctl2;
 };
 
+struct mxc_mma7450_platform_data {
+    char *reg_dvdd_io;
+    char *reg_avdd;
+    void (*gpio_pin_get) (void);
+    void (*gpio_pin_put) (void);
+    int int1;
+    int int2;
+};
+
 /*forward declear*/
 static ssize_t mma7450_show(struct device *dev,
        struct device_attribute *attr, char *buf);
 static ssize_t mma7450_store(struct device *dev,
         struct device_attribute *attr, const char *buf,
         size_t count);
+static ssize_t mma7450_test_show(struct device *dev,
+                struct device_attribute *attr, char *buf);
+static ssize_t mma7450_test_store(struct device *dev,
+                 struct device_attribute *attr, const char *buf,
+                 size_t count);
 static int mma7450_probe(struct i2c_client *client,
     const struct i2c_device_id *id);
 static int mma7450_remove(struct i2c_client *client);
@@ -116,6 +130,14 @@ static struct device_attribute mma7450_dev_attr = {
  .show = mma7450_show,
  .store = mma7450_store,
 };
+static struct device_attribute mma7450_attr_test = {
+ .attr = {
+  .name = "mma7450_test",
+  .mode = S_IRUSR | S_IWUSR,
+  },
+ .show = mma7450_test_show,
+ .store = mma7450_test_store,
+};
 
 static const struct i2c_device_id mma7450_id[] = {
  {"mma7450", 0},
@@ -484,6 +506,75 @@ static ssize_t mma7450_store(struct device *dev,
 
 #endif
 
+#include <linux/delay.h>
+static ssize_t mma7450_test_show(struct device *dev,
+                struct device_attribute *attr, char *buf)
+{
+    int ii, ret;
+ u8  reg_value[0x1F];
+ unsigned long long begin;
+
+    dev_info(&mma7450_client->dev, "mma7450 i2c test start...\n");
+ for(ii=0; ii<1000; ii++)
+ {
+  memset(reg_value, 0, sizeof(reg_value));
+  begin = jiffies;
+  ret = i2c_smbus_read_i2c_block_data(mma7450_client, REG_XOUTL, 0x1F, reg_value);
+  if(ret < 0x1F)
+  {
+   dev_err(&mma7450_client->dev, "error %d in loop %d\n", ret, ii);
+   break;
+  }
+  if( (reg_value[REG_I2CAD]&0x7F) != MMA7450_I2C_ADDR)
+  {
+   dev_err(&mma7450_client->dev, "error2 %d in loop %d\n", ret, ii);
+   break;
+  }
+  
+  if(jiffies_to_msecs(jiffies - begin) > 1000)
+  {
+   dev_err(&mma7450_client->dev, "(%d)i2c transaction takes longer than 1 sec\n", ii);
+  }  
+ }
+ dev_info(&mma7450_client->dev, "mma7450 i2c test end...\n");
+    return 0;
+}
+
+static ssize_t mma7450_test_store(struct device *dev,
+                 struct device_attribute *attr, const char *buf,
+                 size_t count)
+{
+ int ii, value, ret;
+ u8  reg_value[0x1F];
+ unsigned long long begin;
+
+ value = simple_strtol(buf, NULL, 10);
+ dev_info(&mma7450_client->dev, "mma7450 i2c test start...\n");
+ for(ii=0; ii<value; ii++)
+ {
+        memset(reg_value, 0, sizeof(reg_value));
+  begin = jiffies;
+        ret = i2c_smbus_read_i2c_block_data(mma7450_client, REG_XOUTL, 0x1F, reg_value);
+        if(ret < 0x1F)
+        {
+            dev_err(&mma7450_client->dev, "error %d in loop %d\n", ret, ii);
+            break;
+        }
+        if( (reg_value[REG_I2CAD]&0x7F) != MMA7450_I2C_ADDR)
+        {
+            dev_err(&mma7450_client->dev, "error2 %d in loop %d\n", ret, ii);
+            break;
+        } 
+
+        if(jiffies_to_msecs(jiffies - begin) > 1000)
+        {
+            dev_err(&mma7450_client->dev, "(%d)i2c transaction takes longer than 1 sec\n", ii);
+        }
+ }
+ dev_info(&mma7450_client->dev, "mma7450 i2c test end...\n");
+    return count;
+}
+
 static void report_abs(void)
 {
  u8 status, mod = mma_status.mod;
@@ -623,12 +714,17 @@ static int mma7450_probe(struct i2c_client *client,
  /*enable power supply */
  /*when to power on/off the power is to be considered later */
  /*shall I check the return value */
+ if(plat_data->reg_dvdd_io != NULL)
+ {
  reg_dvdd_io = regulator_get(&client->dev, plat_data->reg_dvdd_io);
  if (reg_dvdd_io != ERR_PTR(-ENOENT))
   regulator_enable(reg_dvdd_io);
  else
   return -EINVAL;
+ }
 
+ if(plat_data->reg_avdd != NULL)
+ {
  reg_avdd = regulator_get(&client->dev, plat_data->reg_avdd);
  if (reg_avdd != ERR_PTR(-ENOENT))
   regulator_enable(reg_avdd);
@@ -636,6 +732,7 @@ static int mma7450_probe(struct i2c_client *client,
   regulator_put(reg_dvdd_io);
   return -EINVAL;
  }
+ }
 
  /*bind the right device to the driver */
  ret = i2c_smbus_read_byte_data(client, REG_I2CAD);
@@ -657,7 +754,15 @@ static int mma7450_probe(struct i2c_client *client,
   goto error_disable_power;
  }
 
+    /*create device file in sysfs as user interface */
+    ret = device_create_file(&client->dev, &mma7450_attr_test);
+    if (ret) {
+        dev_err(&client->dev, "create device file failed!\n");
+        goto error_rm_dev_file;
+    }
+
  /*register to hwmon device */
+#if 0
  hwmon_dev = hwmon_device_register(&client->dev);
  if (IS_ERR(hwmon_dev)) {
   dev_err(&client->dev, "hwmon register failed!\n");
@@ -688,10 +793,12 @@ static int mma7450_probe(struct i2c_client *client,
   dev_err(&client->dev, "register poll device failed!\n");
   goto error_free_poll_dev;
  }
+#endif
 
  /* configure gpio as input for interrupt monitor */
  plat_data->gpio_pin_get();
 
+#if 0
  set_irq_type(plat_data->int1, IRQF_TRIGGER_RISING);
  /* register interrupt handle */
  ret = request_irq(plat_data->int1, mma7450_interrupt,
@@ -711,7 +818,7 @@ static int mma7450_probe(struct i2c_client *client,
    plat_data->int2, ret);
   goto error_free_irq1;
  }
-
+#endif
  dev_info(&client->dev, "mma7450 device is probed successfully.\n");
 
  set_mod(1);
@@ -729,10 +836,14 @@ static int mma7450_probe(struct i2c_client *client,
       error_rm_dev_file:
  device_remove_file(&client->dev, &mma7450_dev_attr);
       error_disable_power:
- regulator_disable(reg_dvdd_io); /*shall I check the return value */
- regulator_disable(reg_avdd);
- regulator_put(reg_dvdd_io);
- regulator_put(reg_avdd);
+ if(plat_data->reg_dvdd_io != NULL)
+  regulator_disable(reg_dvdd_io); /*shall I check the return value */
+ if(plat_data->reg_avdd != NULL)
+  regulator_disable(reg_avdd);
+ if(plat_data->reg_dvdd_io != NULL)
+  regulator_put(reg_dvdd_io);
+ if(plat_data->reg_avdd != NULL)
+  regulator_put(reg_avdd);
 
  return ret;
 }
@@ -746,10 +857,14 @@ static int mma7450_remove(struct i2c_client *client)
  input_free_polled_device(mma7450_idev);
  hwmon_device_unregister(hwmon_dev);
  device_remove_file(&client->dev, &mma7450_dev_attr);
- regulator_disable(reg_dvdd_io); /*shall I check the return value */
- regulator_disable(reg_avdd);
- regulator_put(reg_dvdd_io);
- regulator_put(reg_avdd);
+ if(plat_data->reg_dvdd_io != NULL)
+  regulator_disable(reg_dvdd_io); /*shall I check the return value */
+ if(plat_data->reg_avdd != NULL)
+  regulator_disable(reg_avdd);
+ if(plat_data->reg_dvdd_io != NULL)
+  regulator_put(reg_dvdd_io);
+ if(plat_data->reg_avdd != NULL)
+  regulator_put(reg_avdd);
  return 0;
 }
 
diff --git a/drivers/i2c/busses/i2c-stmp378x.c b/drivers/i2c/busses/i2c-stmp378x.c
index 08c20f8..c615500 100644
--- a/drivers/i2c/busses/i2c-stmp378x.c
+++ b/drivers/i2c/busses/i2c-stmp378x.c
@@ -39,6 +39,7 @@ static void reset_i2c_module(void)
  u32 ctrl;
  int count;
  count = 1000;
+printk(KERN_INFO "i2c_module is reset\n");
  __raw_writel(BM_I2C_CTRL0_SFTRST, REGS_I2C_BASE + HW_I2C_CTRL0_SET);
  udelay(10); /* Reseting the module can take multiple clocks.*/
  while (--count && (!(__raw_readl(REGS_I2C_BASE + HW_I2C_CTRL0) & BM_I2C_CTRL0_CLKGATE)))
@@ -101,10 +102,11 @@ static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
   dev_dbg(dev->dev, "controler is timed out\n");
   return -ETIMEDOUT;
  }
- if ((!dev->cmd_err) && (msg->flags & I2C_M_RD))
+ if ((!dev->cmd_err) && (msg->flags & I2C_M_RD)) {
   hw_i2c_finish_read(msg->buf, msg->len);
-
- dev_dbg(dev->dev, "<============= Done with err=%d\n", dev->cmd_err);
+ }
+ if(dev->cmd_err)
+  dev_dbg(dev->dev, "<============= Done with err=%d\n", dev->cmd_err);
 
 
  return dev->cmd_err;
@@ -145,21 +147,24 @@ stmp378x_i2c_func(struct i2c_adapter *adap)
 static irqreturn_t
 stmp378x_i2c_dma_isr(int this_irq, void *dev_id)
 {
+ struct stmp378x_i2c_dev *dev = dev_id;
+
  hw_i2c_clear_dma_interrupt();
+
+ complete(&dev->cmd_complete);
  return IRQ_HANDLED;
 
 }
 
 #define I2C_IRQ_MASK 0x000000FF
 
-static irqreturn_t
-stmp378x_i2c_isr(int this_irq, void *dev_id)
+static irqreturn_t stmp378x_i2c_isr(int this_irq, void *dev_id)
 {
  struct stmp378x_i2c_dev *dev = dev_id;
  u32 stat, ctrl;
- u32 done_mask =
-  BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
-  BM_I2C_CTRL1_BUS_FREE_IRQ ;
+// u32 done_mask =
+//  BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ /*|
+//  BM_I2C_CTRL1_BUS_FREE_IRQ*/ ;
 
  stat = __raw_readl(REGS_I2C_BASE + HW_I2C_CTRL1) & I2C_IRQ_MASK;
  if (!stat)
@@ -193,9 +198,9 @@ stmp378x_i2c_isr(int this_irq, void *dev_id)
   complete(&dev->cmd_complete);
   goto done;
  }
- if ((stat & done_mask)  == done_mask)
-  complete(&dev->cmd_complete);
-
+// if ((stat & done_mask) == done_mask) {
+//  complete(&dev->cmd_complete);
+// }
 
 done:
  __raw_writel(stat, REGS_I2C_BASE + HW_I2C_CTRL1_CLR);
@@ -242,6 +247,11 @@ stmp378x_i2c_probe(struct platform_device *pdev)
  dev->irq_dma = irq->start;
  dev->dev = &pdev->dev;
 
+ // Clear all IRQs
+ __raw_writel(0xFF, REGS_I2C_BASE + HW_I2C_CTRL1_CLR);
+
+ /* Will catch all error (IRQ mask) */
+ __raw_writel(0x0000FF00, REGS_I2C_BASE + HW_I2C_CTRL1_SET);
  err = request_irq(dev->irq_err, stmp378x_i2c_isr, 0, pdev->name, dev);
  if (err) {
   dev_err(&pdev->dev, "Can't get IRQ\n");
@@ -263,8 +273,8 @@ stmp378x_i2c_probe(struct platform_device *pdev)
  }
 
  /* Will catch all error (IRQ mask) */
- __raw_writel(0x0000FF00,
-  REGS_I2C_BASE + HW_I2C_CTRL1_SET);
+// __raw_writel(0x0000FF00,
+//  REGS_I2C_BASE + HW_I2C_CTRL1_SET);
 
  adap = &dev->adapter;
  i2c_set_adapdata(adap, dev);
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index 065205b..fc9a8cc 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -368,9 +368,6 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
  irqreturn_t ret, retval = IRQ_NONE;
  unsigned int status = 0;
 
- if (!(action->flags & IRQF_DISABLED))
-  local_irq_enable_in_hardirq();
-
  do {
   trace_irq_handler_entry(irq, action);
   ret = action->handler(irq, action->dev_id);

===============================================================

Here, the accelometers mma7456 and mma7450 are sharing the same driver. I add the attribute "mma7450_test" to do a full register read for testing purpose. Minor change to the i2C driver to make the transaction more robust.

 

Cheers.

Outcomes