1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2021 Rockchip Co.,Ltd. 4 * Author: Wang Jie <dave.wang@rock-chips.com> 5 * 6 * Hynetek Husb311 Type-C Chip Driver 7 */ 8 9 #include <dm.h> 10 #include <i2c.h> 11 #include <asm/gpio.h> 12 #include <power/power_delivery/tcpm.h> 13 #include <power/power_delivery/power_delivery.h> 14 #include "tcpci.h" 15 16 #define HUSB311_VID 0x2E99 17 #define HUSB311_PID 0x0311 18 #define HUSB311_TCPC_I2C_RESET 0x9E 19 #define HUSB311_TCPC_SOFTRESET 0xA0 20 #define HUSB311_TCPC_FILTER 0xA1 21 #define HUSB311_TCPC_TDRP 0xA2 22 #define HUSB311_TCPC_DCSRCDRP 0xA3 23 #define HUSB311_I2C_RETRY_MAX_CNT 3 24 25 struct husb311_chip { 26 struct udevice *udev; 27 struct tcpci_data data; 28 struct tcpci *tcpci; 29 }; 30 31 static int husb311_read16(struct husb311_chip *chip, unsigned int reg) 32 { 33 int ret = 0; 34 u8 buffer[2]; 35 36 ret = dm_i2c_read(chip->udev, reg, buffer, 2); 37 if (ret < 0) { 38 printf("%s: cannot read %02x, ret=%d\n", 39 __func__, reg, ret); 40 return ret; 41 } 42 ret = ((buffer[1] << 8) & 0xFF00) + (buffer[0] & 0xFF); 43 44 return ret; 45 } 46 47 static int husb311_write8(struct husb311_chip *chip, unsigned int reg, u8 val) 48 { 49 int ret = 0; 50 int i; 51 52 for (i = 0; i < HUSB311_I2C_RETRY_MAX_CNT; i++) { 53 ret = dm_i2c_write(chip->udev, reg, &val, 1); 54 if (!ret) 55 break; 56 else 57 udelay(200); 58 } 59 60 if (ret) 61 printf("%s: cannot write 0x%02x to 0x%02x, ret=%d\n", 62 __func__, val, reg, ret); 63 64 return ret; 65 } 66 67 static int husb311_write16(struct husb311_chip *chip, unsigned int reg, u16 val) 68 { 69 int ret = 0; 70 u8 buffer[2]; 71 72 buffer[0] = val & 0xFF; 73 buffer[1] = (val >> 8) & 0xFF; 74 ret = dm_i2c_write(chip->udev, reg, buffer, 2); 75 if (ret) 76 printf("%s: cannot write 0x%02x, len=%d, ret=%d\n", 77 __func__, reg, 2, ret); 78 79 return ret; 80 } 81 82 static struct husb311_chip *tdata_to_husb311(struct tcpci_data *tdata) 83 { 84 return container_of(tdata, struct husb311_chip, data); 85 } 86 87 static int husb311_sw_reset(struct husb311_chip *chip) 88 { 89 /* soft reset */ 90 return husb311_write8(chip, HUSB311_TCPC_SOFTRESET, 0x01); 91 } 92 93 static int husb311_init(struct tcpci *tcpci, struct tcpci_data *tdata) 94 { 95 int ret; 96 struct husb311_chip *chip = tdata_to_husb311(tdata); 97 98 /* I2C reset : (val + 1) * 12.5ms */ 99 ret = husb311_write8(chip, HUSB311_TCPC_I2C_RESET, 0x8F); 100 /* tTCPCfilter : (26.7 * val) us */ 101 ret |= husb311_write8(chip, HUSB311_TCPC_FILTER, 0x0F); 102 /* tDRP : (51.2 + 6.4 * val) ms */ 103 ret |= husb311_write8(chip, HUSB311_TCPC_TDRP, 0x04); 104 /* dcSRC.DRP : 33% */ 105 ret |= husb311_write16(chip, HUSB311_TCPC_DCSRCDRP, 330); 106 107 if (ret) 108 printf("%s: fail to init registers(%d)\n", __func__, ret); 109 110 return ret; 111 } 112 113 static int husb311_check_revision(struct husb311_chip *chip) 114 { 115 int ret; 116 117 ret = husb311_read16(chip, TCPC_VENDOR_ID); 118 if (ret < 0) { 119 printf("%s: fail to read Vendor id(%d)\n", __func__, ret); 120 return ret; 121 } 122 123 if (ret != HUSB311_VID) { 124 printf("%s: vid is not correct, 0x%04x\n", __func__, ret); 125 return -ENODEV; 126 } 127 128 ret = husb311_read16(chip, TCPC_PRODUCT_ID); 129 if (ret < 0) { 130 printf("%s: fail to read Product id(%d)\n", __func__, ret); 131 return ret; 132 } 133 134 if (ret != HUSB311_PID) { 135 printf("%s: pid is not correct, 0x%04x\n", __func__, ret); 136 return -ENODEV; 137 } 138 139 return 0; 140 } 141 142 static int husb311_probe(struct udevice *dev) 143 { 144 int ret; 145 struct husb311_chip *chip = dev_get_priv(dev); 146 147 chip->udev = dev; 148 149 ret = husb311_check_revision(chip); 150 if (ret < 0) { 151 printf("%s: check vid/pid fail(%d)\n", __func__, ret); 152 return ret; 153 } 154 155 ret = husb311_sw_reset(chip); 156 if (ret) { 157 printf("%s: fail to soft reset, ret = %d\n", __func__, ret); 158 return ret; 159 } 160 161 chip->data.init = husb311_init; 162 chip->tcpci = tcpci_register_port(chip->udev, &chip->data); 163 if (IS_ERR(chip->tcpci)) 164 return PTR_ERR(chip->tcpci); 165 166 return 0; 167 } 168 169 static int husb311_remove(struct udevice *dev) 170 { 171 struct husb311_chip *chip = dev_get_priv(dev); 172 int ret = 0; 173 174 printf("PD chip husb311 remove\n"); 175 /* Disable chip interrupts before unregistering port */ 176 ret = husb311_write16(chip, TCPC_ALERT_MASK, 0); 177 if (ret < 0) 178 return ret; 179 180 tcpci_unregister_port(chip->tcpci); 181 182 return 0; 183 } 184 185 static int husb311_get_voltage(struct udevice *dev) 186 { 187 struct husb311_chip *chip = dev_get_priv(dev); 188 189 return tcpci_get_voltage_fun(chip->tcpci); 190 } 191 192 static int husb311_get_current(struct udevice *dev) 193 { 194 struct husb311_chip *chip = dev_get_priv(dev); 195 196 return tcpci_get_current_fun(chip->tcpci); 197 } 198 199 static int husb311_get_online(struct udevice *dev) 200 { 201 struct husb311_chip *chip = dev_get_priv(dev); 202 203 return tcpci_get_online_fun(chip->tcpci); 204 } 205 206 static struct dm_power_delivery_ops husb311_ops = { 207 .get_voltage = husb311_get_voltage, 208 .get_current = husb311_get_current, 209 .get_online = husb311_get_online, 210 }; 211 212 static const struct udevice_id husb311_ids[] = { 213 { .compatible = "hynetek,husb311" }, 214 {}, 215 }; 216 217 U_BOOT_DRIVER(husb311) = { 218 .name = "husb311", 219 .id = UCLASS_PD, 220 .of_match = husb311_ids, 221 .ops = &husb311_ops, 222 .probe = husb311_probe, 223 .remove = husb311_remove, 224 .priv_auto_alloc_size = sizeof(struct husb311_chip), 225 }; 226 227 MODULE_AUTHOR("Wang Jie <dave.wang@rock-chips.com>"); 228 MODULE_DESCRIPTION("Husb311 USB Type-C Port Controller Interface Driver"); 229 MODULE_LICENSE("GPL v2"); 230