diff options
author | Jan Dabros <jsd@semihalf.com> | 2020-11-27 18:35:00 +0100 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-12-07 08:35:41 +0000 |
commit | e6e50043b2e199cac5c596541405765aa984083e (patch) | |
tree | d9fe2d34cfd1f2d7fe4d546d7befa221358f3d99 /driver/gl3590.c | |
parent | f8bd5bd04ce255df7a84db22ffb3a491f4ef7ec2 (diff) | |
download | chrome-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.c | 144 |
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, + ®, 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)); +} |