summaryrefslogtreecommitdiff
path: root/driver
diff options
context:
space:
mode:
authorJan Dabros <jsd@semihalf.com>2020-11-27 18:35:00 +0100
committerCommit Bot <commit-bot@chromium.org>2020-12-08 19:23:53 +0000
commitb64904e81c66071aa39adb1bd6e9e001f876ef4d (patch)
tree3a91c0013f2e7f35cd69ebff91098901baa0ec00 /driver
parent22c30a4e561254d3e63303646cfb3d597abc4d0d (diff)
downloadchrome-ec-b64904e81c66071aa39adb1bd6e9e001f876ef4d.tar.gz
gl3590: Add initial support for USB HUB I2C interface
GL3590 is a USB hub with support for out-of-band transactions via I2C. Set of additional registers allow to disable/enable ports, query port status (including power-related information), handle over-current events and others. This initial driver implements method for reading and writing from/to GL3590 registers as well as event handler to parse asynchronous messages from hub. BUG:b:150323106,b:169929627 BRANCH:master TEST:buildall. Functionality can be tested on servo_v4p1 with a consecutive commit applied. Signed-off-by: Jan Dabros <jsd@semihalf.com> Change-Id: I385ff4790ddc367d07b7e588f4e10aeb83e9f1c0 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2563686 Reviewed-by: Brian Nemec <bnemec@chromium.org> Signed-off-by: Abe Levkoy <alevkoy@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2580261
Diffstat (limited to 'driver')
-rw-r--r--driver/build.mk3
-rw-r--r--driver/gl3590.c144
-rw-r--r--driver/gl3590.h19
3 files changed, 166 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk
index 33c61a4d7d..6a752ce72a 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -175,6 +175,9 @@ driver-$(CONFIG_USB_MUX_PS8742)+=usb_mux/ps8740.o
driver-$(CONFIG_USB_MUX_PS8743)+=usb_mux/ps8743.o
driver-$(CONFIG_USB_MUX_VIRTUAL)+=usb_mux/virtual.o
+# USB Hub with I2C interface
+driver-$(CONFIG_USB_HUB_GL3590)+=gl3590.o
+
# Type-C Power Path Controllers (PPC)
driver-$(CONFIG_USBC_PPC_AOZ1380)+=ppc/aoz1380.o
driver-$(CONFIG_USBC_PPC_SN5S330)+=ppc/sn5s330.o
diff --git a/driver/gl3590.c b/driver/gl3590.c
new file mode 100644
index 0000000000..bde2971553
--- /dev/null
+++ b/driver/gl3590.c
@@ -0,0 +1,144 @@
+#include <console.h>
+#include <i2c.h>
+#include <system.h>
+#include <util.h>
+
+#include <gl3590.h>
+
+/* GL3590 is unique in terms of i2c_read, since it doesn't support repeated
+ * start sequence. One need to issue two separate transactions - first is write
+ * with a register offset, then after a delay second transaction is actual read.
+ */
+int gl3590_read(int hub, uint8_t reg, uint8_t *data, int count)
+{
+ int rv;
+ struct uhub_i2c_iface_t *uhub_p = &uhub_config[hub];
+
+ i2c_lock(uhub_p->i2c_host_port, 1);
+ rv = i2c_xfer_unlocked(uhub_p->i2c_host_port,
+ uhub_p->i2c_addr,
+ &reg, 1,
+ NULL, 0,
+ I2C_XFER_SINGLE);
+ i2c_lock(uhub_p->i2c_host_port, 0);
+
+ if (rv)
+ return rv;
+
+ /* GL3590 requires at least 300us before data is ready */
+ udelay(400);
+
+ i2c_lock(uhub_p->i2c_host_port, 1);
+ rv = i2c_xfer_unlocked(uhub_p->i2c_host_port,
+ uhub_p->i2c_addr,
+ NULL, 0,
+ data, count,
+ I2C_XFER_SINGLE);
+ i2c_lock(uhub_p->i2c_host_port, 0);
+
+ return rv;
+};
+
+int gl3590_write(int hub, uint8_t reg, uint8_t *data, int count)
+{
+ int rv;
+ uint8_t buf[5];
+ struct uhub_i2c_iface_t *uhub_p = &uhub_config[hub];
+
+ /* GL3590 registers accept 4 bytes at max */
+ if (count > (sizeof(buf) - 1)) {
+ ccprintf("Too many bytes to write");
+ return EC_ERROR_INVAL;
+ }
+
+ buf[0] = reg;
+ memcpy(&buf[1], data, count);
+
+ i2c_lock(uhub_p->i2c_host_port, 1);
+ rv = i2c_xfer_unlocked(uhub_p->i2c_host_port,
+ uhub_p->i2c_addr,
+ buf, count + 1,
+ NULL, 0,
+ I2C_XFER_SINGLE);
+ i2c_lock(uhub_p->i2c_host_port, 0);
+
+ return rv;
+}
+
+void gl3590_irq_handler(int hub)
+{
+ uint8_t buf = 0;
+ uint8_t res_reg[2];
+
+ /* Verify that irq is pending */
+ if (gl3590_read(hub, GL3590_INT_REG, &buf, sizeof(buf))) {
+ ccprintf("Cannot read from the host hub i2c\n");
+ goto exit;
+ }
+
+ if ((buf & GL3590_INT_PENDING) == 0) {
+ ccprintf("Invalid hub event\n");
+ goto exit;
+ }
+
+ /* Get the hub event reason */
+ if (gl3590_read(hub, GL3590_RESPONSE_REG, res_reg, sizeof(res_reg))) {
+ ccprintf("Cannot read from the host hub i2c\n");
+ goto exit;
+ }
+
+ if ((res_reg[0] & GL3590_RESPONSE_REG_SYNC_MASK) == 0)
+ ccprintf("Host hub response: ");
+ else
+ ccprintf("Host hub event! ");
+
+ switch(res_reg[0]) {
+ case 0x0:
+ ccprintf("No response");
+ break;
+ case 0x1:
+ ccprintf("Successful");
+ break;
+ case 0x2:
+ ccprintf("Invalid command");
+ break;
+ case 0x3:
+ ccprintf("Invalid arguments");
+ break;
+ case 0x4:
+ ccprintf("Invalid port: %d", res_reg[1]);
+ break;
+ case 0x5:
+ ccprintf("Command not completed");
+ break;
+ case 0x80:
+ ccprintf("Reset complete");
+ break;
+ case 0x81:
+ ccprintf("Power operation mode change");
+ break;
+ case 0x82:
+ ccprintf("Connect change");
+ break;
+ case 0x83:
+ ccprintf("Error on the specific port");
+ break;
+ case 0x84:
+ ccprintf("Hub state change");
+ break;
+ case 0x85:
+ ccprintf("SetFeature PORT_POWER failure");
+ break;
+ default:
+ ccprintf("Unknown value: 0x%0x", res_reg[0]);
+ }
+ ccprintf("\n");
+
+ if (res_reg[1])
+ ccprintf("Affected port %d\n", res_reg[1]);
+
+exit:
+ /* Try to clear interrupt */
+ buf = GL3590_INT_CLEAR;
+ gl3590_write(hub, GL3590_INT_REG, &buf, sizeof(buf));
+}
diff --git a/driver/gl3590.h b/driver/gl3590.h
new file mode 100644
index 0000000000..ef018d0685
--- /dev/null
+++ b/driver/gl3590.h
@@ -0,0 +1,19 @@
+/* Registers definitions */
+#define GL3590_INT_REG 0x1
+#define GL3590_INT_PENDING 0x1
+#define GL3590_INT_CLEAR 0x1
+#define GL3590_RESPONSE_REG 0x2
+#define GL3590_RESPONSE_REG_SYNC_MASK 0x80
+
+#define GL3590_I2C_ADDR0 0x50
+
+int gl3590_read(int hub, uint8_t reg, uint8_t *data, int count);
+int gl3590_write(int hub, uint8_t reg, uint8_t *data, int count);
+void gl3590_irq_handler(int hub);
+
+/* Generic USB HUB I2C interface */
+struct uhub_i2c_iface_t {
+ int i2c_host_port;
+ int i2c_addr;
+};
+extern struct uhub_i2c_iface_t uhub_config[];