162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Diolan DLN-2 USB adapter 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2014 Intel Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Derived from: 862306a36Sopenharmony_ci * i2c-diolan-u2c.c 962306a36Sopenharmony_ci * Copyright (c) 2010-2011 Ericsson AB 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/usb.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/mfd/core.h> 2062306a36Sopenharmony_ci#include <linux/mfd/dln2.h> 2162306a36Sopenharmony_ci#include <linux/rculist.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct dln2_header { 2462306a36Sopenharmony_ci __le16 size; 2562306a36Sopenharmony_ci __le16 id; 2662306a36Sopenharmony_ci __le16 echo; 2762306a36Sopenharmony_ci __le16 handle; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct dln2_response { 3162306a36Sopenharmony_ci struct dln2_header hdr; 3262306a36Sopenharmony_ci __le16 result; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DLN2_GENERIC_MODULE_ID 0x00 3662306a36Sopenharmony_ci#define DLN2_GENERIC_CMD(cmd) DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID) 3762306a36Sopenharmony_ci#define CMD_GET_DEVICE_VER DLN2_GENERIC_CMD(0x30) 3862306a36Sopenharmony_ci#define CMD_GET_DEVICE_SN DLN2_GENERIC_CMD(0x31) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define DLN2_HW_ID 0x200 4162306a36Sopenharmony_ci#define DLN2_USB_TIMEOUT 200 /* in ms */ 4262306a36Sopenharmony_ci#define DLN2_MAX_RX_SLOTS 16 4362306a36Sopenharmony_ci#define DLN2_MAX_URBS 16 4462306a36Sopenharmony_ci#define DLN2_RX_BUF_SIZE 512 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum dln2_handle { 4762306a36Sopenharmony_ci DLN2_HANDLE_EVENT = 0, /* don't change, hardware defined */ 4862306a36Sopenharmony_ci DLN2_HANDLE_CTRL, 4962306a36Sopenharmony_ci DLN2_HANDLE_GPIO, 5062306a36Sopenharmony_ci DLN2_HANDLE_I2C, 5162306a36Sopenharmony_ci DLN2_HANDLE_SPI, 5262306a36Sopenharmony_ci DLN2_HANDLE_ADC, 5362306a36Sopenharmony_ci DLN2_HANDLES 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Receive context used between the receive demultiplexer and the transfer 5862306a36Sopenharmony_ci * routine. While sending a request the transfer routine will look for a free 5962306a36Sopenharmony_ci * receive context and use it to wait for a response and to receive the URB and 6062306a36Sopenharmony_ci * thus the response data. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistruct dln2_rx_context { 6362306a36Sopenharmony_ci /* completion used to wait for a response */ 6462306a36Sopenharmony_ci struct completion done; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* if non-NULL the URB contains the response */ 6762306a36Sopenharmony_ci struct urb *urb; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* if true then this context is used to wait for a response */ 7062306a36Sopenharmony_ci bool in_use; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * Receive contexts for a particular DLN2 module (i2c, gpio, etc.). We use the 7562306a36Sopenharmony_ci * handle header field to identify the module in dln2_dev.mod_rx_slots and then 7662306a36Sopenharmony_ci * the echo header field to index the slots field and find the receive context 7762306a36Sopenharmony_ci * for a particular request. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistruct dln2_mod_rx_slots { 8062306a36Sopenharmony_ci /* RX slots bitmap */ 8162306a36Sopenharmony_ci DECLARE_BITMAP(bmap, DLN2_MAX_RX_SLOTS); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* used to wait for a free RX slot */ 8462306a36Sopenharmony_ci wait_queue_head_t wq; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* used to wait for an RX operation to complete */ 8762306a36Sopenharmony_ci struct dln2_rx_context slots[DLN2_MAX_RX_SLOTS]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* avoid races between alloc/free_rx_slot and dln2_rx_transfer */ 9062306a36Sopenharmony_ci spinlock_t lock; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct dln2_dev { 9462306a36Sopenharmony_ci struct usb_device *usb_dev; 9562306a36Sopenharmony_ci struct usb_interface *interface; 9662306a36Sopenharmony_ci u8 ep_in; 9762306a36Sopenharmony_ci u8 ep_out; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci struct urb *rx_urb[DLN2_MAX_URBS]; 10062306a36Sopenharmony_ci void *rx_buf[DLN2_MAX_URBS]; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci struct dln2_mod_rx_slots mod_rx_slots[DLN2_HANDLES]; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci struct list_head event_cb_list; 10562306a36Sopenharmony_ci spinlock_t event_cb_lock; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci bool disconnect; 10862306a36Sopenharmony_ci int active_transfers; 10962306a36Sopenharmony_ci wait_queue_head_t disconnect_wq; 11062306a36Sopenharmony_ci spinlock_t disconnect_lock; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct dln2_event_cb_entry { 11462306a36Sopenharmony_ci struct list_head list; 11562306a36Sopenharmony_ci u16 id; 11662306a36Sopenharmony_ci struct platform_device *pdev; 11762306a36Sopenharmony_ci dln2_event_cb_t callback; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciint dln2_register_event_cb(struct platform_device *pdev, u16 id, 12162306a36Sopenharmony_ci dln2_event_cb_t event_cb) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); 12462306a36Sopenharmony_ci struct dln2_event_cb_entry *i, *entry; 12562306a36Sopenharmony_ci unsigned long flags; 12662306a36Sopenharmony_ci int ret = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 12962306a36Sopenharmony_ci if (!entry) 13062306a36Sopenharmony_ci return -ENOMEM; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci entry->id = id; 13362306a36Sopenharmony_ci entry->callback = event_cb; 13462306a36Sopenharmony_ci entry->pdev = pdev; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci spin_lock_irqsave(&dln2->event_cb_lock, flags); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci list_for_each_entry(i, &dln2->event_cb_list, list) { 13962306a36Sopenharmony_ci if (i->id == id) { 14062306a36Sopenharmony_ci ret = -EBUSY; 14162306a36Sopenharmony_ci break; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (!ret) 14662306a36Sopenharmony_ci list_add_rcu(&entry->list, &dln2->event_cb_list); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci spin_unlock_irqrestore(&dln2->event_cb_lock, flags); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (ret) 15162306a36Sopenharmony_ci kfree(entry); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciEXPORT_SYMBOL(dln2_register_event_cb); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_civoid dln2_unregister_event_cb(struct platform_device *pdev, u16 id) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); 16062306a36Sopenharmony_ci struct dln2_event_cb_entry *i; 16162306a36Sopenharmony_ci unsigned long flags; 16262306a36Sopenharmony_ci bool found = false; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci spin_lock_irqsave(&dln2->event_cb_lock, flags); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci list_for_each_entry(i, &dln2->event_cb_list, list) { 16762306a36Sopenharmony_ci if (i->id == id) { 16862306a36Sopenharmony_ci list_del_rcu(&i->list); 16962306a36Sopenharmony_ci found = true; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&dln2->event_cb_lock, flags); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (found) { 17762306a36Sopenharmony_ci synchronize_rcu(); 17862306a36Sopenharmony_ci kfree(i); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL(dln2_unregister_event_cb); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * Returns true if a valid transfer slot is found. In this case the URB must not 18562306a36Sopenharmony_ci * be resubmitted immediately in dln2_rx as we need the data when dln2_transfer 18662306a36Sopenharmony_ci * is woke up. It will be resubmitted there. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistatic bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb, 18962306a36Sopenharmony_ci u16 handle, u16 rx_slot) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct device *dev = &dln2->interface->dev; 19262306a36Sopenharmony_ci struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; 19362306a36Sopenharmony_ci struct dln2_rx_context *rxc; 19462306a36Sopenharmony_ci unsigned long flags; 19562306a36Sopenharmony_ci bool valid_slot = false; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (rx_slot >= DLN2_MAX_RX_SLOTS) 19862306a36Sopenharmony_ci goto out; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci rxc = &rxs->slots[rx_slot]; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci spin_lock_irqsave(&rxs->lock, flags); 20362306a36Sopenharmony_ci if (rxc->in_use && !rxc->urb) { 20462306a36Sopenharmony_ci rxc->urb = urb; 20562306a36Sopenharmony_ci complete(&rxc->done); 20662306a36Sopenharmony_ci valid_slot = true; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci spin_unlock_irqrestore(&rxs->lock, flags); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciout: 21162306a36Sopenharmony_ci if (!valid_slot) 21262306a36Sopenharmony_ci dev_warn(dev, "bad/late response %d/%d\n", handle, rx_slot); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return valid_slot; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void dln2_run_event_callbacks(struct dln2_dev *dln2, u16 id, u16 echo, 21862306a36Sopenharmony_ci void *data, int len) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct dln2_event_cb_entry *i; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci rcu_read_lock(); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci list_for_each_entry_rcu(i, &dln2->event_cb_list, list) { 22562306a36Sopenharmony_ci if (i->id == id) { 22662306a36Sopenharmony_ci i->callback(i->pdev, echo, data, len); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci rcu_read_unlock(); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic void dln2_rx(struct urb *urb) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct dln2_dev *dln2 = urb->context; 23762306a36Sopenharmony_ci struct dln2_header *hdr = urb->transfer_buffer; 23862306a36Sopenharmony_ci struct device *dev = &dln2->interface->dev; 23962306a36Sopenharmony_ci u16 id, echo, handle, size; 24062306a36Sopenharmony_ci u8 *data; 24162306a36Sopenharmony_ci int len; 24262306a36Sopenharmony_ci int err; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci switch (urb->status) { 24562306a36Sopenharmony_ci case 0: 24662306a36Sopenharmony_ci /* success */ 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case -ECONNRESET: 24962306a36Sopenharmony_ci case -ENOENT: 25062306a36Sopenharmony_ci case -ESHUTDOWN: 25162306a36Sopenharmony_ci case -EPIPE: 25262306a36Sopenharmony_ci /* this urb is terminated, clean up */ 25362306a36Sopenharmony_ci dev_dbg(dev, "urb shutting down with status %d\n", urb->status); 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci default: 25662306a36Sopenharmony_ci dev_dbg(dev, "nonzero urb status received %d\n", urb->status); 25762306a36Sopenharmony_ci goto out; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (urb->actual_length < sizeof(struct dln2_header)) { 26162306a36Sopenharmony_ci dev_err(dev, "short response: %d\n", urb->actual_length); 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci handle = le16_to_cpu(hdr->handle); 26662306a36Sopenharmony_ci id = le16_to_cpu(hdr->id); 26762306a36Sopenharmony_ci echo = le16_to_cpu(hdr->echo); 26862306a36Sopenharmony_ci size = le16_to_cpu(hdr->size); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (size != urb->actual_length) { 27162306a36Sopenharmony_ci dev_err(dev, "size mismatch: handle %x cmd %x echo %x size %d actual %d\n", 27262306a36Sopenharmony_ci handle, id, echo, size, urb->actual_length); 27362306a36Sopenharmony_ci goto out; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (handle >= DLN2_HANDLES) { 27762306a36Sopenharmony_ci dev_warn(dev, "invalid handle %d\n", handle); 27862306a36Sopenharmony_ci goto out; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci data = urb->transfer_buffer + sizeof(struct dln2_header); 28262306a36Sopenharmony_ci len = urb->actual_length - sizeof(struct dln2_header); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (handle == DLN2_HANDLE_EVENT) { 28562306a36Sopenharmony_ci unsigned long flags; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci spin_lock_irqsave(&dln2->event_cb_lock, flags); 28862306a36Sopenharmony_ci dln2_run_event_callbacks(dln2, id, echo, data, len); 28962306a36Sopenharmony_ci spin_unlock_irqrestore(&dln2->event_cb_lock, flags); 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci /* URB will be re-submitted in _dln2_transfer (free_rx_slot) */ 29262306a36Sopenharmony_ci if (dln2_transfer_complete(dln2, urb, handle, echo)) 29362306a36Sopenharmony_ci return; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciout: 29762306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 29862306a36Sopenharmony_ci if (err < 0) 29962306a36Sopenharmony_ci dev_err(dev, "failed to resubmit RX URB: %d\n", err); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic void *dln2_prep_buf(u16 handle, u16 cmd, u16 echo, const void *obuf, 30362306a36Sopenharmony_ci int *obuf_len, gfp_t gfp) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int len; 30662306a36Sopenharmony_ci void *buf; 30762306a36Sopenharmony_ci struct dln2_header *hdr; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci len = *obuf_len + sizeof(*hdr); 31062306a36Sopenharmony_ci buf = kmalloc(len, gfp); 31162306a36Sopenharmony_ci if (!buf) 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci hdr = (struct dln2_header *)buf; 31562306a36Sopenharmony_ci hdr->id = cpu_to_le16(cmd); 31662306a36Sopenharmony_ci hdr->size = cpu_to_le16(len); 31762306a36Sopenharmony_ci hdr->echo = cpu_to_le16(echo); 31862306a36Sopenharmony_ci hdr->handle = cpu_to_le16(handle); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci memcpy(buf + sizeof(*hdr), obuf, *obuf_len); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci *obuf_len = len; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return buf; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int dln2_send_wait(struct dln2_dev *dln2, u16 handle, u16 cmd, u16 echo, 32862306a36Sopenharmony_ci const void *obuf, int obuf_len) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci int ret = 0; 33162306a36Sopenharmony_ci int len = obuf_len; 33262306a36Sopenharmony_ci void *buf; 33362306a36Sopenharmony_ci int actual; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci buf = dln2_prep_buf(handle, cmd, echo, obuf, &len, GFP_KERNEL); 33662306a36Sopenharmony_ci if (!buf) 33762306a36Sopenharmony_ci return -ENOMEM; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci ret = usb_bulk_msg(dln2->usb_dev, 34062306a36Sopenharmony_ci usb_sndbulkpipe(dln2->usb_dev, dln2->ep_out), 34162306a36Sopenharmony_ci buf, len, &actual, DLN2_USB_TIMEOUT); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci kfree(buf); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic bool find_free_slot(struct dln2_dev *dln2, u16 handle, int *slot) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct dln2_mod_rx_slots *rxs; 35162306a36Sopenharmony_ci unsigned long flags; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (dln2->disconnect) { 35462306a36Sopenharmony_ci *slot = -ENODEV; 35562306a36Sopenharmony_ci return true; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci rxs = &dln2->mod_rx_slots[handle]; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci spin_lock_irqsave(&rxs->lock, flags); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci *slot = find_first_zero_bit(rxs->bmap, DLN2_MAX_RX_SLOTS); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (*slot < DLN2_MAX_RX_SLOTS) { 36562306a36Sopenharmony_ci struct dln2_rx_context *rxc = &rxs->slots[*slot]; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci set_bit(*slot, rxs->bmap); 36862306a36Sopenharmony_ci rxc->in_use = true; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci spin_unlock_irqrestore(&rxs->lock, flags); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return *slot < DLN2_MAX_RX_SLOTS; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int alloc_rx_slot(struct dln2_dev *dln2, u16 handle) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci int ret; 37962306a36Sopenharmony_ci int slot; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * No need to timeout here, the wait is bounded by the timeout in 38362306a36Sopenharmony_ci * _dln2_transfer. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci ret = wait_event_interruptible(dln2->mod_rx_slots[handle].wq, 38662306a36Sopenharmony_ci find_free_slot(dln2, handle, &slot)); 38762306a36Sopenharmony_ci if (ret < 0) 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return slot; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct dln2_mod_rx_slots *rxs; 39662306a36Sopenharmony_ci struct urb *urb = NULL; 39762306a36Sopenharmony_ci unsigned long flags; 39862306a36Sopenharmony_ci struct dln2_rx_context *rxc; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci rxs = &dln2->mod_rx_slots[handle]; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spin_lock_irqsave(&rxs->lock, flags); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci clear_bit(slot, rxs->bmap); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci rxc = &rxs->slots[slot]; 40762306a36Sopenharmony_ci rxc->in_use = false; 40862306a36Sopenharmony_ci urb = rxc->urb; 40962306a36Sopenharmony_ci rxc->urb = NULL; 41062306a36Sopenharmony_ci reinit_completion(&rxc->done); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci spin_unlock_irqrestore(&rxs->lock, flags); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (urb) { 41562306a36Sopenharmony_ci int err; 41662306a36Sopenharmony_ci struct device *dev = &dln2->interface->dev; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_KERNEL); 41962306a36Sopenharmony_ci if (err < 0) 42062306a36Sopenharmony_ci dev_err(dev, "failed to resubmit RX URB: %d\n", err); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci wake_up_interruptible(&rxs->wq); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, 42762306a36Sopenharmony_ci const void *obuf, unsigned obuf_len, 42862306a36Sopenharmony_ci void *ibuf, unsigned *ibuf_len) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci int ret = 0; 43162306a36Sopenharmony_ci int rx_slot; 43262306a36Sopenharmony_ci struct dln2_response *rsp; 43362306a36Sopenharmony_ci struct dln2_rx_context *rxc; 43462306a36Sopenharmony_ci struct device *dev = &dln2->interface->dev; 43562306a36Sopenharmony_ci const unsigned long timeout = msecs_to_jiffies(DLN2_USB_TIMEOUT); 43662306a36Sopenharmony_ci struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; 43762306a36Sopenharmony_ci int size; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci spin_lock(&dln2->disconnect_lock); 44062306a36Sopenharmony_ci if (!dln2->disconnect) 44162306a36Sopenharmony_ci dln2->active_transfers++; 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci ret = -ENODEV; 44462306a36Sopenharmony_ci spin_unlock(&dln2->disconnect_lock); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (ret) 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci rx_slot = alloc_rx_slot(dln2, handle); 45062306a36Sopenharmony_ci if (rx_slot < 0) { 45162306a36Sopenharmony_ci ret = rx_slot; 45262306a36Sopenharmony_ci goto out_decr; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ret = dln2_send_wait(dln2, handle, cmd, rx_slot, obuf, obuf_len); 45662306a36Sopenharmony_ci if (ret < 0) { 45762306a36Sopenharmony_ci dev_err(dev, "USB write failed: %d\n", ret); 45862306a36Sopenharmony_ci goto out_free_rx_slot; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci rxc = &rxs->slots[rx_slot]; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ret = wait_for_completion_interruptible_timeout(&rxc->done, timeout); 46462306a36Sopenharmony_ci if (ret <= 0) { 46562306a36Sopenharmony_ci if (!ret) 46662306a36Sopenharmony_ci ret = -ETIMEDOUT; 46762306a36Sopenharmony_ci goto out_free_rx_slot; 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci ret = 0; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (dln2->disconnect) { 47362306a36Sopenharmony_ci ret = -ENODEV; 47462306a36Sopenharmony_ci goto out_free_rx_slot; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* if we got here we know that the response header has been checked */ 47862306a36Sopenharmony_ci rsp = rxc->urb->transfer_buffer; 47962306a36Sopenharmony_ci size = le16_to_cpu(rsp->hdr.size); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (size < sizeof(*rsp)) { 48262306a36Sopenharmony_ci ret = -EPROTO; 48362306a36Sopenharmony_ci goto out_free_rx_slot; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (le16_to_cpu(rsp->result) > 0x80) { 48762306a36Sopenharmony_ci dev_dbg(dev, "%d received response with error %d\n", 48862306a36Sopenharmony_ci handle, le16_to_cpu(rsp->result)); 48962306a36Sopenharmony_ci ret = -EREMOTEIO; 49062306a36Sopenharmony_ci goto out_free_rx_slot; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (!ibuf) 49462306a36Sopenharmony_ci goto out_free_rx_slot; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (*ibuf_len > size - sizeof(*rsp)) 49762306a36Sopenharmony_ci *ibuf_len = size - sizeof(*rsp); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci memcpy(ibuf, rsp + 1, *ibuf_len); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ciout_free_rx_slot: 50262306a36Sopenharmony_ci free_rx_slot(dln2, handle, rx_slot); 50362306a36Sopenharmony_ciout_decr: 50462306a36Sopenharmony_ci spin_lock(&dln2->disconnect_lock); 50562306a36Sopenharmony_ci dln2->active_transfers--; 50662306a36Sopenharmony_ci spin_unlock(&dln2->disconnect_lock); 50762306a36Sopenharmony_ci if (dln2->disconnect) 50862306a36Sopenharmony_ci wake_up(&dln2->disconnect_wq); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return ret; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ciint dln2_transfer(struct platform_device *pdev, u16 cmd, 51462306a36Sopenharmony_ci const void *obuf, unsigned obuf_len, 51562306a36Sopenharmony_ci void *ibuf, unsigned *ibuf_len) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct dln2_platform_data *dln2_pdata; 51862306a36Sopenharmony_ci struct dln2_dev *dln2; 51962306a36Sopenharmony_ci u16 handle; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci dln2 = dev_get_drvdata(pdev->dev.parent); 52262306a36Sopenharmony_ci dln2_pdata = dev_get_platdata(&pdev->dev); 52362306a36Sopenharmony_ci handle = dln2_pdata->handle; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return _dln2_transfer(dln2, handle, cmd, obuf, obuf_len, ibuf, 52662306a36Sopenharmony_ci ibuf_len); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ciEXPORT_SYMBOL(dln2_transfer); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic int dln2_check_hw(struct dln2_dev *dln2) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci int ret; 53362306a36Sopenharmony_ci __le32 hw_type; 53462306a36Sopenharmony_ci int len = sizeof(hw_type); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_VER, 53762306a36Sopenharmony_ci NULL, 0, &hw_type, &len); 53862306a36Sopenharmony_ci if (ret < 0) 53962306a36Sopenharmony_ci return ret; 54062306a36Sopenharmony_ci if (len < sizeof(hw_type)) 54162306a36Sopenharmony_ci return -EREMOTEIO; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (le32_to_cpu(hw_type) != DLN2_HW_ID) { 54462306a36Sopenharmony_ci dev_err(&dln2->interface->dev, "Device ID 0x%x not supported\n", 54562306a36Sopenharmony_ci le32_to_cpu(hw_type)); 54662306a36Sopenharmony_ci return -ENODEV; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int dln2_print_serialno(struct dln2_dev *dln2) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci int ret; 55562306a36Sopenharmony_ci __le32 serial_no; 55662306a36Sopenharmony_ci int len = sizeof(serial_no); 55762306a36Sopenharmony_ci struct device *dev = &dln2->interface->dev; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_SN, NULL, 0, 56062306a36Sopenharmony_ci &serial_no, &len); 56162306a36Sopenharmony_ci if (ret < 0) 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci if (len < sizeof(serial_no)) 56462306a36Sopenharmony_ci return -EREMOTEIO; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci dev_info(dev, "Diolan DLN2 serial %u\n", le32_to_cpu(serial_no)); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int dln2_hw_init(struct dln2_dev *dln2) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci int ret; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci ret = dln2_check_hw(dln2); 57662306a36Sopenharmony_ci if (ret < 0) 57762306a36Sopenharmony_ci return ret; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return dln2_print_serialno(dln2); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void dln2_free_rx_urbs(struct dln2_dev *dln2) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci int i; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci for (i = 0; i < DLN2_MAX_URBS; i++) { 58762306a36Sopenharmony_ci usb_free_urb(dln2->rx_urb[i]); 58862306a36Sopenharmony_ci kfree(dln2->rx_buf[i]); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic void dln2_stop_rx_urbs(struct dln2_dev *dln2) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci int i; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci for (i = 0; i < DLN2_MAX_URBS; i++) 59762306a36Sopenharmony_ci usb_kill_urb(dln2->rx_urb[i]); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic void dln2_free(struct dln2_dev *dln2) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci dln2_free_rx_urbs(dln2); 60362306a36Sopenharmony_ci usb_put_dev(dln2->usb_dev); 60462306a36Sopenharmony_ci kfree(dln2); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int dln2_setup_rx_urbs(struct dln2_dev *dln2, 60862306a36Sopenharmony_ci struct usb_host_interface *hostif) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci int i; 61162306a36Sopenharmony_ci const int rx_max_size = DLN2_RX_BUF_SIZE; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci for (i = 0; i < DLN2_MAX_URBS; i++) { 61462306a36Sopenharmony_ci dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); 61562306a36Sopenharmony_ci if (!dln2->rx_buf[i]) 61662306a36Sopenharmony_ci return -ENOMEM; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci dln2->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); 61962306a36Sopenharmony_ci if (!dln2->rx_urb[i]) 62062306a36Sopenharmony_ci return -ENOMEM; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev, 62362306a36Sopenharmony_ci usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in), 62462306a36Sopenharmony_ci dln2->rx_buf[i], rx_max_size, dln2_rx, dln2); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int dln2_start_rx_urbs(struct dln2_dev *dln2, gfp_t gfp) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct device *dev = &dln2->interface->dev; 63362306a36Sopenharmony_ci int ret; 63462306a36Sopenharmony_ci int i; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci for (i = 0; i < DLN2_MAX_URBS; i++) { 63762306a36Sopenharmony_ci ret = usb_submit_urb(dln2->rx_urb[i], gfp); 63862306a36Sopenharmony_ci if (ret < 0) { 63962306a36Sopenharmony_ci dev_err(dev, "failed to submit RX URB: %d\n", ret); 64062306a36Sopenharmony_ci return ret; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cienum { 64862306a36Sopenharmony_ci DLN2_ACPI_MATCH_GPIO = 0, 64962306a36Sopenharmony_ci DLN2_ACPI_MATCH_I2C = 1, 65062306a36Sopenharmony_ci DLN2_ACPI_MATCH_SPI = 2, 65162306a36Sopenharmony_ci DLN2_ACPI_MATCH_ADC = 3, 65262306a36Sopenharmony_ci}; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic struct dln2_platform_data dln2_pdata_gpio = { 65562306a36Sopenharmony_ci .handle = DLN2_HANDLE_GPIO, 65662306a36Sopenharmony_ci}; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic struct mfd_cell_acpi_match dln2_acpi_match_gpio = { 65962306a36Sopenharmony_ci .adr = DLN2_ACPI_MATCH_GPIO, 66062306a36Sopenharmony_ci}; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci/* Only one I2C port seems to be supported on current hardware */ 66362306a36Sopenharmony_cistatic struct dln2_platform_data dln2_pdata_i2c = { 66462306a36Sopenharmony_ci .handle = DLN2_HANDLE_I2C, 66562306a36Sopenharmony_ci .port = 0, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic struct mfd_cell_acpi_match dln2_acpi_match_i2c = { 66962306a36Sopenharmony_ci .adr = DLN2_ACPI_MATCH_I2C, 67062306a36Sopenharmony_ci}; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/* Only one SPI port supported */ 67362306a36Sopenharmony_cistatic struct dln2_platform_data dln2_pdata_spi = { 67462306a36Sopenharmony_ci .handle = DLN2_HANDLE_SPI, 67562306a36Sopenharmony_ci .port = 0, 67662306a36Sopenharmony_ci}; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic struct mfd_cell_acpi_match dln2_acpi_match_spi = { 67962306a36Sopenharmony_ci .adr = DLN2_ACPI_MATCH_SPI, 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/* Only one ADC port supported */ 68362306a36Sopenharmony_cistatic struct dln2_platform_data dln2_pdata_adc = { 68462306a36Sopenharmony_ci .handle = DLN2_HANDLE_ADC, 68562306a36Sopenharmony_ci .port = 0, 68662306a36Sopenharmony_ci}; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic struct mfd_cell_acpi_match dln2_acpi_match_adc = { 68962306a36Sopenharmony_ci .adr = DLN2_ACPI_MATCH_ADC, 69062306a36Sopenharmony_ci}; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic const struct mfd_cell dln2_devs[] = { 69362306a36Sopenharmony_ci { 69462306a36Sopenharmony_ci .name = "dln2-gpio", 69562306a36Sopenharmony_ci .acpi_match = &dln2_acpi_match_gpio, 69662306a36Sopenharmony_ci .platform_data = &dln2_pdata_gpio, 69762306a36Sopenharmony_ci .pdata_size = sizeof(struct dln2_platform_data), 69862306a36Sopenharmony_ci }, 69962306a36Sopenharmony_ci { 70062306a36Sopenharmony_ci .name = "dln2-i2c", 70162306a36Sopenharmony_ci .acpi_match = &dln2_acpi_match_i2c, 70262306a36Sopenharmony_ci .platform_data = &dln2_pdata_i2c, 70362306a36Sopenharmony_ci .pdata_size = sizeof(struct dln2_platform_data), 70462306a36Sopenharmony_ci }, 70562306a36Sopenharmony_ci { 70662306a36Sopenharmony_ci .name = "dln2-spi", 70762306a36Sopenharmony_ci .acpi_match = &dln2_acpi_match_spi, 70862306a36Sopenharmony_ci .platform_data = &dln2_pdata_spi, 70962306a36Sopenharmony_ci .pdata_size = sizeof(struct dln2_platform_data), 71062306a36Sopenharmony_ci }, 71162306a36Sopenharmony_ci { 71262306a36Sopenharmony_ci .name = "dln2-adc", 71362306a36Sopenharmony_ci .acpi_match = &dln2_acpi_match_adc, 71462306a36Sopenharmony_ci .platform_data = &dln2_pdata_adc, 71562306a36Sopenharmony_ci .pdata_size = sizeof(struct dln2_platform_data), 71662306a36Sopenharmony_ci }, 71762306a36Sopenharmony_ci}; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void dln2_stop(struct dln2_dev *dln2) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci int i, j; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* don't allow starting new transfers */ 72462306a36Sopenharmony_ci spin_lock(&dln2->disconnect_lock); 72562306a36Sopenharmony_ci dln2->disconnect = true; 72662306a36Sopenharmony_ci spin_unlock(&dln2->disconnect_lock); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* cancel in progress transfers */ 72962306a36Sopenharmony_ci for (i = 0; i < DLN2_HANDLES; i++) { 73062306a36Sopenharmony_ci struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[i]; 73162306a36Sopenharmony_ci unsigned long flags; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci spin_lock_irqsave(&rxs->lock, flags); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* cancel all response waiters */ 73662306a36Sopenharmony_ci for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) { 73762306a36Sopenharmony_ci struct dln2_rx_context *rxc = &rxs->slots[j]; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (rxc->in_use) 74062306a36Sopenharmony_ci complete(&rxc->done); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci spin_unlock_irqrestore(&rxs->lock, flags); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* wait for transfers to end */ 74762306a36Sopenharmony_ci wait_event(dln2->disconnect_wq, !dln2->active_transfers); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci dln2_stop_rx_urbs(dln2); 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic void dln2_disconnect(struct usb_interface *interface) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct dln2_dev *dln2 = usb_get_intfdata(interface); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci dln2_stop(dln2); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci mfd_remove_devices(&interface->dev); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci dln2_free(dln2); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int dln2_probe(struct usb_interface *interface, 76462306a36Sopenharmony_ci const struct usb_device_id *usb_id) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct usb_host_interface *hostif = interface->cur_altsetting; 76762306a36Sopenharmony_ci struct usb_endpoint_descriptor *epin; 76862306a36Sopenharmony_ci struct usb_endpoint_descriptor *epout; 76962306a36Sopenharmony_ci struct device *dev = &interface->dev; 77062306a36Sopenharmony_ci struct dln2_dev *dln2; 77162306a36Sopenharmony_ci int ret; 77262306a36Sopenharmony_ci int i, j; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (hostif->desc.bInterfaceNumber != 0) 77562306a36Sopenharmony_ci return -ENODEV; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci ret = usb_find_common_endpoints(hostif, &epin, &epout, NULL, NULL); 77862306a36Sopenharmony_ci if (ret) 77962306a36Sopenharmony_ci return ret; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); 78262306a36Sopenharmony_ci if (!dln2) 78362306a36Sopenharmony_ci return -ENOMEM; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci dln2->ep_out = epout->bEndpointAddress; 78662306a36Sopenharmony_ci dln2->ep_in = epin->bEndpointAddress; 78762306a36Sopenharmony_ci dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); 78862306a36Sopenharmony_ci dln2->interface = interface; 78962306a36Sopenharmony_ci usb_set_intfdata(interface, dln2); 79062306a36Sopenharmony_ci init_waitqueue_head(&dln2->disconnect_wq); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci for (i = 0; i < DLN2_HANDLES; i++) { 79362306a36Sopenharmony_ci init_waitqueue_head(&dln2->mod_rx_slots[i].wq); 79462306a36Sopenharmony_ci spin_lock_init(&dln2->mod_rx_slots[i].lock); 79562306a36Sopenharmony_ci for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) 79662306a36Sopenharmony_ci init_completion(&dln2->mod_rx_slots[i].slots[j].done); 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci spin_lock_init(&dln2->event_cb_lock); 80062306a36Sopenharmony_ci spin_lock_init(&dln2->disconnect_lock); 80162306a36Sopenharmony_ci INIT_LIST_HEAD(&dln2->event_cb_list); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci ret = dln2_setup_rx_urbs(dln2, hostif); 80462306a36Sopenharmony_ci if (ret) 80562306a36Sopenharmony_ci goto out_free; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci ret = dln2_start_rx_urbs(dln2, GFP_KERNEL); 80862306a36Sopenharmony_ci if (ret) 80962306a36Sopenharmony_ci goto out_stop_rx; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci ret = dln2_hw_init(dln2); 81262306a36Sopenharmony_ci if (ret < 0) { 81362306a36Sopenharmony_ci dev_err(dev, "failed to initialize hardware\n"); 81462306a36Sopenharmony_ci goto out_stop_rx; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs)); 81862306a36Sopenharmony_ci if (ret != 0) { 81962306a36Sopenharmony_ci dev_err(dev, "failed to add mfd devices to core\n"); 82062306a36Sopenharmony_ci goto out_stop_rx; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ciout_stop_rx: 82662306a36Sopenharmony_ci dln2_stop_rx_urbs(dln2); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ciout_free: 82962306a36Sopenharmony_ci dln2_free(dln2); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return ret; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int dln2_suspend(struct usb_interface *iface, pm_message_t message) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct dln2_dev *dln2 = usb_get_intfdata(iface); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci dln2_stop(dln2); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int dln2_resume(struct usb_interface *iface) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct dln2_dev *dln2 = usb_get_intfdata(iface); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci dln2->disconnect = false; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return dln2_start_rx_urbs(dln2, GFP_NOIO); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic const struct usb_device_id dln2_table[] = { 85362306a36Sopenharmony_ci { USB_DEVICE(0xa257, 0x2013) }, 85462306a36Sopenharmony_ci { } 85562306a36Sopenharmony_ci}; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, dln2_table); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic struct usb_driver dln2_driver = { 86062306a36Sopenharmony_ci .name = "dln2", 86162306a36Sopenharmony_ci .probe = dln2_probe, 86262306a36Sopenharmony_ci .disconnect = dln2_disconnect, 86362306a36Sopenharmony_ci .id_table = dln2_table, 86462306a36Sopenharmony_ci .suspend = dln2_suspend, 86562306a36Sopenharmony_ci .resume = dln2_resume, 86662306a36Sopenharmony_ci}; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cimodule_usb_driver(dln2_driver); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ciMODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); 87162306a36Sopenharmony_ciMODULE_DESCRIPTION("Core driver for the Diolan DLN2 interface adapter"); 87262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 873