xref: /openbmc/linux/drivers/usb/typec/ucsi/ucsi_ccg.c (revision 5d438e20)
1247c554aSAjay Gupta // SPDX-License-Identifier: GPL-2.0
2247c554aSAjay Gupta /*
3247c554aSAjay Gupta  * UCSI driver for Cypress CCGx Type-C controller
4247c554aSAjay Gupta  *
5247c554aSAjay Gupta  * Copyright (C) 2017-2018 NVIDIA Corporation. All rights reserved.
6247c554aSAjay Gupta  * Author: Ajay Gupta <ajayg@nvidia.com>
7247c554aSAjay Gupta  *
8247c554aSAjay Gupta  * Some code borrowed from drivers/usb/typec/ucsi/ucsi_acpi.c
9247c554aSAjay Gupta  */
10247c554aSAjay Gupta #include <linux/acpi.h>
11247c554aSAjay Gupta #include <linux/delay.h>
12247c554aSAjay Gupta #include <linux/i2c.h>
13247c554aSAjay Gupta #include <linux/module.h>
14247c554aSAjay Gupta #include <linux/pci.h>
15247c554aSAjay Gupta #include <linux/platform_device.h>
16247c554aSAjay Gupta 
17247c554aSAjay Gupta #include <asm/unaligned.h>
18247c554aSAjay Gupta #include "ucsi.h"
19247c554aSAjay Gupta 
205d438e20SAjay Gupta enum enum_fw_mode {
215d438e20SAjay Gupta 	BOOT,   /* bootloader */
225d438e20SAjay Gupta 	FW1,    /* FW partition-1 (contains secondary fw) */
235d438e20SAjay Gupta 	FW2,    /* FW partition-2 (contains primary fw) */
245d438e20SAjay Gupta 	FW_INVALID,
255d438e20SAjay Gupta };
265d438e20SAjay Gupta 
275d438e20SAjay Gupta struct ccg_dev_info {
285d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_SHIFT (0)
295d438e20SAjay Gupta #define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT)
305d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_SHIFT (2)
315d438e20SAjay Gupta #define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT)
325d438e20SAjay Gupta 	u8 mode;
335d438e20SAjay Gupta 	u8 bl_mode;
345d438e20SAjay Gupta 	__le16 silicon_id;
355d438e20SAjay Gupta 	__le16 bl_last_row;
365d438e20SAjay Gupta } __packed;
375d438e20SAjay Gupta 
385d438e20SAjay Gupta struct version_format {
395d438e20SAjay Gupta 	__le16 build;
405d438e20SAjay Gupta 	u8 patch;
415d438e20SAjay Gupta 	u8 ver;
425d438e20SAjay Gupta #define CCG_VERSION_MIN_SHIFT (0)
435d438e20SAjay Gupta #define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT)
445d438e20SAjay Gupta #define CCG_VERSION_MAJ_SHIFT (4)
455d438e20SAjay Gupta #define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT)
465d438e20SAjay Gupta } __packed;
475d438e20SAjay Gupta 
485d438e20SAjay Gupta struct version_info {
495d438e20SAjay Gupta 	struct version_format base;
505d438e20SAjay Gupta 	struct version_format app;
515d438e20SAjay Gupta };
525d438e20SAjay Gupta 
53247c554aSAjay Gupta struct ucsi_ccg {
54247c554aSAjay Gupta 	struct device *dev;
55247c554aSAjay Gupta 	struct ucsi *ucsi;
56247c554aSAjay Gupta 	struct ucsi_ppm ppm;
57247c554aSAjay Gupta 	struct i2c_client *client;
585d438e20SAjay Gupta 	struct ccg_dev_info info;
595d438e20SAjay Gupta 	/* version info for boot, primary and secondary */
605d438e20SAjay Gupta 	struct version_info version[FW2 + 1];
61247c554aSAjay Gupta };
62247c554aSAjay Gupta 
635d438e20SAjay Gupta #define CCGX_RAB_DEVICE_MODE			0x0000
645d438e20SAjay Gupta #define CCGX_RAB_INTR_REG			0x0006
655d438e20SAjay Gupta #define CCGX_RAB_READ_ALL_VER			0x0010
665d438e20SAjay Gupta #define CCGX_RAB_READ_FW2_VER			0x0020
675d438e20SAjay Gupta #define CCGX_RAB_UCSI_CONTROL			0x0039
68247c554aSAjay Gupta #define CCGX_RAB_UCSI_CONTROL_START		BIT(0)
69247c554aSAjay Gupta #define CCGX_RAB_UCSI_CONTROL_STOP		BIT(1)
70247c554aSAjay Gupta #define CCGX_RAB_UCSI_DATA_BLOCK(offset)	(0xf000 | ((offset) & 0xff))
71247c554aSAjay Gupta 
72247c554aSAjay Gupta static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
73247c554aSAjay Gupta {
74247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
75247c554aSAjay Gupta 	const struct i2c_adapter_quirks *quirks = client->adapter->quirks;
76247c554aSAjay Gupta 	unsigned char buf[2];
77247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
78247c554aSAjay Gupta 		{
79247c554aSAjay Gupta 			.addr	= client->addr,
80247c554aSAjay Gupta 			.flags  = 0x0,
81247c554aSAjay Gupta 			.len	= sizeof(buf),
82247c554aSAjay Gupta 			.buf	= buf,
83247c554aSAjay Gupta 		},
84247c554aSAjay Gupta 		{
85247c554aSAjay Gupta 			.addr	= client->addr,
86247c554aSAjay Gupta 			.flags  = I2C_M_RD,
87247c554aSAjay Gupta 			.buf	= data,
88247c554aSAjay Gupta 		},
89247c554aSAjay Gupta 	};
90247c554aSAjay Gupta 	u32 rlen, rem_len = len, max_read_len = len;
91247c554aSAjay Gupta 	int status;
92247c554aSAjay Gupta 
93247c554aSAjay Gupta 	/* check any max_read_len limitation on i2c adapter */
94247c554aSAjay Gupta 	if (quirks && quirks->max_read_len)
95247c554aSAjay Gupta 		max_read_len = quirks->max_read_len;
96247c554aSAjay Gupta 
97247c554aSAjay Gupta 	while (rem_len > 0) {
98247c554aSAjay Gupta 		msgs[1].buf = &data[len - rem_len];
99247c554aSAjay Gupta 		rlen = min_t(u16, rem_len, max_read_len);
100247c554aSAjay Gupta 		msgs[1].len = rlen;
101247c554aSAjay Gupta 		put_unaligned_le16(rab, buf);
102247c554aSAjay Gupta 		status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
103247c554aSAjay Gupta 		if (status < 0) {
104247c554aSAjay Gupta 			dev_err(uc->dev, "i2c_transfer failed %d\n", status);
105247c554aSAjay Gupta 			return status;
106247c554aSAjay Gupta 		}
107247c554aSAjay Gupta 		rab += rlen;
108247c554aSAjay Gupta 		rem_len -= rlen;
109247c554aSAjay Gupta 	}
110247c554aSAjay Gupta 
111247c554aSAjay Gupta 	return 0;
112247c554aSAjay Gupta }
113247c554aSAjay Gupta 
114247c554aSAjay Gupta static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
115247c554aSAjay Gupta {
116247c554aSAjay Gupta 	struct i2c_client *client = uc->client;
117247c554aSAjay Gupta 	unsigned char *buf;
118247c554aSAjay Gupta 	struct i2c_msg msgs[] = {
119247c554aSAjay Gupta 		{
120247c554aSAjay Gupta 			.addr	= client->addr,
121247c554aSAjay Gupta 			.flags  = 0x0,
122247c554aSAjay Gupta 		}
123247c554aSAjay Gupta 	};
124247c554aSAjay Gupta 	int status;
125247c554aSAjay Gupta 
126247c554aSAjay Gupta 	buf = kzalloc(len + sizeof(rab), GFP_KERNEL);
127247c554aSAjay Gupta 	if (!buf)
128247c554aSAjay Gupta 		return -ENOMEM;
129247c554aSAjay Gupta 
130247c554aSAjay Gupta 	put_unaligned_le16(rab, buf);
131247c554aSAjay Gupta 	memcpy(buf + sizeof(rab), data, len);
132247c554aSAjay Gupta 
133247c554aSAjay Gupta 	msgs[0].len = len + sizeof(rab);
134247c554aSAjay Gupta 	msgs[0].buf = buf;
135247c554aSAjay Gupta 
136247c554aSAjay Gupta 	status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
137247c554aSAjay Gupta 	if (status < 0) {
138247c554aSAjay Gupta 		dev_err(uc->dev, "i2c_transfer failed %d\n", status);
139247c554aSAjay Gupta 		kfree(buf);
140247c554aSAjay Gupta 		return status;
141247c554aSAjay Gupta 	}
142247c554aSAjay Gupta 
143247c554aSAjay Gupta 	kfree(buf);
144247c554aSAjay Gupta 	return 0;
145247c554aSAjay Gupta }
146247c554aSAjay Gupta 
147247c554aSAjay Gupta static int ucsi_ccg_init(struct ucsi_ccg *uc)
148247c554aSAjay Gupta {
149247c554aSAjay Gupta 	unsigned int count = 10;
150247c554aSAjay Gupta 	u8 data;
151247c554aSAjay Gupta 	int status;
152247c554aSAjay Gupta 
153247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_STOP;
154247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
155247c554aSAjay Gupta 	if (status < 0)
156247c554aSAjay Gupta 		return status;
157247c554aSAjay Gupta 
158247c554aSAjay Gupta 	data = CCGX_RAB_UCSI_CONTROL_START;
159247c554aSAjay Gupta 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
160247c554aSAjay Gupta 	if (status < 0)
161247c554aSAjay Gupta 		return status;
162247c554aSAjay Gupta 
163247c554aSAjay Gupta 	/*
164247c554aSAjay Gupta 	 * Flush CCGx RESPONSE queue by acking interrupts. Above ucsi control
165247c554aSAjay Gupta 	 * register write will push response which must be cleared.
166247c554aSAjay Gupta 	 */
167247c554aSAjay Gupta 	do {
168247c554aSAjay Gupta 		status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
169247c554aSAjay Gupta 		if (status < 0)
170247c554aSAjay Gupta 			return status;
171247c554aSAjay Gupta 
172247c554aSAjay Gupta 		if (!data)
173247c554aSAjay Gupta 			return 0;
174247c554aSAjay Gupta 
175247c554aSAjay Gupta 		status = ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
176247c554aSAjay Gupta 		if (status < 0)
177247c554aSAjay Gupta 			return status;
178247c554aSAjay Gupta 
179247c554aSAjay Gupta 		usleep_range(10000, 11000);
180247c554aSAjay Gupta 	} while (--count);
181247c554aSAjay Gupta 
182247c554aSAjay Gupta 	return -ETIMEDOUT;
183247c554aSAjay Gupta }
184247c554aSAjay Gupta 
185247c554aSAjay Gupta static int ucsi_ccg_send_data(struct ucsi_ccg *uc)
186247c554aSAjay Gupta {
187247c554aSAjay Gupta 	u8 *ppm = (u8 *)uc->ppm.data;
188247c554aSAjay Gupta 	int status;
189247c554aSAjay Gupta 	u16 rab;
190247c554aSAjay Gupta 
191247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_out));
192247c554aSAjay Gupta 	status = ccg_write(uc, rab, ppm +
193247c554aSAjay Gupta 			   offsetof(struct ucsi_data, message_out),
194247c554aSAjay Gupta 			   sizeof(uc->ppm.data->message_out));
195247c554aSAjay Gupta 	if (status < 0)
196247c554aSAjay Gupta 		return status;
197247c554aSAjay Gupta 
198247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, ctrl));
199247c554aSAjay Gupta 	return ccg_write(uc, rab, ppm + offsetof(struct ucsi_data, ctrl),
200247c554aSAjay Gupta 			 sizeof(uc->ppm.data->ctrl));
201247c554aSAjay Gupta }
202247c554aSAjay Gupta 
203247c554aSAjay Gupta static int ucsi_ccg_recv_data(struct ucsi_ccg *uc)
204247c554aSAjay Gupta {
205247c554aSAjay Gupta 	u8 *ppm = (u8 *)uc->ppm.data;
206247c554aSAjay Gupta 	int status;
207247c554aSAjay Gupta 	u16 rab;
208247c554aSAjay Gupta 
209247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, cci));
210247c554aSAjay Gupta 	status = ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, cci),
211247c554aSAjay Gupta 			  sizeof(uc->ppm.data->cci));
212247c554aSAjay Gupta 	if (status < 0)
213247c554aSAjay Gupta 		return status;
214247c554aSAjay Gupta 
215247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, message_in));
216247c554aSAjay Gupta 	return ccg_read(uc, rab, ppm + offsetof(struct ucsi_data, message_in),
217247c554aSAjay Gupta 			sizeof(uc->ppm.data->message_in));
218247c554aSAjay Gupta }
219247c554aSAjay Gupta 
220247c554aSAjay Gupta static int ucsi_ccg_ack_interrupt(struct ucsi_ccg *uc)
221247c554aSAjay Gupta {
222247c554aSAjay Gupta 	int status;
223247c554aSAjay Gupta 	unsigned char data;
224247c554aSAjay Gupta 
225247c554aSAjay Gupta 	status = ccg_read(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
226247c554aSAjay Gupta 	if (status < 0)
227247c554aSAjay Gupta 		return status;
228247c554aSAjay Gupta 
229247c554aSAjay Gupta 	return ccg_write(uc, CCGX_RAB_INTR_REG, &data, sizeof(data));
230247c554aSAjay Gupta }
231247c554aSAjay Gupta 
232247c554aSAjay Gupta static int ucsi_ccg_sync(struct ucsi_ppm *ppm)
233247c554aSAjay Gupta {
234247c554aSAjay Gupta 	struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
235247c554aSAjay Gupta 	int status;
236247c554aSAjay Gupta 
237247c554aSAjay Gupta 	status = ucsi_ccg_recv_data(uc);
238247c554aSAjay Gupta 	if (status < 0)
239247c554aSAjay Gupta 		return status;
240247c554aSAjay Gupta 
241247c554aSAjay Gupta 	/* ack interrupt to allow next command to run */
242247c554aSAjay Gupta 	return ucsi_ccg_ack_interrupt(uc);
243247c554aSAjay Gupta }
244247c554aSAjay Gupta 
245247c554aSAjay Gupta static int ucsi_ccg_cmd(struct ucsi_ppm *ppm, struct ucsi_control *ctrl)
246247c554aSAjay Gupta {
247247c554aSAjay Gupta 	struct ucsi_ccg *uc = container_of(ppm, struct ucsi_ccg, ppm);
248247c554aSAjay Gupta 
249247c554aSAjay Gupta 	ppm->data->ctrl.raw_cmd = ctrl->raw_cmd;
250247c554aSAjay Gupta 	return ucsi_ccg_send_data(uc);
251247c554aSAjay Gupta }
252247c554aSAjay Gupta 
253247c554aSAjay Gupta static irqreturn_t ccg_irq_handler(int irq, void *data)
254247c554aSAjay Gupta {
255247c554aSAjay Gupta 	struct ucsi_ccg *uc = data;
256247c554aSAjay Gupta 
257247c554aSAjay Gupta 	ucsi_notify(uc->ucsi);
258247c554aSAjay Gupta 
259247c554aSAjay Gupta 	return IRQ_HANDLED;
260247c554aSAjay Gupta }
261247c554aSAjay Gupta 
2625d438e20SAjay Gupta static int get_fw_info(struct ucsi_ccg *uc)
2635d438e20SAjay Gupta {
2645d438e20SAjay Gupta 	int err;
2655d438e20SAjay Gupta 
2665d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version),
2675d438e20SAjay Gupta 		       sizeof(uc->version));
2685d438e20SAjay Gupta 	if (err < 0)
2695d438e20SAjay Gupta 		return err;
2705d438e20SAjay Gupta 
2715d438e20SAjay Gupta 	err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info),
2725d438e20SAjay Gupta 		       sizeof(uc->info));
2735d438e20SAjay Gupta 	if (err < 0)
2745d438e20SAjay Gupta 		return err;
2755d438e20SAjay Gupta 
2765d438e20SAjay Gupta 	return 0;
2775d438e20SAjay Gupta }
2785d438e20SAjay Gupta 
279247c554aSAjay Gupta static int ucsi_ccg_probe(struct i2c_client *client,
280247c554aSAjay Gupta 			  const struct i2c_device_id *id)
281247c554aSAjay Gupta {
282247c554aSAjay Gupta 	struct device *dev = &client->dev;
283247c554aSAjay Gupta 	struct ucsi_ccg *uc;
284247c554aSAjay Gupta 	int status;
285247c554aSAjay Gupta 	u16 rab;
286247c554aSAjay Gupta 
287247c554aSAjay Gupta 	uc = devm_kzalloc(dev, sizeof(*uc), GFP_KERNEL);
288247c554aSAjay Gupta 	if (!uc)
289247c554aSAjay Gupta 		return -ENOMEM;
290247c554aSAjay Gupta 
291247c554aSAjay Gupta 	uc->ppm.data = devm_kzalloc(dev, sizeof(struct ucsi_data), GFP_KERNEL);
292247c554aSAjay Gupta 	if (!uc->ppm.data)
293247c554aSAjay Gupta 		return -ENOMEM;
294247c554aSAjay Gupta 
295247c554aSAjay Gupta 	uc->ppm.cmd = ucsi_ccg_cmd;
296247c554aSAjay Gupta 	uc->ppm.sync = ucsi_ccg_sync;
297247c554aSAjay Gupta 	uc->dev = dev;
298247c554aSAjay Gupta 	uc->client = client;
299247c554aSAjay Gupta 
300247c554aSAjay Gupta 	/* reset ccg device and initialize ucsi */
301247c554aSAjay Gupta 	status = ucsi_ccg_init(uc);
302247c554aSAjay Gupta 	if (status < 0) {
303247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_ccg_init failed - %d\n", status);
304247c554aSAjay Gupta 		return status;
305247c554aSAjay Gupta 	}
306247c554aSAjay Gupta 
3075d438e20SAjay Gupta 	status = get_fw_info(uc);
3085d438e20SAjay Gupta 	if (status < 0) {
3095d438e20SAjay Gupta 		dev_err(uc->dev, "get_fw_info failed - %d\n", status);
3105d438e20SAjay Gupta 		return status;
3115d438e20SAjay Gupta 	}
3125d438e20SAjay Gupta 
313247c554aSAjay Gupta 	status = devm_request_threaded_irq(dev, client->irq, NULL,
314247c554aSAjay Gupta 					   ccg_irq_handler,
315247c554aSAjay Gupta 					   IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
316247c554aSAjay Gupta 					   dev_name(dev), uc);
317247c554aSAjay Gupta 	if (status < 0) {
318247c554aSAjay Gupta 		dev_err(uc->dev, "request_threaded_irq failed - %d\n", status);
319247c554aSAjay Gupta 		return status;
320247c554aSAjay Gupta 	}
321247c554aSAjay Gupta 
322247c554aSAjay Gupta 	uc->ucsi = ucsi_register_ppm(dev, &uc->ppm);
323247c554aSAjay Gupta 	if (IS_ERR(uc->ucsi)) {
324247c554aSAjay Gupta 		dev_err(uc->dev, "ucsi_register_ppm failed\n");
325247c554aSAjay Gupta 		return PTR_ERR(uc->ucsi);
326247c554aSAjay Gupta 	}
327247c554aSAjay Gupta 
328247c554aSAjay Gupta 	rab = CCGX_RAB_UCSI_DATA_BLOCK(offsetof(struct ucsi_data, version));
329247c554aSAjay Gupta 	status = ccg_read(uc, rab, (u8 *)(uc->ppm.data) +
330247c554aSAjay Gupta 			  offsetof(struct ucsi_data, version),
331247c554aSAjay Gupta 			  sizeof(uc->ppm.data->version));
332247c554aSAjay Gupta 	if (status < 0) {
333247c554aSAjay Gupta 		ucsi_unregister_ppm(uc->ucsi);
334247c554aSAjay Gupta 		return status;
335247c554aSAjay Gupta 	}
336247c554aSAjay Gupta 
337247c554aSAjay Gupta 	i2c_set_clientdata(client, uc);
338247c554aSAjay Gupta 	return 0;
339247c554aSAjay Gupta }
340247c554aSAjay Gupta 
341247c554aSAjay Gupta static int ucsi_ccg_remove(struct i2c_client *client)
342247c554aSAjay Gupta {
343247c554aSAjay Gupta 	struct ucsi_ccg *uc = i2c_get_clientdata(client);
344247c554aSAjay Gupta 
345247c554aSAjay Gupta 	ucsi_unregister_ppm(uc->ucsi);
346247c554aSAjay Gupta 
347247c554aSAjay Gupta 	return 0;
348247c554aSAjay Gupta }
349247c554aSAjay Gupta 
350247c554aSAjay Gupta static const struct i2c_device_id ucsi_ccg_device_id[] = {
351247c554aSAjay Gupta 	{"ccgx-ucsi", 0},
352247c554aSAjay Gupta 	{}
353247c554aSAjay Gupta };
354247c554aSAjay Gupta MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
355247c554aSAjay Gupta 
356247c554aSAjay Gupta static struct i2c_driver ucsi_ccg_driver = {
357247c554aSAjay Gupta 	.driver = {
358247c554aSAjay Gupta 		.name = "ucsi_ccg",
359247c554aSAjay Gupta 	},
360247c554aSAjay Gupta 	.probe = ucsi_ccg_probe,
361247c554aSAjay Gupta 	.remove = ucsi_ccg_remove,
362247c554aSAjay Gupta 	.id_table = ucsi_ccg_device_id,
363247c554aSAjay Gupta };
364247c554aSAjay Gupta 
365247c554aSAjay Gupta module_i2c_driver(ucsi_ccg_driver);
366247c554aSAjay Gupta 
367247c554aSAjay Gupta MODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>");
368247c554aSAjay Gupta MODULE_DESCRIPTION("UCSI driver for Cypress CCGx Type-C controller");
369247c554aSAjay Gupta MODULE_LICENSE("GPL v2");
370