aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/typec/ucsi/Kconfig10
-rw-r--r--drivers/usb/typec/ucsi/Makefile2
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c307
3 files changed, 319 insertions, 0 deletions
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index e36d6c73c4a4..78118883f96c 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -23,6 +23,16 @@ config TYPEC_UCSI
23 23
24if TYPEC_UCSI 24if TYPEC_UCSI
25 25
26config UCSI_CCG
27 tristate "UCSI Interface Driver for Cypress CCGx"
28 depends on I2C
29 help
30 This driver enables UCSI support on platforms that expose a
31 Cypress CCGx Type-C controller over I2C interface.
32
33 To compile the driver as a module, choose M here: the module will be
34 called ucsi_ccg.
35
26config UCSI_ACPI 36config UCSI_ACPI
27 tristate "UCSI ACPI Interface Driver" 37 tristate "UCSI ACPI Interface Driver"
28 depends on ACPI 38 depends on ACPI
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index 7afbea512207..2f4900b26210 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -8,3 +8,5 @@ typec_ucsi-y := ucsi.o
8typec_ucsi-$(CONFIG_TRACING) += trace.o 8typec_ucsi-$(CONFIG_TRACING) += trace.o
9 9
10obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o 10obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
11
12obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
new file mode 100644
index 000000000000..de8a43bdff68
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -0,0 +1,307 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * UCSI driver for Cypress CCGx Type-C controller
4 *
5 * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved.
6 * Author: Ajay Gupta <ajayg@nvidia.com>
7 *
8 * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c
9 */
10#include <linux/acpi.h>
11#include <linux/delay.h>
12#include <linux/i2c.h>
13#include <linux/module.h>
14#include <linux/pci.h>
15#include <linux/platform_device.h>
16
17#include <asm/unaligned.h>
18#include "ucsi.h"
19
20struct ucsi_ccg {
21 struct device *dev;
22 struct ucsi *ucsi;
23 struct ucsi_ppm ppm;
24 struct i2c_client *client;
25};
26
27#define CCGX_RAB_INTR_REG 0x06
28#define CCGX_RAB_UCSI_CONTROL 0x39
29#define CCGX_RAB_UCSI_CONTROL_START BIT(0)
30#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1)
31#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff))
32
33static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
34{
35 struct i2c_client *client = uc->client;
36 const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
37 unsigned char buf[2];
38 struct i2c_msg msgs[] = {
39 {
40 .addr = client->addr,
41 .flags = 0x0,
42 .len = sizeof(buf),
43 .buf = buf,
44 },
45 {
46 .addr = client->addr,
47 .flags = I2C_M_RD,
48 .buf = data,
49 },
50 };
51 u32 rlen, rem_len = len, max_read_len = len;
52 int status;
53
54 /* check any max_read_len limitation on i2c adapter */
55 if (quirks && quirks->max_read_len)
56 max_read_len = quirks->max_read_len;
57
58 while (rem_len > 0) {
59 msgs[1].buf = &data[len - rem_len];
60 rlen = min_t(u16, rem_len, max_read_len);
61 msgs[1].len = rlen;
62 put_unaligned_le16(rab, buf);
63 status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
64 if (status < 0) {
65 dev_err(uc->dev, "i2c_transfer failed %d\n", status);
66 return status;
67 }
68 rab += rlen;
69 rem_len -= rlen;
70 }
71
72 return 0;
73}
74
75static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
76{
77 struct i2c_client *client = uc->client;
78 unsigned char *buf;
79 struct i2c_msg msgs[] = {
80 {
81 .addr = client->addr,
82 .flags = 0x0,
83 }
84 };
85 int status;
86
87 buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
88 if (!buf)
89 return -ENOMEM;
90
91 put_unaligned_le16(rab, buf);
92 memcpy(buf + sizeof(rab), data, len);
93
94 msgs[0].len = len + sizeof(rab);
95 msgs[0].buf = buf;
96
97 status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
98 if (status < 0) {
99 dev_err(uc->dev, "i2c_transfer failed %d\n", status);
100 kfree(buf);
101 return status;
102 }
103
104 kfree(buf);
105 return 0;
106}
107
108static int ucsi_ccg_init(struct ucsi_ccg *uc)
109{
110 unsigned int count = 10;
111 u8 data;
112 int status;
113
114 data = CCGX_RAB_UCSI_CONTROL_STOP;
115 status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
116 if (status < 0)
117 return status;
118
119 data = CCGX_RAB_UCSI_CONTROL_START;
120 status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
121 if (status < 0)
122 return status;
123
124 /*
125 * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
126 * register write will push response which must be cleared.
127 */
128 do {
129 status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
130 if (status < 0)
131 return status;
132
133 if (!data)
134 return 0;
135
136 status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
137 if (status < 0)
138 return status;
139
140 usleep_range(10000, 11000);
141 } while (--count);
142
143 return -ETIMEDOUT;
144}
145
146static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
147{
148 u8 *ppm = (u8 *)uc->ppm.data;
149 int status;
150 u16 rab;
151
152 rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
153 status = ccg_write(uc, rab, ppm +
154 offsetof(struct ucsi_data, message_out),
155 sizeof(uc->ppm.data->message_out));
156 if (status < 0)
157 return status;
158
159 rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
160 return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
161 sizeof(uc->ppm.data->ctrl));
162}
163
164static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
165{
166 u8 *ppm = (u8 *)uc->ppm.data;
167 int status;
168 u16 rab;
169
170 rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
171 status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
172 sizeof(uc->ppm.data->cci));
173 if (status < 0)
174 return status;
175
176 rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
177 return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
178 sizeof(uc->ppm.data->message_in));
179}
180
181static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
182{
183 int status;
184 unsigned char data;
185
186 status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
187 if (status < 0)
188 return status;
189
190 return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
191}
192
193static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
194{
195 struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
196 int status;
197
198 status = ucsi_ccg_recv_data(uc);
199 if (status < 0)
200 return status;
201
202 /* ack interrupt to allow next command to run */
203 return ucsi_ccg_ack_interrupt(uc);
204}
205
206static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
207{
208 struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
209
210 ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
211 return ucsi_ccg_send_data(uc);
212}
213
214static irqreturn_t ccg_irq_handler(int irq, void *data)
215{
216 struct ucsi_ccg *uc = data;
217
218 ucsi_notify(uc->ucsi);
219
220 return IRQ_HANDLED;
221}
222
223static int ucsi_ccg_probe(struct i2c_client *client,
224 const struct i2c_device_id *id)
225{
226 struct device *dev = &client->dev;
227 struct ucsi_ccg *uc;
228 int status;
229 u16 rab;
230
231 uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
232 if (!uc)
233 return -ENOMEM;
234
235 uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
236 if (!uc->ppm.data)
237 return -ENOMEM;
238
239 uc->ppm.cmd = ucsi_ccg_cmd;
240 uc->ppm.sync = ucsi_ccg_sync;
241 uc->dev = dev;
242 uc->client = client;
243
244 /* reset ccg device and initialize ucsi */
245 status = ucsi_ccg_init(uc);
246 if (status < 0) {
247 dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
248 return status;
249 }
250
251 status = devm_request_threaded_irq(dev, client->irq, NULL,
252 ccg_irq_handler,
253 IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
254 dev_name(dev), uc);
255 if (status < 0) {
256 dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
257 return status;
258 }
259
260 uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
261 if (IS_ERR(uc->ucsi)) {
262 dev_err(uc->dev, "ucsi_register_ppm failed\n");
263 return PTR_ERR(uc->ucsi);
264 }
265
266 rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
267 status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
268 offsetof(struct ucsi_data, version),
269 sizeof(uc->ppm.data->version));
270 if (status < 0) {
271 ucsi_unregister_ppm(uc->ucsi);
272 return status;
273 }
274
275 i2c_set_clientdata(client, uc);
276 return 0;
277}
278
279static int ucsi_ccg_remove(struct i2c_client *client)
280{
281 struct ucsi_ccg *uc = i2c_get_clientdata(client);
282
283 ucsi_unregister_ppm(uc->ucsi);
284
285 return 0;
286}
287
288static const struct i2c_device_id ucsi_ccg_device_id[] = {
289 {"ccgx-ucsi", 0},
290 {}
291};
292MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
293
294static struct i2c_driver ucsi_ccg_driver = {
295 .driver = {
296 .name = "ucsi_ccg",
297 },
298 .probe = ucsi_ccg_probe,
299 .remove = ucsi_ccg_remove,
300 .id_table = ucsi_ccg_device_id,
301};
302
303module_i2c_driver(ucsi_ccg_driver);
304
305MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
306MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
307MODULE_LICENSE("GPL v2");