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
husb311_read16(struct husb311_chip * chip,unsigned int reg)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
husb311_write8(struct husb311_chip * chip,unsigned int reg,u8 val)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
husb311_write16(struct husb311_chip * chip,unsigned int reg,u16 val)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
tdata_to_husb311(struct tcpci_data * tdata)82 static struct husb311_chip *tdata_to_husb311(struct tcpci_data *tdata)
83 {
84 return container_of(tdata, struct husb311_chip, data);
85 }
86
husb311_sw_reset(struct husb311_chip * chip)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
husb311_init(struct tcpci * tcpci,struct tcpci_data * tdata)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
husb311_check_revision(struct husb311_chip * chip)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
husb311_probe(struct udevice * dev)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
husb311_remove(struct udevice * dev)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
husb311_get_voltage(struct udevice * dev)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
husb311_get_current(struct udevice * dev)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
husb311_get_online(struct udevice * dev)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