brintos

brintos / linux-shallow public Read only

0
0
Text · 9.1 KiB · bf57f7f Raw
393 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * MCP2200 - Microchip USB to GPIO bridge4 *5 * Copyright (c) 2023, Johannes Roith <johannes@gnu-linux.rocks>6 *7 * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22228A.pdf8 * App Note for HID: https://ww1.microchip.com/downloads/en/DeviceDoc/93066A.pdf9 */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	0x0822#define CONFIGURE		0x1023#define READ_EE			0x2024#define WRITE_EE		0x4025#define READ_ALL		0x8026 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		235#define RXLED		336#define USBCFG		637#define SSPND		738#define MCP_NGPIO	839 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 void 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 154	mutex_unlock(&mcp->lock);155}156 157static void mcp_set(struct gpio_chip *gc, unsigned int gpio_nr, int value)158{159	unsigned long mask = 1 << gpio_nr;160	unsigned long bmap_value = value << gpio_nr;161 162	mcp_set_multiple(gc, &mask, &bmap_value);163}164 165static int mcp_get_multiple(struct gpio_chip *gc, unsigned long *mask,166		unsigned long *bits)167{168	u32 val;169	struct mcp2200 *mcp = gpiochip_get_data(gc);170	int status;171 172	status = mcp_cmd_read_all(mcp);173	if (status)174		return status;175 176	val = mcp->gpio_inval;177	*bits = (val & *mask);178	return 0;179}180 181static int mcp_get(struct gpio_chip *gc, unsigned int gpio_nr)182{183	unsigned long mask = 0, bits = 0;184 185	mask = (1 << gpio_nr);186	mcp_get_multiple(gc, &mask, &bits);187	return bits > 0;188}189 190static int mcp_get_direction(struct gpio_chip *gc, unsigned int gpio_nr)191{192	struct mcp2200 *mcp = gpiochip_get_data(gc);193 194	return (mcp->gpio_dir & (MCP2200_DIR_IN << gpio_nr))195		? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;196}197 198static int mcp_set_direction(struct gpio_chip *gc, unsigned int gpio_nr,199			     enum MCP_IO_DIR io_direction)200{201	struct mcp2200 *mcp = gpiochip_get_data(gc);202	struct mcp_configure *conf;203	int status;204	/* after the configure cmd we will need to set the outputs again */205	unsigned long mask = ~(mcp->gpio_dir); /* only set outputs */206	unsigned long bits = mcp->gpio_val;207	/* Offsets of alternative pins in config_alt_pins, 0 is not used */208	u8 alt_pin_conf[8] = {SSPND, USBCFG, 0, 0, 0, 0, RXLED, TXLED};209	u8 config_alt_pins = mcp->config_alt_pins;210 211	/* Read in the reset baudrate first, we need it later */212	status = mcp_cmd_read_all(mcp);213	if (status != 0)214		return status;215 216	mutex_lock(&mcp->lock);217	conf = (struct mcp_configure  *) mcp->hid_report;218 219	/* configure will reset the chip! */220	conf->cmd = CONFIGURE;221	conf->io_bmap = (mcp->gpio_dir & ~(1 << gpio_nr))222		| (io_direction << gpio_nr);223	/* Don't overwrite the reset parameters */224	conf->baud_h = mcp->baud_h;225	conf->baud_l = mcp->baud_l;226	conf->config_alt_options = mcp->config_alt_options;227	conf->io_default_val_bmap = mcp->gpio_reset_val;228	/* Adjust alt. func if necessary */229	if (alt_pin_conf[gpio_nr])230		config_alt_pins &= ~(1 << alt_pin_conf[gpio_nr]);231	conf->config_alt_pins = config_alt_pins;232 233	status = hid_hw_output_report(mcp->hdev, (u8 *) conf,234				      sizeof(struct mcp_set_clear_outputs));235 236	if (status == sizeof(struct mcp_set_clear_outputs)) {237		mcp->gpio_dir = conf->io_bmap;238		mcp->config_alt_pins = config_alt_pins;239	} else {240		mutex_unlock(&mcp->lock);241		return -EIO;242	}243 244	mutex_unlock(&mcp->lock);245 246	/* Configure CMD will clear all IOs -> rewrite them */247	mcp_set_multiple(gc, &mask, &bits);248	return 0;249}250 251static int mcp_direction_input(struct gpio_chip *gc, unsigned int gpio_nr)252{253	return mcp_set_direction(gc, gpio_nr, MCP2200_DIR_IN);254}255 256static int mcp_direction_output(struct gpio_chip *gc, unsigned int gpio_nr,257				int value)258{259	int ret;260	unsigned long mask, bmap_value;261 262	mask = 1 << gpio_nr;263	bmap_value = value << gpio_nr;264 265	ret = mcp_set_direction(gc, gpio_nr, MCP2200_DIR_OUT);266	if (!ret)267		mcp_set_multiple(gc, &mask, &bmap_value);268	return ret;269}270 271static const struct gpio_chip template_chip = {272	.label			= "mcp2200",273	.owner			= THIS_MODULE,274	.get_direction		= mcp_get_direction,275	.direction_input	= mcp_direction_input,276	.direction_output	= mcp_direction_output,277	.set			= mcp_set,278	.set_multiple		= mcp_set_multiple,279	.get			= mcp_get,280	.get_multiple		= mcp_get_multiple,281	.base			= -1,282	.ngpio			= MCP_NGPIO,283	.can_sleep		= true,284};285 286/*287 * MCP2200 uses interrupt endpoint for input reports. This function288 * is called by HID layer when it receives i/p report from mcp2200,289 * which is actually a response to the previously sent command.290 */291static int mcp2200_raw_event(struct hid_device *hdev, struct hid_report *report,292		u8 *data, int size)293{294	struct mcp2200 *mcp = hid_get_drvdata(hdev);295	struct mcp_read_all_resp *all_resp;296 297	switch (data[0]) {298	case READ_ALL:299		all_resp = (struct mcp_read_all_resp *) data;300		mcp->status = 0;301		mcp->gpio_inval = all_resp->io_port_val_bmap;302		mcp->baud_h = all_resp->baud_h;303		mcp->baud_l = all_resp->baud_l;304		mcp->gpio_reset_val = all_resp->io_default_val_bmap;305		mcp->config_alt_pins = all_resp->config_alt_pins;306		mcp->config_alt_options = all_resp->config_alt_options;307		break;308	default:309		mcp->status = -EIO;310		break;311	}312 313	complete(&mcp->wait_in_report);314	return 0;315}316 317static int mcp2200_probe(struct hid_device *hdev, const struct hid_device_id *id)318{319	int ret;320	struct mcp2200 *mcp;321 322	mcp = devm_kzalloc(&hdev->dev, sizeof(*mcp), GFP_KERNEL);323	if (!mcp)324		return -ENOMEM;325 326	ret = hid_parse(hdev);327	if (ret) {328		hid_err(hdev, "can't parse reports\n");329		return ret;330	}331 332	ret = hid_hw_start(hdev, 0);333	if (ret) {334		hid_err(hdev, "can't start hardware\n");335		return ret;336	}337 338	hid_info(hdev, "USB HID v%x.%02x Device [%s] on %s\n", hdev->version >> 8,339			hdev->version & 0xff, hdev->name, hdev->phys);340 341	ret = hid_hw_open(hdev);342	if (ret) {343		hid_err(hdev, "can't open device\n");344		hid_hw_stop(hdev);345		return ret;346	}347 348	mutex_init(&mcp->lock);349	init_completion(&mcp->wait_in_report);350	hid_set_drvdata(hdev, mcp);351	mcp->hdev = hdev;352 353	mcp->gc = template_chip;354	mcp->gc.parent = &hdev->dev;355 356	ret = devm_gpiochip_add_data(&hdev->dev, &mcp->gc, mcp);357	if (ret < 0) {358		hid_err(hdev, "Unable to register gpiochip\n");359		hid_hw_close(hdev);360		hid_hw_stop(hdev);361		return ret;362	}363 364	return 0;365}366 367static void mcp2200_remove(struct hid_device *hdev)368{369	hid_hw_close(hdev);370	hid_hw_stop(hdev);371}372 373static const struct hid_device_id mcp2200_devices[] = {374	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_MCP2200) },375	{ }376};377MODULE_DEVICE_TABLE(hid, mcp2200_devices);378 379static struct hid_driver mcp2200_driver = {380	.name		= "mcp2200",381	.id_table	= mcp2200_devices,382	.probe		= mcp2200_probe,383	.remove		= mcp2200_remove,384	.raw_event	= mcp2200_raw_event,385};386 387/* Register with HID core */388module_hid_driver(mcp2200_driver);389 390MODULE_AUTHOR("Johannes Roith <johannes@gnu-linux.rocks>");391MODULE_DESCRIPTION("MCP2200 Microchip HID USB to GPIO bridge");392MODULE_LICENSE("GPL");393