/* * tusb320hai.c * * Drivers for usb type-C interface's CC-Logic chip of TI */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cclogic-core.h" #define DRIVER_NAME "ti,tusb320hai" #define MAX_REG_NUM 11 #define TUSB320_DEVID "023BSUT" /* tusb320hai_read_i2c() */ static int tusb320hai_read_i2c(struct i2c_client *client, u8 reg) { struct i2c_msg msg[2]; int data = 0; int ret; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); if (!client->adapter) return -ENODEV; msg[0].addr = client->addr; msg[0].flags = 0; msg[0].buf = ® msg[0].len = sizeof(reg); msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].buf = (char *)&data; msg[1].len = 1; ret = i2c_transfer(client->adapter, msg, 2); if (ret != 2) { dev_err(&client->dev, "cclogic:%s-->i2c_transfer error\n", __func__); return ret; } return data; } /* tusb320hai_recv_i2c() */ static int tusb320hai_recv_i2c(struct i2c_client *client, char baseaddr, char *buf, int len) { struct i2c_msg msg[2]; int ret; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); if (!client->adapter) return -ENODEV; msg[0].addr = client->addr; msg[0].flags = 0; msg[0].buf = &baseaddr; msg[0].len = 1; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].buf = buf; msg[1].len = len; ret = i2c_transfer(client->adapter, msg, 2); if (ret != 2) { dev_err(&client->dev, "cclogic:%s-->i2c_transfer error\n", __func__); return ret; } return 0; } /* tusb320hai_write_i2c() */ static int tusb320hai_write_i2c(struct i2c_client *client, u8 reg, u8 data) { int ret = 0; struct i2c_msg msg; u8 buf[2]; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); if (!client->adapter) return -ENODEV; buf[0] = reg; buf[1] = data; msg.addr = client->addr; msg.flags = 0; msg.buf = buf; msg.len = sizeof(buf); ret = i2c_transfer(client->adapter, &msg, 1); if (ret != 1) { dev_err(&client->dev, "cclogic:%s-->i2c_transfer error\n", __func__); return ret; } return 0; } /* tusb320hai_parse_cclogic_state() */ static void tusb320hai_parse_cclogic_state(int reg8, int reg9, struct cclogic_state *result) { pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); result->vbus = false; result->cc = CCLOGIC_CC_UNKNOWN; result->evt = CCLOGIC_EVENT_NONE; result->device = CCLOGIC_NO_DEVICE; result->charger = CCLOGIC_CURRENT_NONE; if (!(reg9&0x10)) {/* No interrupt */ result->evt = CCLOGIC_EVENT_NONE; } else{ result->evt = CCLOGIC_EVENT_ATTACHED; } if (reg9&0x20) result->cc = CCLOGIC_CC2; else result->cc = CCLOGIC_CC1; switch (reg9&0xc0) { case 0x00:/* nothing attached */ if (reg9&0x10) result->evt = CCLOGIC_EVENT_DETACHED; result->vbus = false; result->device = CCLOGIC_NO_DEVICE; break; case 0x40: result->device = CCLOGIC_USB_DEVICE; result->vbus = false; break; case 0x80: result->device = CCLOGIC_USB_HOST; result->vbus = true; break; case 0xC0:/* accessory */ switch (reg8&0x0e) { case 0x00: /* No Accessory attached */ if (reg9&0x10) result->evt = CCLOGIC_EVENT_DETACHED; result->vbus = false; result->device = CCLOGIC_NO_DEVICE; break; case 0x08: /* Audio Accessory DFP */ result->vbus = false; result->device = CCLOGIC_AUDIO_DEVICE; break; case 0x0a: /* Audio Accessory UFP */ result->vbus = true; result->device = CCLOGIC_AUDIO_DEVICE; break; case 0x0e:/* Debug Accessory UFP */ result->vbus = true; result->device = CCLOGIC_DEBUG_DEVICE; break; case 0x0c:/* Debug Accessory DFP set to Hi-Z */ result->vbus = false; result->device = CCLOGIC_DEBUG_DEVICE; break; default:/* Reserve */ result->vbus = false; result->device = CCLOGIC_NO_DEVICE; break; } break; } switch (reg8&0x30) { case 0x00: result->charger = CCLOGIC_CURRENT_DEFAULT; break; case 0x10: result->charger = CCLOGIC_CURRENT_MEDIUM; break; case 0x20: result->charger = CCLOGIC_CURRENT_ACCESSORY; /* result->vbus = false; */ break; case 0x30: result->charger = CCLOGIC_CURRENT_HIGH; break; } } /* tusb320hai_reset_chip() */ static int tusb320hai_reset_chip(struct i2c_client *client) { int retries = 10; int regval; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); regval = tusb320hai_write_i2c(client, 0xa, 0x8); if (regval) { dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__); return regval; } while (retries--) { regval = tusb320hai_read_i2c(client, 0xa); if (regval < 0) { dev_err(&client->dev, "cclogic:%s: i2c read error\n", __func__); return regval; } if (!(regval&0x08)) return 0; msleep(100); } if (regval&0x08) return -1; return 0; } /* tusb320hai_trymode() */ static int tusb320hai_trymode(struct i2c_client *client, int mode) { int ret; int regval; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); ret = tusb320hai_write_i2c(client, 0xa, 0x33); if (ret) { dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__); return ret; } if (mode == CCLOGIC_MODE_UFP) { regval = 0x13; pr_debug("cclogic:trymode sink\n"); } else{ regval = 0x23; pr_debug("cclogic:trymode source\n"); } ret = tusb320hai_write_i2c(client, 0xa, regval); if (ret) { dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__); return ret; } msleep(6); ret = tusb320hai_write_i2c(client, 0xa, regval & 0xfe); if (ret) { dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__); return ret; } return ret; } /* tusb320hai_config_chip() */ static int tusb320hai_config_chip(struct i2c_client *client) { int ret; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); ret = tusb320hai_write_i2c(client, 0x8, 0); if (ret) { dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__); return ret; } ret = tusb320hai_write_i2c(client, 0xa, 0x32); if (ret) { dev_err(&client->dev, "cclogic:%s: i2c write error\n", __func__); return ret; } return ret; } /* tusb320hai_check_chip() */ static int tusb320hai_check_chip(struct i2c_client *client) { char buf[8]; int ret; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); /* ID check */ ret = tusb320hai_recv_i2c(client, 0, buf, 8); if (ret) { dev_err(&client->dev, "cclogic:%s: i2c recv error\n", __func__); return -ENODEV; } if (memcmp(buf, TUSB320_DEVID, sizeof(TUSB320_DEVID))) { dev_err(&client->dev, "cclogic:%s: devid mismatch (%s != %s)\n", __func__, buf, TUSB320_DEVID); return -ENODEV; } return 0; } /* tusb320hai_ack_irq() */ static int tusb320hai_ack_irq(struct i2c_client *client) { int reg9, ret; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); reg9 = tusb320hai_read_i2c(client, 0x9); if (reg9 < 0) { dev_err(&client->dev, "cclogic:%s-->i2c read error\n", __func__); return reg9; } /* clear interrupt */ ret = tusb320hai_write_i2c(client, 0x9, reg9); if (ret) { dev_err(&client->dev, "cclogic:%s-->i2c write error\n", __func__); return ret; } return 0; } /* tusb320hai_get_state() */ static int tusb320hai_get_state(struct i2c_client *client, struct cclogic_state *state) { int reg8, reg9, rega; static int flag; pr_debug("cclogic:[%s][%d]\n", __func__, __LINE__); reg9 = tusb320hai_read_i2c(client, 0x9); if (reg9 < 0) { dev_err(&client->dev, "cclogic:%s-->i2c read error,reg9=%d\n", __func__, reg9); return reg9; } reg8 = tusb320hai_read_i2c(client, 0x8); if (reg8 < 0) { dev_err(&client->dev, "cclogic:%s-->i2c read error,reg8=%d\n", __func__, reg8); return reg8; } pr_debug("cclogic:%s-->i2c register value: reg8=0x%02x reg9=0x%02x\n", __func__, reg8, reg9); tusb320hai_parse_cclogic_state(reg8, reg9, state); #if 1 /* for chip bug */ if (state->evt == CCLOGIC_EVENT_DETACHED) {/* skip detach event */ return 0; } rega = tusb320hai_read_i2c(client, 0xa); if (rega < 0) { dev_err(&client->dev, "cclogic:%s: i2c read error\n", __func__); return rega; } if ((rega & 0x30) != 0x30) return 0; if (!flag && (state->device == CCLOGIC_USB_DEVICE)) { tusb320hai_reset_chip(client); tusb320hai_config_chip(client); flag = 1; return -1; } flag = 0; #endif return 0; } static struct cclogic_chip tusb320hai_chip = { .chip_name = DRIVER_NAME, .get_state = tusb320hai_get_state, .ack_irq = tusb320hai_ack_irq, .chip_config = tusb320hai_config_chip, .chip_reset = tusb320hai_reset_chip, .chip_check = tusb320hai_check_chip, .chip_trymode = tusb320hai_trymode, .typec_version = 11,/* spec 1.1 */ .support = CCLOGIC_SUPPORT_MODE_DUAL, .read = tusb320hai_read_i2c, .write = tusb320hai_write_i2c, }; /* tusb320hai_init() */ static int __init tusb320hai_init(void) { pr_info("cclogic:[%s][%d]\n", __func__, __LINE__); return cclogic_register(&tusb320hai_chip); } /* tusb320hai_exit() */ static void __exit tusb320hai_exit(void) { pr_info("cclogic:[%s][%d]\n", __func__, __LINE__); cclogic_unregister(&tusb320hai_chip); return; } late_initcall(tusb320hai_init); module_exit(tusb320hai_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Yang ShaoYing "); MODULE_DESCRIPTION("Drivers for usb type-C CC-Logic chip of TI Tusb320"); MODULE_ALIAS("platform:cc-logic");