| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 |
- // 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/mod_devicetable.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;
- };
- static const struct acpi_device_id ljca_gpio_hids[] = {
- { "INTC1074" },
- { "INTC1096" },
- { "INTC100B" },
- { "INTC10D1" },
- { "INTC10B5" },
- {},
- };
- static const struct acpi_device_id ljca_i2c_hids[] = {
- { "INTC1075" },
- { "INTC1097" },
- { "INTC100C" },
- { "INTC10D2" },
- {},
- };
- static const struct acpi_device_id ljca_spi_hids[] = {
- { "INTC1091" },
- { "INTC1098" },
- { "INTC100D" },
- { "INTC10D3" },
- {},
- };
- 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;
- }
- acpi_dev_for_each_child(parent, ljca_match_device_ids, &wd);
- if (wd.adev) {
- ACPI_COMPANION_SET(&auxdev->dev, wd.adev);
- return;
- }
- parent = ACPI_COMPANION(dev->parent->parent);
- if (!parent)
- return;
- acpi_dev_for_each_child(parent, ljca_match_device_ids, &wd);
- if (wd.adev)
- ACPI_COMPANION_SET(&auxdev->dev, wd.adev);
- }
- static void ljca_auxdev_release(struct device *dev)
- {
- struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
- kfree(auxdev->dev.platform_data);
- }
- static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id,
- char *name, void *data, u64 adr)
- {
- struct auxiliary_device *auxdev;
- struct ljca_client *client;
- int ret;
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client) {
- kfree(data);
- return -ENOMEM;
- }
- client->type = type;
- client->id = id;
- client->adapter = adap;
- spin_lock_init(&client->event_cb_lock);
- auxdev = &client->auxdev;
- auxdev->name = name;
- auxdev->id = id;
- auxdev->dev.parent = adap->dev;
- auxdev->dev.platform_data = data;
- auxdev->dev.release = ljca_auxdev_release;
- ret = auxiliary_device_init(auxdev);
- if (ret) {
- kfree(data);
- goto err_free;
- }
- ljca_auxdev_acpi_bind(adap, auxdev, adr, id);
- ret = auxiliary_device_add(auxdev);
- if (ret)
- goto err_uninit;
- list_add_tail(&client->link, &adap->client_list);
- return 0;
- err_uninit:
- auxiliary_device_uninit(auxdev);
- err_free:
- kfree(client);
- return ret;
- }
- static int ljca_enumerate_gpio(struct ljca_adapter *adap)
- {
- u32 valid_pin[LJCA_MAX_GPIO_NUM / BITS_PER_TYPE(u32)];
- struct ljca_gpio_descriptor *desc;
- struct ljca_gpio_info *gpio_info;
- u8 buf[LJCA_MAX_PAYLOAD_SIZE];
- int ret, gpio_num;
- unsigned int i;
- ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_GPIO, NULL, 0, buf,
- sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS);
- if (ret < 0)
- return ret;
- /* check firmware response */
- desc = (struct ljca_gpio_descriptor *)buf;
- if (ret != struct_size(desc, bank_desc, desc->bank_num))
- return -EINVAL;
- gpio_num = desc->pins_per_bank * desc->bank_num;
- if (gpio_num > LJCA_MAX_GPIO_NUM)
- return -EINVAL;
- /* construct platform data */
- gpio_info = kzalloc(sizeof *gpio_info, GFP_KERNEL);
- if (!gpio_info)
- return -ENOMEM;
- gpio_info->num = gpio_num;
- for (i = 0; i < desc->bank_num; i++)
- valid_pin[i] = get_unaligned_le32(&desc->bank_desc[i].valid_pins);
- bitmap_from_arr32(gpio_info->valid_pin_map, valid_pin, gpio_num);
- return ljca_new_client_device(adap, LJCA_CLIENT_GPIO, 0, "ljca-gpio",
- gpio_info, LJCA_GPIO_ACPI_ADR);
- }
- static int ljca_enumerate_i2c(struct ljca_adapter *adap)
- {
- struct ljca_i2c_descriptor *desc;
- struct ljca_i2c_info *i2c_info;
- u8 buf[LJCA_MAX_PAYLOAD_SIZE];
- unsigned int i;
- int ret;
- ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_I2C, NULL, 0, buf,
- sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS);
- if (ret < 0)
- return ret;
- /* check firmware response */
- desc = (struct ljca_i2c_descriptor *)buf;
- if (ret != struct_size(desc, info, desc->num))
- return -EINVAL;
- for (i = 0; i < desc->num; i++) {
- /* construct platform data */
- i2c_info = kzalloc(sizeof *i2c_info, GFP_KERNEL);
- if (!i2c_info)
- return -ENOMEM;
- i2c_info->id = desc->info[i].id;
- i2c_info->capacity = desc->info[i].capacity;
- i2c_info->intr_pin = desc->info[i].intr_pin;
- ret = ljca_new_client_device(adap, LJCA_CLIENT_I2C, i,
- "ljca-i2c", i2c_info,
- LJCA_I2C1_ACPI_ADR + i);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int ljca_enumerate_spi(struct ljca_adapter *adap)
- {
- struct ljca_spi_descriptor *desc;
- struct ljca_spi_info *spi_info;
- u8 buf[LJCA_MAX_PAYLOAD_SIZE];
- unsigned int i;
- int ret;
- /* Not all LJCA chips implement SPI, a timeout reading the descriptors is normal */
- ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_ENUM_SPI, NULL, 0, buf,
- sizeof(buf), true, LJCA_ENUM_CLIENT_TIMEOUT_MS);
- if (ret < 0)
- return (ret == -ETIMEDOUT) ? 0 : ret;
- /* check firmware response */
- desc = (struct ljca_spi_descriptor *)buf;
- if (ret != struct_size(desc, info, desc->num))
- return -EINVAL;
- for (i = 0; i < desc->num; i++) {
- /* construct platform data */
- spi_info = kzalloc(sizeof *spi_info, GFP_KERNEL);
- if (!spi_info)
- return -ENOMEM;
- spi_info->id = desc->info[i].id;
- spi_info->capacity = desc->info[i].capacity;
- ret = ljca_new_client_device(adap, LJCA_CLIENT_SPI, i,
- "ljca-spi", spi_info,
- LJCA_SPI1_ACPI_ADR + i);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int ljca_reset_handshake(struct ljca_adapter *adap)
- {
- __le32 reset_id = cpu_to_le32(adap->reset_id);
- __le32 reset_id_ret = 0;
- int ret;
- adap->reset_id++;
- ret = ljca_send(adap, LJCA_CLIENT_MNG, LJCA_MNG_RESET, (u8 *)&reset_id,
- sizeof(__le32), (u8 *)&reset_id_ret, sizeof(__le32),
- true, LJCA_WRITE_ACK_TIMEOUT_MS);
- if (ret < 0)
- return ret;
- if (reset_id_ret != reset_id)
- return -EINVAL;
- return 0;
- }
- static int ljca_enumerate_clients(struct ljca_adapter *adap)
- {
- struct ljca_client *client, *next;
- int ret;
- ret = ljca_reset_handshake(adap);
- if (ret)
- goto err_kill;
- ret = ljca_enumerate_gpio(adap);
- if (ret) {
- dev_err(adap->dev, "enumerate GPIO error\n");
- goto err_kill;
- }
- ret = ljca_enumerate_i2c(adap);
- if (ret) {
- dev_err(adap->dev, "enumerate I2C error\n");
- goto err_kill;
- }
- ret = ljca_enumerate_spi(adap);
- if (ret) {
- dev_err(adap->dev, "enumerate SPI error\n");
- goto err_kill;
- }
- return 0;
- err_kill:
- adap->disconnect = true;
- usb_kill_urb(adap->rx_urb);
- list_for_each_entry_safe_reverse(client, next, &adap->client_list, link) {
- auxiliary_device_delete(&client->auxdev);
- auxiliary_device_uninit(&client->auxdev);
- list_del_init(&client->link);
- kfree(client);
- }
- return ret;
- }
- static int ljca_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
- {
- struct usb_device *usb_dev = interface_to_usbdev(interface);
- struct usb_host_interface *alt = interface->cur_altsetting;
- struct usb_endpoint_descriptor *ep_in, *ep_out;
- struct device *dev = &interface->dev;
- struct ljca_adapter *adap;
- int ret;
- adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);
- if (!adap)
- return -ENOMEM;
- /* separate tx buffer allocation for alignment */
- adap->tx_buf = devm_kzalloc(dev, LJCA_MAX_PACKET_SIZE, GFP_KERNEL);
- if (!adap->tx_buf)
- return -ENOMEM;
- adap->tx_buf_len = LJCA_MAX_PACKET_SIZE;
- mutex_init(&adap->mutex);
- spin_lock_init(&adap->lock);
- init_completion(&adap->cmd_completion);
- INIT_LIST_HEAD(&adap->client_list);
- adap->intf = usb_get_intf(interface);
- adap->usb_dev = usb_dev;
- adap->dev = dev;
- /*
- * find the first bulk in and out endpoints.
- * ignore any others.
- */
- ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL);
- if (ret) {
- dev_err(dev, "bulk endpoints not found\n");
- goto err_put;
- }
- adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in));
- adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out));
- /* setup rx buffer */
- adap->rx_len = usb_endpoint_maxp(ep_in);
- adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL);
- if (!adap->rx_buf) {
- ret = -ENOMEM;
- goto err_put;
- }
- /* alloc rx urb */
- adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!adap->rx_urb) {
- ret = -ENOMEM;
- goto err_put;
- }
- usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe,
- adap->rx_buf, adap->rx_len, ljca_recv, adap);
- usb_set_intfdata(interface, adap);
- /* submit rx urb before enumerate clients */
- ret = usb_submit_urb(adap->rx_urb, GFP_KERNEL);
- if (ret) {
- dev_err(dev, "submit rx urb failed: %d\n", ret);
- goto err_free;
- }
- ret = ljca_enumerate_clients(adap);
- if (ret)
- goto err_free;
- /*
- * This works around problems with ov2740 initialization on some
- * Lenovo platforms. The autosuspend delay, has to be smaller than
- * the delay after setting the reset_gpio line in ov2740_resume().
- * Otherwise the sensor randomly fails to initialize.
- */
- pm_runtime_set_autosuspend_delay(&usb_dev->dev, 10);
- usb_enable_autosuspend(usb_dev);
- return 0;
- err_free:
- usb_free_urb(adap->rx_urb);
- err_put:
- usb_put_intf(adap->intf);
- mutex_destroy(&adap->mutex);
- return ret;
- }
- static void ljca_disconnect(struct usb_interface *interface)
- {
- struct ljca_adapter *adap = usb_get_intfdata(interface);
- struct ljca_client *client, *next;
- adap->disconnect = true;
- usb_kill_urb(adap->rx_urb);
- list_for_each_entry_safe_reverse(client, next, &adap->client_list, link) {
- auxiliary_device_delete(&client->auxdev);
- auxiliary_device_uninit(&client->auxdev);
- list_del_init(&client->link);
- kfree(client);
- }
- usb_free_urb(adap->rx_urb);
- usb_put_intf(adap->intf);
- mutex_destroy(&adap->mutex);
- }
- static int ljca_suspend(struct usb_interface *interface, pm_message_t message)
- {
- struct ljca_adapter *adap = usb_get_intfdata(interface);
- usb_kill_urb(adap->rx_urb);
- return 0;
- }
- static int ljca_resume(struct usb_interface *interface)
- {
- struct ljca_adapter *adap = usb_get_intfdata(interface);
- return usb_submit_urb(adap->rx_urb, GFP_KERNEL);
- }
- static const struct usb_device_id ljca_table[] = {
- { USB_DEVICE(0x8086, 0x0b63) },
- { /* sentinel */ }
- };
- MODULE_DEVICE_TABLE(usb, ljca_table);
- static struct usb_driver ljca_driver = {
- .name = "ljca",
- .id_table = ljca_table,
- .probe = ljca_probe,
- .disconnect = ljca_disconnect,
- .suspend = ljca_suspend,
- .resume = ljca_resume,
- .supports_autosuspend = 1,
- };
- module_usb_driver(ljca_driver);
- MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
- MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
- MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>");
- MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver");
- MODULE_LICENSE("GPL");
|