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