File Index
Files and directories
Sign in
FILE

hid-mcp2200.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/hid-mcp2200.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * MCP2200 - Microchip USB to GPIO bridge
  4 *
  5 * Copyright (c) 2023, Johannes Roith <johannes@gnu-linux.rocks>
  6 *
  7 * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22228A.pdf
  8 * App Note for HID: https://ww1.microchip.com/downloads/en/DeviceDoc/93066A.pdf
  9 */
 10#include <linux/completion.h>
 11#include <linux/delay.h>
 12#include <linux/err.h>
 13#include <linux/gpio/driver.h>
 14#include <linux/hid.h>
 15#include <linux/hidraw.h>
 16#include <linux/module.h>
 17#include <linux/mutex.h>
 18#include "hid-ids.h"
 19
 20/* Commands codes in a raw output report */
 21#define SET_CLEAR_OUTPUTS	0x08
 22#define CONFIGURE		0x10
 23#define READ_EE			0x20
 24#define WRITE_EE		0x40
 25#define READ_ALL		0x80
 26
 27/* MCP GPIO direction encoding */
 28enum MCP_IO_DIR {
 29	MCP2200_DIR_OUT = 0x00,
 30	MCP2200_DIR_IN  = 0x01,
 31};
 32
 33/* Altternative pin assignments */
 34#define TXLED		2
 35#define RXLED		3
 36#define USBCFG		6
 37#define SSPND		7
 38#define MCP_NGPIO	8
 39
 40/* CMD to set or clear a GPIO output */
 41struct mcp_set_clear_outputs {
 42	u8 cmd;
 43	u8 dummys1[10];
 44	u8 set_bmap;
 45	u8 clear_bmap;
 46	u8 dummys2[3];
 47} __packed;
 48
 49/* CMD to configure the IOs */
 50struct mcp_configure {
 51	u8 cmd;
 52	u8 dummys1[3];
 53	u8 io_bmap;
 54	u8 config_alt_pins;
 55	u8 io_default_val_bmap;
 56	u8 config_alt_options;
 57	u8 baud_h;
 58	u8 baud_l;
 59	u8 dummys2[6];
 60} __packed;
 61
 62/* CMD to read all parameters */
 63struct mcp_read_all {
 64	u8 cmd;
 65	u8 dummys[15];
 66} __packed;
 67
 68/* Response to the read all cmd */
 69struct mcp_read_all_resp {
 70	u8 cmd;
 71	u8 eep_addr;
 72	u8 dummy;
 73	u8 eep_val;
 74	u8 io_bmap;
 75	u8 config_alt_pins;
 76	u8 io_default_val_bmap;
 77	u8 config_alt_options;
 78	u8 baud_h;
 79	u8 baud_l;
 80	u8 io_port_val_bmap;
 81	u8 dummys[5];
 82} __packed;
 83
 84struct mcp2200 {
 85	struct hid_device *hdev;
 86	struct mutex lock;
 87	struct completion wait_in_report;
 88	u8 gpio_dir;
 89	u8 gpio_val;
 90	u8 gpio_inval;
 91	u8 baud_h;
 92	u8 baud_l;
 93	u8 config_alt_pins;
 94	u8 gpio_reset_val;
 95	u8 config_alt_options;
 96	int status;
 97	struct gpio_chip gc;
 98	u8 hid_report[16];
 99};
100
101/* this executes the READ_ALL cmd */
102static int mcp_cmd_read_all(struct mcp2200 *mcp)
103{
104	struct mcp_read_all *read_all;
105	int len, t;
106
107	reinit_completion(&mcp->wait_in_report);
108
109	mutex_lock(&mcp->lock);
110
111	read_all = (struct mcp_read_all *) mcp->hid_report;
112	read_all->cmd = READ_ALL;
113	len = hid_hw_output_report(mcp->hdev, (u8 *) read_all,
114				   sizeof(struct mcp_read_all));
115
116	mutex_unlock(&mcp->lock);
117
118	if (len != sizeof(struct mcp_read_all))
119		return -EINVAL;
120
121	t = wait_for_completion_timeout(&mcp->wait_in_report,
122					msecs_to_jiffies(4000));
123	if (!t)
124		return -ETIMEDOUT;
125
126	/* return status, negative value if wrong response was received */
127	return mcp->status;
128}
129
130static int mcp_set_multiple(struct gpio_chip *gc, unsigned long *mask,
131			    unsigned long *bits)
132{
133	struct mcp2200 *mcp = gpiochip_get_data(gc);
134	u8 value;
135	int status;
136	struct mcp_set_clear_outputs *cmd;
137
138	mutex_lock(&mcp->lock);
139	cmd = (struct mcp_set_clear_outputs *) mcp->hid_report;
140
141	value = mcp->gpio_val & ~*mask;
142	value |= (*mask & *bits);
143
144	cmd->cmd = SET_CLEAR_OUTPUTS;
145	cmd->set_bmap = value;
146	cmd->clear_bmap = ~(value);
147
148	status = hid_hw_output_report(mcp->hdev, (u8 *) cmd,
149		       sizeof(struct mcp_set_clear_outputs));
150
151	if (status == sizeof(struct mcp_set_clear_outputs))
152		mcp->gpio_val = value;
153	else
154		status = -EIO;
155
156	mutex_unlock(&mcp->lock);
157
158	return status;
159}
160
161static int mcp_set(struct gpio_chip *gc, unsigned int gpio_nr, int value)
162{
163	unsigned long mask = 1 << gpio_nr;
164	unsigned long bmap_value = value << gpio_nr;
165
166	return mcp_set_multiple(gc, &mask, &bmap_value);
167}
168
169static int mcp_get_multiple(struct gpio_chip *gc, unsigned long *mask,
170		unsigned long *bits)
171{
172	u32 val;
173	struct mcp2200 *mcp = gpiochip_get_data(gc);
174	int status;
175
176	status = mcp_cmd_read_all(mcp);
177	if (status)
178		return status;
179
180	val = mcp->gpio_inval;
181	*bits = (val & *mask);
182	return 0;
183}
184
185static int mcp_get(struct gpio_chip *gc, unsigned int gpio_nr)
186{
187	unsigned long mask = 0, bits = 0;
188
189	mask = (1 << gpio_nr);
190	mcp_get_multiple(gc, &mask, &bits);
191	return bits > 0;
192}
193
194static int mcp_get_direction(struct gpio_chip *gc, unsigned int gpio_nr)
195{
196	struct mcp2200 *mcp = gpiochip_get_data(gc);
197
198	return (mcp->gpio_dir & (MCP2200_DIR_IN << gpio_nr))
199		? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
200}
201
202static int mcp_set_direction(struct gpio_chip *gc, unsigned int gpio_nr,
203			     enum MCP_IO_DIR io_direction)
204{
205	struct mcp2200 *mcp = gpiochip_get_data(gc);
206	struct mcp_configure *conf;
207	int status;
208	/* after the configure cmd we will need to set the outputs again */
209	unsigned long mask = ~(mcp->gpio_dir); /* only set outputs */
210	unsigned long bits = mcp->gpio_val;
211	/* Offsets of alternative pins in config_alt_pins, 0 is not used */
212	u8 alt_pin_conf[8] = {SSPND, USBCFG, 0, 0, 0, 0, RXLED, TXLED};
213	u8 config_alt_pins = mcp->config_alt_pins;
214
215	/* Read in the reset baudrate first, we need it later */
216	status = mcp_cmd_read_all(mcp);
217	if (status != 0)
218		return status;
219
220	mutex_lock(&mcp->lock);
221	conf = (struct mcp_configure  *) mcp->hid_report;
222
223	/* configure will reset the chip! */
224	conf->cmd = CONFIGURE;
225	conf->io_bmap = (mcp->gpio_dir & ~(1 << gpio_nr))
226		| (io_direction << gpio_nr);
227	/* Don't overwrite the reset parameters */
228	conf->baud_h = mcp->baud_h;
229	conf->baud_l = mcp->baud_l;
230	conf->config_alt_options = mcp->config_alt_options;
231	conf->io_default_val_bmap = mcp->gpio_reset_val;
232	/* Adjust alt. func if necessary */
233	if (alt_pin_conf[gpio_nr])
234		config_alt_pins &= ~(1 << alt_pin_conf[gpio_nr]);
235	conf->config_alt_pins = config_alt_pins;
236
237	status = hid_hw_output_report(mcp->hdev, (u8 *) conf,
238				      sizeof(struct mcp_set_clear_outputs));
239
240	if (status == sizeof(struct mcp_set_clear_outputs)) {
241		mcp->gpio_dir = conf->io_bmap;
242		mcp->config_alt_pins = config_alt_pins;
243	} else {
244		mutex_unlock(&mcp->lock);
245		return -EIO;
246	}
247
248	mutex_unlock(&mcp->lock);
249
250	/* Configure CMD will clear all IOs -> rewrite them */
251	mcp_set_multiple(gc, &mask, &bits);
252	return 0;
253}
254
255static int mcp_direction_input(struct gpio_chip *gc, unsigned int gpio_nr)
256{
257	return mcp_set_direction(gc, gpio_nr, MCP2200_DIR_IN);
258}
259
260static int mcp_direction_output(struct gpio_chip *gc, unsigned int gpio_nr,
261				int value)
262{
263	int ret;
264	unsigned long mask, bmap_value;
265
266	mask = 1 << gpio_nr;
267	bmap_value = value << gpio_nr;
268
269	ret = mcp_set_direction(gc, gpio_nr, MCP2200_DIR_OUT);
270	if (ret)
271		return ret;
272
273	return mcp_set_multiple(gc, &mask, &bmap_value);
274}
275
276static const struct gpio_chip template_chip = {
277	.label			= "mcp2200",
278	.owner			= THIS_MODULE,
279	.get_direction		= mcp_get_direction,
280	.direction_input	= mcp_direction_input,
281	.direction_output	= mcp_direction_output,
282	.set			= mcp_set,
283	.set_multiple		= mcp_set_multiple,
284	.get			= mcp_get,
285	.get_multiple		= mcp_get_multiple,
286	.base			= -1,
287	.ngpio			= MCP_NGPIO,
288	.can_sleep		= true,
289};
290
291/*
292 * MCP2200 uses interrupt endpoint for input reports. This function
293 * is called by HID layer when it receives i/p report from mcp2200,
294 * which is actually a response to the previously sent command.
295 */
296static int mcp2200_raw_event(struct hid_device *hdev, struct hid_report *report,
297		u8 *data, int size)
298{
299	struct mcp2200 *mcp = hid_get_drvdata(hdev);
300	struct mcp_read_all_resp *all_resp;
301
302	switch (data[0]) {
303	case READ_ALL:
304		all_resp = (struct mcp_read_all_resp *) data;
305		mcp->status = 0;
306		mcp->gpio_inval = all_resp->io_port_val_bmap;
307		mcp->baud_h = all_resp->baud_h;
308		mcp->baud_l = all_resp->baud_l;
309		mcp->gpio_reset_val = all_resp->io_default_val_bmap;
310		mcp->config_alt_pins = all_resp->config_alt_pins;
311		mcp->config_alt_options = all_resp->config_alt_options;
312		break;
313	default:
314		mcp->status = -EIO;
315		break;
316	}
317
318	complete(&mcp->wait_in_report);
319	return 0;
320}
321
322static int mcp2200_probe(struct hid_device *hdev, const struct hid_device_id *id)
323{
324	int ret;
325	struct mcp2200 *mcp;
326
327	mcp = devm_kzalloc(&hdev->dev, sizeof(*mcp), GFP_KERNEL);
328	if (!mcp)
329		return -ENOMEM;
330
331	ret = hid_parse(hdev);
332	if (ret) {
333		hid_err(hdev, "can't parse reports\n");
334		return ret;
335	}
336
337	ret = hid_hw_start(hdev, 0);
338	if (ret) {
339		hid_err(hdev, "can't start hardware\n");
340		return ret;
341	}
342
343	hid_info(hdev, "USB HID v%x.%02x Device [%s] on %s\n", hdev->version >> 8,
344			hdev->version & 0xff, hdev->name, hdev->phys);
345
346	ret = hid_hw_open(hdev);
347	if (ret) {
348		hid_err(hdev, "can't open device\n");
349		hid_hw_stop(hdev);
350		return ret;
351	}
352
353	mutex_init(&mcp->lock);
354	init_completion(&mcp->wait_in_report);
355	hid_set_drvdata(hdev, mcp);
356	mcp->hdev = hdev;
357
358	mcp->gc = template_chip;
359	mcp->gc.parent = &hdev->dev;
360
361	ret = devm_gpiochip_add_data(&hdev->dev, &mcp->gc, mcp);
362	if (ret < 0) {
363		hid_err(hdev, "Unable to register gpiochip\n");
364		hid_hw_close(hdev);
365		hid_hw_stop(hdev);
366		return ret;
367	}
368
369	return 0;
370}
371
372static void mcp2200_remove(struct hid_device *hdev)
373{
374	hid_hw_close(hdev);
375	hid_hw_stop(hdev);
376}
377
378static const struct hid_device_id mcp2200_devices[] = {
379	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_MCP2200) },
380	{ }
381};
382MODULE_DEVICE_TABLE(hid, mcp2200_devices);
383
384static struct hid_driver mcp2200_driver = {
385	.name		= "mcp2200",
386	.id_table	= mcp2200_devices,
387	.probe		= mcp2200_probe,
388	.remove		= mcp2200_remove,
389	.raw_event	= mcp2200_raw_event,
390};
391
392/* Register with HID core */
393module_hid_driver(mcp2200_driver);
394
395MODULE_AUTHOR("Johannes Roith <johannes@gnu-linux.rocks>");
396MODULE_DESCRIPTION("MCP2200 Microchip HID USB to GPIO bridge");
397MODULE_LICENSE("GPL");
~/ Read-only file preview.