dm: cros_ec: Convert the I2C tunnel code to use driver model
authorSimon Glass <sjg@chromium.org>
Mon, 3 Aug 2015 14:19:23 +0000 (08:19 -0600)
committerSimon Glass <sjg@chromium.org>
Thu, 6 Aug 2015 03:06:11 +0000 (21:06 -0600)
The Chrome OS EC supports tunnelling through to an I2C bus on the EC. This
currently uses a copy of the I2C command code and a special 'crosec'
sub-command.

With driver model we can define an I2C bus which tunnels through to the EC,
and use the normal 'i2c' command to access it. This simplifies the code and
removes some duplication.

Add an I2C driver which tunnels through to the EC. Adjust the EC code to
support binding child devices so that it can be set up. Adjust the existing
I2C xfer function to fit driver model better.

For now the old code remains to allow things to still work. It will be
removed in a later patch once the new flow is fully enabled.

Signed-off-by: Simon Glass <sjg@chromium.org>
configs/peach-pi_defconfig
configs/peach-pit_defconfig
drivers/i2c/Kconfig
drivers/i2c/Makefile
drivers/i2c/cros_ec_tunnel.c [new file with mode: 0644]
drivers/misc/cros_ec.c
drivers/power/pmic/pmic_tps65090_ec.c
include/cros_ec.h

index c17fc73253c2a0eb7cbb6da6a8d66a53a61a0c52..5050f5b08fb392c4fb84c08d5f432628b5a3500b 100644 (file)
@@ -12,3 +12,5 @@ CONFIG_CROS_EC_SPI=y
 CONFIG_CROS_EC_KEYB=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_CROS_EC_TUNNEL=y
index 8f217221a6ea1bbfa9d58884996ec3cd8af59b70..d19bff28b5b4206d10b732e72a44f1d1eb0ed4be 100644 (file)
@@ -12,3 +12,5 @@ CONFIG_CROS_EC_SPI=y
 CONFIG_CROS_EC_KEYB=y
 CONFIG_USB=y
 CONFIG_DM_USB=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_CROS_EC_TUNNEL=y
index caee3d83386e42845d9421a1b1ff0acbb17abdb3..e861b536863a30d272b1fc8c68e287df8d56e285 100644 (file)
@@ -19,6 +19,17 @@ config DM_I2C_COMPAT
          to convert all code for a board in a single commit. It should not
          be enabled for any board in an official release.
 
+config I2C_CROS_EC_TUNNEL
+       tristate "Chrome OS EC tunnel I2C bus"
+       depends on CROS_EC
+       help
+         This provides an I2C bus that will tunnel i2c commands through to
+         the other side of the Chrome OS EC to the I2C bus connected there.
+         This will work whatever the interface used to talk to the EC (SPI,
+         I2C or LPC). Some Chromebooks use this when the hardware design
+         does not allow direct access to the main PMIC from the AP.
+
+
 config DM_I2C_GPIO
        bool "Enable Driver Model for software emulated I2C bus driver"
        depends on DM_I2C && DM_GPIO
index dc9e81bf76af245afa860515bcac0ca9fa075b0b..7f01fce2e745d757d2fcb656ef883874f9f88c7c 100644 (file)
@@ -7,6 +7,7 @@
 obj-$(CONFIG_DM_I2C) += i2c-uclass.o
 obj-$(CONFIG_DM_I2C_COMPAT) += i2c-uclass-compat.o
 obj-$(CONFIG_DM_I2C_GPIO) += i2c-gpio.o
+obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o
 
 obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o
 obj-$(CONFIG_I2C_MV) += mv_i2c.o
diff --git a/drivers/i2c/cros_ec_tunnel.c b/drivers/i2c/cros_ec_tunnel.c
new file mode 100644 (file)
index 0000000..7ab1fd8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <cros_ec.h>
+#include <errno.h>
+#include <i2c.h>
+
+static int cros_ec_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
+{
+       return 0;
+}
+
+static int cros_ec_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
+                           int nmsgs)
+{
+       return cros_ec_i2c_tunnel(dev->parent, msg, nmsgs);
+}
+
+static const struct dm_i2c_ops cros_ec_i2c_ops = {
+       .xfer           = cros_ec_i2c_xfer,
+       .set_bus_speed  = cros_ec_i2c_set_bus_speed,
+};
+
+static const struct udevice_id cros_ec_i2c_ids[] = {
+       { .compatible = "google,cros-ec-i2c-tunnel" },
+       { }
+};
+
+U_BOOT_DRIVER(cros_ec_tunnel) = {
+       .name   = "cros_ec_tunnel",
+       .id     = UCLASS_I2C,
+       .of_match = cros_ec_i2c_ids,
+       .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip),
+       .ops    = &cros_ec_i2c_ops,
+};
index 4b6ac6a6c01f75d97db362c0a169654c7377dd35..ae525616095ad69ff3add4e89a20f9b0d4c179ab 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/io.h>
 #include <asm-generic/gpio.h>
 #include <dm/device-internal.h>
+#include <dm/root.h>
 #include <dm/uclass-internal.h>
 
 #ifdef DEBUG_TRACE
@@ -1053,8 +1054,8 @@ int cros_ec_decode_ec_flash(const void *blob, int node,
        return 0;
 }
 
-int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr,
-                    int alen, uchar *buffer, int len, int is_read)
+int cros_ec_i2c_xfer_old(struct cros_ec_dev *dev, uchar chip, uint addr,
+                        int alen, uchar *buffer, int len, int is_read)
 {
        union {
                struct ec_params_i2c_passthru p;
@@ -1134,6 +1135,81 @@ int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr,
        return 0;
 }
 
+int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *in, int nmsgs)
+{
+       struct cros_ec_dev *cdev = dev_get_uclass_priv(dev);
+       union {
+               struct ec_params_i2c_passthru p;
+               uint8_t outbuf[EC_PROTO2_MAX_PARAM_SIZE];
+       } params;
+       union {
+               struct ec_response_i2c_passthru r;
+               uint8_t inbuf[EC_PROTO2_MAX_PARAM_SIZE];
+       } response;
+       struct ec_params_i2c_passthru *p = &params.p;
+       struct ec_response_i2c_passthru *r = &response.r;
+       struct ec_params_i2c_passthru_msg *msg;
+       uint8_t *pdata, *read_ptr = NULL;
+       int read_len;
+       int size;
+       int rv;
+       int i;
+
+       p->port = 0;
+
+       p->num_msgs = nmsgs;
+       size = sizeof(*p) + p->num_msgs * sizeof(*msg);
+
+       /* Create a message to write the register address and optional data */
+       pdata = (uint8_t *)p + size;
+
+       read_len = 0;
+       for (i = 0, msg = p->msg; i < nmsgs; i++, msg++, in++) {
+               bool is_read = in->flags & I2C_M_RD;
+
+               msg->addr_flags = in->addr;
+               msg->len = in->len;
+               if (is_read) {
+                       msg->addr_flags |= EC_I2C_FLAG_READ;
+                       read_len += in->len;
+                       read_ptr = in->buf;
+                       if (sizeof(*r) + read_len > sizeof(response)) {
+                               puts("Read length too big for buffer\n");
+                               return -1;
+                       }
+               } else {
+                       if (pdata - (uint8_t *)p + in->len > sizeof(params)) {
+                               puts("Params too large for buffer\n");
+                               return -1;
+                       }
+                       memcpy(pdata, in->buf, in->len);
+                       pdata += in->len;
+               }
+       }
+
+       rv = ec_command(cdev, EC_CMD_I2C_PASSTHRU, 0, p, pdata - (uint8_t *)p,
+                       r, sizeof(*r) + read_len);
+       if (rv < 0)
+               return rv;
+
+       /* Parse response */
+       if (r->i2c_status & EC_I2C_STATUS_ERROR) {
+               printf("Transfer failed with status=0x%x\n", r->i2c_status);
+               return -1;
+       }
+
+       if (rv < sizeof(*r) + read_len) {
+               puts("Truncated read response\n");
+               return -1;
+       }
+
+       /* We only support a single read message for each transfer */
+       if (read_len)
+               memcpy(read_ptr, r->data, read_len);
+
+       return 0;
+}
+
 #ifdef CONFIG_CMD_CROS_EC
 
 /**
@@ -1267,8 +1343,8 @@ static int cros_ec_i2c_md(struct cros_ec_dev *dev, int flag, int argc,
 
                linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
 
-               if (cros_ec_i2c_xfer(dev, chip, addr, alen, linebuf, linebytes,
-                                    1))
+               if (cros_ec_i2c_xfer_old(dev, chip, addr, alen, linebuf,
+                                        linebytes, 1))
                        puts("Error reading the chip.\n");
                else {
                        printf("%04x:", addr);
@@ -1333,7 +1409,7 @@ static int cros_ec_i2c_mw(struct cros_ec_dev *dev, int flag, int argc,
                count = 1;
 
        while (count-- > 0) {
-               if (cros_ec_i2c_xfer(dev, chip, addr++, alen, &byte, 1, 0))
+               if (cros_ec_i2c_xfer_old(dev, chip, addr++, alen, &byte, 1, 0))
                        puts("Error writing the chip.\n");
                /*
                 * Wait for the write to complete.  The write can take
@@ -1633,6 +1709,12 @@ static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        return ret;
 }
 
+int cros_ec_post_bind(struct udevice *dev)
+{
+       /* Scan for available EC devices (e.g. I2C tunnel) */
+       return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
+}
+
 U_BOOT_CMD(
        crosec, 6,      1,      do_cros_ec,
        "CROS-EC utility command",
@@ -1661,4 +1743,5 @@ UCLASS_DRIVER(cros_ec) = {
        .id             = UCLASS_CROS_EC,
        .name           = "cros_ec",
        .per_device_auto_alloc_size = sizeof(struct cros_ec_dev),
+       .post_bind      = cros_ec_post_bind,
 };
index ac0d44fec839d1cf5dcfac106f9e1c648212fffe..f79a8782c9cd269775a2ce608a8aa6d5a8441ecb 100644 (file)
@@ -56,8 +56,8 @@ enum {
  */
 static int tps65090_read(u32 reg, u8 *val)
 {
-       return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1,
-                               val, 1, true);
+       return cros_ec_i2c_xfer_old(config.dev, TPS65090_ADDR, reg, 1,
+                                   val, 1, true);
 }
 
 /**
@@ -69,8 +69,8 @@ static int tps65090_read(u32 reg, u8 *val)
  */
 static int tps65090_write(u32 reg, u8 val)
 {
-       return cros_ec_i2c_xfer(config.dev, TPS65090_ADDR, reg, 1,
-                               &val, 1, false);
+       return cros_ec_i2c_xfer_old(config.dev, TPS65090_ADDR, reg, 1,
+                                   &val, 1, false);
 }
 
 /**
index 3b2be2c2fa642dc91c8511f07b9960624f10f5b3..41951c39b65785969079d1c16c4cd884aed95d69 100644 (file)
@@ -390,6 +390,16 @@ int cros_ec_decode_ec_flash(const void *blob, int node,
  */
 void cros_ec_check_keyboard(struct cros_ec_dev *dev);
 
+struct i2c_msg;
+/*
+ * Tunnel an I2C transfer to the EC
+ *
+ * @param dev          CROS-EC device
+ * @param msg          List of messages to transfer
+ * @param nmsgs                Number of messages to transfer
+ */
+int cros_ec_i2c_tunnel(struct udevice *dev, struct i2c_msg *msg, int nmsgs);
+
 /*
  * Tunnel an I2C transfer to the EC
  *
@@ -401,7 +411,7 @@ void cros_ec_check_keyboard(struct cros_ec_dev *dev);
  * @param len          Length of buffer
  * @param is_read      1 if this is a read, 0 if this is a write
  */
-int cros_ec_i2c_xfer(struct cros_ec_dev *dev, uchar chip, uint addr,
-                    int alen, uchar *buffer, int len, int is_read);
+int cros_ec_i2c_xfer_old(struct cros_ec_dev *dev, uchar chip, uint addr,
+                        int alen, uchar *buffer, int len, int is_read);
 
 #endif