// 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. Load more
// 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; }