summaryrefslogtreecommitdiff
path: root/driver/gl3590.c
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-07 08:35:41 +0000
commite6e50043b2e199cac5c596541405765aa984083e (patch)
treed9fe2d34cfd1f2d7fe4d546d7befa221358f3d99 /driver/gl3590.c
parentf8bd5bd04ce255df7a84db22ffb3a491f4ef7ec2 (diff)
downloadchrome-ec-e6e50043b2e199cac5c596541405765aa984083e.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>
Diffstat (limited to 'driver/gl3590.c')
-rw-r--r--driver/gl3590.c144
1 files changed, 144 insertions, 0 deletions
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));
+}