Greg Kroah-HartmanMerge 7.2-rc3 into usb-nextb60af0b4 months agoHistory
906 lines · 20.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Intel La Jolla Cove Adapter USB driver * * Copyright (c) 2023, Intel Corporation. */ #include <linux/acpi.h>#include <linux/auxiliary_bus.h>#include <linux/dev_printk.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/mutex.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/types.h>#include <linux/usb.h>#include <linux/usb/ljca.h> #include <linux/unaligned.h> /* command flags */#define LJCA_ACK_FLAG			BIT(0)#define LJCA_RESP_FLAG			BIT(1)#define LJCA_CMPL_FLAG			BIT(2) #define LJCA_MAX_PACKET_SIZE		64u#define LJCA_MAX_PAYLOAD_SIZE		\		(LJCA_MAX_PACKET_SIZE - sizeof(struct ljca_msg)) #define LJCA_WRITE_TIMEOUT_MS		200#define LJCA_WRITE_ACK_TIMEOUT_MS	500#define LJCA_ENUM_CLIENT_TIMEOUT_MS	20 /* ljca client type */enum ljca_client_type {	LJCA_CLIENT_MNG = 1,	LJCA_CLIENT_GPIO = 3,	LJCA_CLIENT_I2C = 4,	LJCA_CLIENT_SPI = 5,}; /* MNG client commands */enum ljca_mng_cmd {	LJCA_MNG_RESET = 2,	LJCA_MNG_ENUM_GPIO = 4,	LJCA_MNG_ENUM_I2C = 5,	LJCA_MNG_ENUM_SPI = 8,}; /* ljca client acpi _ADR */enum ljca_client_acpi_adr {	LJCA_GPIO_ACPI_ADR,	LJCA_I2C1_ACPI_ADR,	LJCA_I2C2_ACPI_ADR,	LJCA_SPI1_ACPI_ADR,	LJCA_SPI2_ACPI_ADR,	LJCA_CLIENT_ACPI_ADR_MAX,}; /* ljca cmd message structure */struct ljca_msg {	u8 type;	u8 cmd;	u8 flags;	u8 len;	u8 data[] __counted_by(len);} __packed; struct ljca_i2c_ctr_info {	u8 id;	u8 capacity;	u8 intr_pin;} __packed; struct ljca_i2c_descriptor {	u8 num;	struct ljca_i2c_ctr_info info[] __counted_by(num);} __packed; struct ljca_spi_ctr_info {	u8 id;	u8 capacity;	u8 intr_pin;} __packed; struct ljca_spi_descriptor {	u8 num;	struct ljca_spi_ctr_info info[] __counted_by(num);} __packed; struct ljca_bank_descriptor {	u8 bank_id;	u8 pin_num; 	/* 1 bit for each gpio, 1 means valid */	__le32 valid_pins;} __packed; struct ljca_gpio_descriptor {	u8 pins_per_bank;	u8 bank_num;	struct ljca_bank_descriptor bank_desc[] __counted_by(bank_num);} __packed; /** * struct ljca_adapter - represent a ljca adapter * * @intf: the usb interface for this ljca adapter * @usb_dev: the usb device for this ljca adapter * @dev: the specific device info of the usb interface * @rx_pipe: bulk in pipe for receive data from firmware * @tx_pipe: bulk out pipe for send data to firmware * @rx_urb: urb used for the bulk in pipe * @rx_buf: buffer used to receive command response and event * @rx_len: length of rx buffer * @ex_buf: external buffer to save command response * @ex_buf_len: length of external buffer * @actual_length: actual length of data copied to external buffer * @tx_buf: buffer used to download command to firmware * @tx_buf_len: length of tx buffer * @lock: spinlock to protect tx_buf and ex_buf * @cmd_completion: completion object as the command receives ack * @mutex: mutex to avoid command download concurrently * @client_list: client device list * @disconnect: usb disconnect ongoing or not * @reset_id: used to reset firmware */struct ljca_adapter {	struct usb_interface *intf;	struct usb_device *usb_dev;	struct device *dev; 	unsigned int rx_pipe;	unsigned int tx_pipe; 	struct urb *rx_urb;	void *rx_buf;	unsigned int rx_len; 	u8 *ex_buf;	u8 ex_buf_len;	u8 actual_length; 	void *tx_buf;	u8 tx_buf_len; 	spinlock_t lock; 	struct completion cmd_completion;	struct mutex mutex; 	struct list_head client_list; 	bool disconnect; 	u32 reset_id;}; struct ljca_match_ids_walk_data {	const struct acpi_device_id *ids;	const char *uid;	struct acpi_device *adev;}; /* * ACPI hardware IDs for LJCA client devices. * * [1] Some BIOS implementations use these IDs for denoting LJCA client devices *     even though the IDs have been allocated for USBIO. This isn't a problem *     as the usb-ljca driver is probed based on the USB device's vendor and *     product IDs and its client drivers are probed based on auxiliary device *     names, not these ACPI _HIDs. List of such systems: * *     Dell Precision 5490 */static const struct acpi_device_id ljca_gpio_hids[] = {	{ "INTC100B" }, /* RPL LJCA GPIO */	{ "INTC1074" }, /* CVF LJCA GPIO */	{ "INTC1096" }, /* ADL LJCA GPIO */	{ "INTC10B5" }, /* LNL LJCA GPIO */	{ "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */	{},}; static const struct acpi_device_id ljca_i2c_hids[] = {	{ "INTC100C" }, /* RPL LJCA I2C */	{ "INTC1075" }, /* CVF LJCA I2C */	{ "INTC1097" }, /* ADL LJCA I2C */	{ "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */	{},}; static const struct acpi_device_id ljca_spi_hids[] = {	{ "INTC100D" }, /* RPL LJCA SPI */	{ "INTC1091" }, /* TGL/ADL LJCA SPI */	{ "INTC1098" }, /* ADL LJCA SPI */	{ "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */	{},}; static void ljca_handle_event(struct ljca_adapter *adap,			      struct ljca_msg *header){	struct ljca_client *client; 	list_for_each_entry(client, &adap->client_list, link) {		/*		 * Currently only GPIO register event callback, but		 * firmware message structure should include id when		 * multiple same type clients register event callback.		 */		if (client->type == header->type) {			unsigned long flags; 			spin_lock_irqsave(&client->event_cb_lock, flags);			client->event_cb(client->context, header->cmd,					 header->data, header->len);			spin_unlock_irqrestore(&client->event_cb_lock, flags); 			break;		}	}} /* process command ack and received data if available */static void ljca_handle_cmd_ack(struct ljca_adapter *adap, struct ljca_msg *header){	struct ljca_msg *tx_header = adap->tx_buf;	u8 ibuf_len, actual_len = 0;	unsigned long flags;	u8 *ibuf; 	spin_lock_irqsave(&adap->lock, flags); 	if (tx_header->type != header->type || tx_header->cmd != header->cmd) {		spin_unlock_irqrestore(&adap->lock, flags);		dev_err(adap->dev, "cmd ack mismatch error\n");		return;	} 	ibuf_len = adap->ex_buf_len;	ibuf = adap->ex_buf; 	if (ibuf && ibuf_len) {		actual_len = min(header->len, ibuf_len); 		/* copy received data to external buffer */		memcpy(ibuf, header->data, actual_len);	}	/* update copied data length */	adap->actual_length = actual_len; 	spin_unlock_irqrestore(&adap->lock, flags); 	complete(&adap->cmd_completion);} static void ljca_recv(struct urb *urb){	struct ljca_msg *header = urb->transfer_buffer;	struct ljca_adapter *adap = urb->context;	int ret; 	switch (urb->status) {	case 0:		/* success */		break;	case -ENOENT:		/*		 * directly complete the possible ongoing transfer		 * during disconnect		 */		if (adap->disconnect)			complete(&adap->cmd_completion);		return;	case -ECONNRESET:	case -ESHUTDOWN:	case -EPIPE:		/* rx urb is terminated */		dev_dbg(adap->dev, "rx urb terminated with status: %d\n",			urb->status);		return;	default:		dev_dbg(adap->dev, "rx urb error: %d\n", urb->status);		goto resubmit;	} 	if (header->len + sizeof(*header) != urb->actual_length)		goto resubmit; 	if (header->flags & LJCA_ACK_FLAG)		ljca_handle_cmd_ack(adap, header);	else		ljca_handle_event(adap, header); resubmit:	ret = usb_submit_urb(urb, GFP_ATOMIC);	if (ret && ret != -EPERM)		dev_err(adap->dev, "resubmit rx urb error %d\n", ret);} static int ljca_send(struct ljca_adapter *adap, u8 type, u8 cmd,		     const u8 *obuf, u8 obuf_len, u8 *ibuf, u8 ibuf_len,		     bool ack, unsigned long timeout){	unsigned int msg_len = sizeof(struct ljca_msg) + obuf_len;	struct ljca_msg *header = adap->tx_buf;	unsigned int transferred;	unsigned long flags;	int ret; 	if (adap->disconnect)		return -ENODEV; 	if (msg_len > adap->tx_buf_len)		return -EINVAL; 	mutex_lock(&adap->mutex); 	spin_lock_irqsave(&adap->lock, flags); 	header->type = type;	header->cmd = cmd;	header->len = obuf_len;	if (obuf)		memcpy(header->data, obuf, obuf_len); 	header->flags = LJCA_CMPL_FLAG | (ack ? LJCA_ACK_FLAG : 0); 	adap->ex_buf = ibuf;	adap->ex_buf_len = ibuf_len;	adap->actual_length = 0; 	spin_unlock_irqrestore(&adap->lock, flags); 	reinit_completion(&adap->cmd_completion); 	ret = usb_autopm_get_interface(adap->intf);	if (ret < 0)		goto out; 	ret = usb_bulk_msg(adap->usb_dev, adap->tx_pipe, header,			   msg_len, &transferred, LJCA_WRITE_TIMEOUT_MS);	if (ret < 0)		goto out_put;	if (transferred != msg_len) {		ret = -EIO;		goto out_put;	} 	if (ack) {		ret = wait_for_completion_timeout(&adap->cmd_completion,						  timeout);		if (!ret) {			ret = -ETIMEDOUT;			goto out_put;		}	}	ret = adap->actual_length; out_put:	usb_autopm_put_interface(adap->intf); out:	spin_lock_irqsave(&adap->lock, flags);	adap->ex_buf = NULL;	adap->ex_buf_len = 0; 	memset(header, 0, sizeof(*header));	spin_unlock_irqrestore(&adap->lock, flags); 	mutex_unlock(&adap->mutex); 	return ret;} int ljca_transfer(struct ljca_client *client, u8 cmd, const u8 *obuf,		  u8 obuf_len, u8 *ibuf, u8 ibuf_len){	return ljca_send(client->adapter, client->type, cmd,			 obuf, obuf_len, ibuf, ibuf_len, true,			 LJCA_WRITE_ACK_TIMEOUT_MS);}EXPORT_SYMBOL_NS_GPL(ljca_transfer, "LJCA"); int ljca_transfer_noack(struct ljca_client *client, u8 cmd, const u8 *obuf,			u8 obuf_len){	return ljca_send(client->adapter, client->type, cmd, obuf,			 obuf_len, NULL, 0, false, LJCA_WRITE_ACK_TIMEOUT_MS);}EXPORT_SYMBOL_NS_GPL(ljca_transfer_noack, "LJCA"); int ljca_register_event_cb(struct ljca_client *client, ljca_event_cb_t event_cb,			   void *context){	unsigned long flags; 	if (!event_cb)		return -EINVAL; 	spin_lock_irqsave(&client->event_cb_lock, flags); 	if (client->event_cb) {		spin_unlock_irqrestore(&client->event_cb_lock, flags);		return -EALREADY;	} 	client->event_cb = event_cb;	client->context = context; 	spin_unlock_irqrestore(&client->event_cb_lock, flags); 	return 0;}EXPORT_SYMBOL_NS_GPL(ljca_register_event_cb, "LJCA"); void ljca_unregister_event_cb(struct ljca_client *client){	unsigned long flags; 	spin_lock_irqsave(&client->event_cb_lock, flags); 	client->event_cb = NULL;	client->context = NULL; 	spin_unlock_irqrestore(&client->event_cb_lock, flags);}EXPORT_SYMBOL_NS_GPL(ljca_unregister_event_cb, "LJCA"); static int ljca_match_device_ids(struct acpi_device *adev, void *data){	struct ljca_match_ids_walk_data *wd = data;	const char *uid = acpi_device_uid(adev); 	if (acpi_match_device_ids(adev, wd->ids))		return 0; 	if (!wd->uid)		goto match; 	if (!uid)		/*		 * Some DSDTs have only one ACPI companion for the two I2C		 * controllers and they don't set a UID at all (e.g. Dell		 * Latitude 9420). On these platforms only the first I2C		 * controller is used, so if a HID match has no UID we use		 * "0" as the UID and assign ACPI companion to the first		 * I2C controller.		 */		uid = "0";	else		uid = strchr(uid, wd->uid[0]); 	if (!uid || strcmp(uid, wd->uid))		return 0; match:	wd->adev = adev; 	return 1;} /* bind auxiliary device to acpi device */static void ljca_auxdev_acpi_bind(struct ljca_adapter *adap,				  struct auxiliary_device *auxdev,				  u64 adr, u8 id){	struct ljca_match_ids_walk_data wd = { 0 };	struct device *dev = adap->dev;	struct acpi_device *parent;	char uid[4]; 	parent = ACPI_COMPANION(dev);	if (!parent)		return; 	/*	 * Currently LJCA hw doesn't use _ADR instead the shipped	 * platforms use _HID to distinguish children devices.	 */	switch (adr) {	case LJCA_GPIO_ACPI_ADR:		wd.ids = ljca_gpio_hids;		break;	case LJCA_I2C1_ACPI_ADR:	case LJCA_I2C2_ACPI_ADR:		snprintf(uid, sizeof(uid), "%d", id);		wd.uid = uid;		wd.ids = ljca_i2c_hids;		break;	case LJCA_SPI1_ACPI_ADR:	case LJCA_SPI2_ACPI_ADR:		wd.ids = ljca_spi_hids;		break;	default:		dev_warn(dev, "unsupported _ADR\n");		return;	}

Lines 1–500 of 906.