xref: /openbmc/linux/drivers/most/most_usb.c (revision fba3993e)
197a6f772SChristian Gromm // SPDX-License-Identifier: GPL-2.0
297a6f772SChristian Gromm /*
397a6f772SChristian Gromm  * usb.c - Hardware dependent module for USB
497a6f772SChristian Gromm  *
597a6f772SChristian Gromm  * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
697a6f772SChristian Gromm  */
797a6f772SChristian Gromm 
897a6f772SChristian Gromm #include <linux/module.h>
997a6f772SChristian Gromm #include <linux/fs.h>
1097a6f772SChristian Gromm #include <linux/usb.h>
1197a6f772SChristian Gromm #include <linux/slab.h>
1297a6f772SChristian Gromm #include <linux/init.h>
1397a6f772SChristian Gromm #include <linux/cdev.h>
1497a6f772SChristian Gromm #include <linux/device.h>
1597a6f772SChristian Gromm #include <linux/list.h>
1697a6f772SChristian Gromm #include <linux/completion.h>
1797a6f772SChristian Gromm #include <linux/mutex.h>
1897a6f772SChristian Gromm #include <linux/spinlock.h>
1997a6f772SChristian Gromm #include <linux/interrupt.h>
2097a6f772SChristian Gromm #include <linux/workqueue.h>
2197a6f772SChristian Gromm #include <linux/sysfs.h>
2297a6f772SChristian Gromm #include <linux/dma-mapping.h>
2397a6f772SChristian Gromm #include <linux/etherdevice.h>
2497a6f772SChristian Gromm #include <linux/uaccess.h>
2597a6f772SChristian Gromm #include <linux/most.h>
2697a6f772SChristian Gromm 
2797a6f772SChristian Gromm #define USB_MTU			512
2897a6f772SChristian Gromm #define NO_ISOCHRONOUS_URB	0
2997a6f772SChristian Gromm #define AV_PACKETS_PER_XACT	2
3097a6f772SChristian Gromm #define BUF_CHAIN_SIZE		0xFFFF
3197a6f772SChristian Gromm #define MAX_NUM_ENDPOINTS	30
3297a6f772SChristian Gromm #define MAX_SUFFIX_LEN		10
3397a6f772SChristian Gromm #define MAX_STRING_LEN		80
3497a6f772SChristian Gromm #define MAX_BUF_SIZE		0xFFFF
3597a6f772SChristian Gromm 
3697a6f772SChristian Gromm #define USB_VENDOR_ID_SMSC	0x0424  /* VID: SMSC */
3797a6f772SChristian Gromm #define USB_DEV_ID_BRDG		0xC001  /* PID: USB Bridge */
3897a6f772SChristian Gromm #define USB_DEV_ID_OS81118	0xCF18  /* PID: USB OS81118 */
3997a6f772SChristian Gromm #define USB_DEV_ID_OS81119	0xCF19  /* PID: USB OS81119 */
4097a6f772SChristian Gromm #define USB_DEV_ID_OS81210	0xCF30  /* PID: USB OS81210 */
4197a6f772SChristian Gromm /* DRCI Addresses */
4297a6f772SChristian Gromm #define DRCI_REG_NI_STATE	0x0100
4397a6f772SChristian Gromm #define DRCI_REG_PACKET_BW	0x0101
4497a6f772SChristian Gromm #define DRCI_REG_NODE_ADDR	0x0102
4597a6f772SChristian Gromm #define DRCI_REG_NODE_POS	0x0103
4697a6f772SChristian Gromm #define DRCI_REG_MEP_FILTER	0x0140
4797a6f772SChristian Gromm #define DRCI_REG_HASH_TBL0	0x0141
4897a6f772SChristian Gromm #define DRCI_REG_HASH_TBL1	0x0142
4997a6f772SChristian Gromm #define DRCI_REG_HASH_TBL2	0x0143
5097a6f772SChristian Gromm #define DRCI_REG_HASH_TBL3	0x0144
5197a6f772SChristian Gromm #define DRCI_REG_HW_ADDR_HI	0x0145
5297a6f772SChristian Gromm #define DRCI_REG_HW_ADDR_MI	0x0146
5397a6f772SChristian Gromm #define DRCI_REG_HW_ADDR_LO	0x0147
5497a6f772SChristian Gromm #define DRCI_REG_BASE		0x1100
5597a6f772SChristian Gromm #define DRCI_COMMAND		0x02
5697a6f772SChristian Gromm #define DRCI_READ_REQ		0xA0
5797a6f772SChristian Gromm #define DRCI_WRITE_REQ		0xA1
5897a6f772SChristian Gromm 
5997a6f772SChristian Gromm /**
6097a6f772SChristian Gromm  * struct most_dci_obj - Direct Communication Interface
6197a6f772SChristian Gromm  * @kobj:position in sysfs
6297a6f772SChristian Gromm  * @usb_device: pointer to the usb device
6397a6f772SChristian Gromm  * @reg_addr: register address for arbitrary DCI access
6497a6f772SChristian Gromm  */
6597a6f772SChristian Gromm struct most_dci_obj {
6697a6f772SChristian Gromm 	struct device dev;
6797a6f772SChristian Gromm 	struct usb_device *usb_device;
6897a6f772SChristian Gromm 	u16 reg_addr;
6997a6f772SChristian Gromm };
7097a6f772SChristian Gromm 
7197a6f772SChristian Gromm #define to_dci_obj(p) container_of(p, struct most_dci_obj, dev)
7297a6f772SChristian Gromm 
7397a6f772SChristian Gromm struct most_dev;
7497a6f772SChristian Gromm 
7597a6f772SChristian Gromm struct clear_hold_work {
7697a6f772SChristian Gromm 	struct work_struct ws;
7797a6f772SChristian Gromm 	struct most_dev *mdev;
7897a6f772SChristian Gromm 	unsigned int channel;
7997a6f772SChristian Gromm 	int pipe;
8097a6f772SChristian Gromm };
8197a6f772SChristian Gromm 
8297a6f772SChristian Gromm #define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
8397a6f772SChristian Gromm 
8497a6f772SChristian Gromm /**
8597a6f772SChristian Gromm  * struct most_dev - holds all usb interface specific stuff
8697a6f772SChristian Gromm  * @usb_device: pointer to usb device
8797a6f772SChristian Gromm  * @iface: hardware interface
8897a6f772SChristian Gromm  * @cap: channel capabilities
8997a6f772SChristian Gromm  * @conf: channel configuration
9097a6f772SChristian Gromm  * @dci: direct communication interface of hardware
9197a6f772SChristian Gromm  * @ep_address: endpoint address table
9297a6f772SChristian Gromm  * @description: device description
9397a6f772SChristian Gromm  * @suffix: suffix for channel name
9497a6f772SChristian Gromm  * @channel_lock: synchronize channel access
9597a6f772SChristian Gromm  * @padding_active: indicates channel uses padding
9697a6f772SChristian Gromm  * @is_channel_healthy: health status table of each channel
9797a6f772SChristian Gromm  * @busy_urbs: list of anchored items
9897a6f772SChristian Gromm  * @io_mutex: synchronize I/O with disconnect
9997a6f772SChristian Gromm  * @link_stat_timer: timer for link status reports
10097a6f772SChristian Gromm  * @poll_work_obj: work for polling link status
10197a6f772SChristian Gromm  */
10297a6f772SChristian Gromm struct most_dev {
10397a6f772SChristian Gromm 	struct device dev;
10497a6f772SChristian Gromm 	struct usb_device *usb_device;
10597a6f772SChristian Gromm 	struct most_interface iface;
10697a6f772SChristian Gromm 	struct most_channel_capability *cap;
10797a6f772SChristian Gromm 	struct most_channel_config *conf;
10897a6f772SChristian Gromm 	struct most_dci_obj *dci;
10997a6f772SChristian Gromm 	u8 *ep_address;
11097a6f772SChristian Gromm 	char description[MAX_STRING_LEN];
11197a6f772SChristian Gromm 	char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
11297a6f772SChristian Gromm 	spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
11397a6f772SChristian Gromm 	bool padding_active[MAX_NUM_ENDPOINTS];
11497a6f772SChristian Gromm 	bool is_channel_healthy[MAX_NUM_ENDPOINTS];
11597a6f772SChristian Gromm 	struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
11697a6f772SChristian Gromm 	struct usb_anchor *busy_urbs;
11797a6f772SChristian Gromm 	struct mutex io_mutex;
11897a6f772SChristian Gromm 	struct timer_list link_stat_timer;
11997a6f772SChristian Gromm 	struct work_struct poll_work_obj;
12097a6f772SChristian Gromm 	void (*on_netinfo)(struct most_interface *most_iface,
12197a6f772SChristian Gromm 			   unsigned char link_state, unsigned char *addrs);
12297a6f772SChristian Gromm };
12397a6f772SChristian Gromm 
12497a6f772SChristian Gromm #define to_mdev(d) container_of(d, struct most_dev, iface)
12597a6f772SChristian Gromm #define to_mdev_from_dev(d) container_of(d, struct most_dev, dev)
12697a6f772SChristian Gromm #define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj)
12797a6f772SChristian Gromm 
12897a6f772SChristian Gromm static void wq_clear_halt(struct work_struct *wq_obj);
12997a6f772SChristian Gromm static void wq_netinfo(struct work_struct *wq_obj);
13097a6f772SChristian Gromm 
13197a6f772SChristian Gromm /**
13297a6f772SChristian Gromm  * drci_rd_reg - read a DCI register
13397a6f772SChristian Gromm  * @dev: usb device
13497a6f772SChristian Gromm  * @reg: register address
13597a6f772SChristian Gromm  * @buf: buffer to store data
13697a6f772SChristian Gromm  *
13797a6f772SChristian Gromm  * This is reads data from INIC's direct register communication interface
13897a6f772SChristian Gromm  */
drci_rd_reg(struct usb_device * dev,u16 reg,u16 * buf)13997a6f772SChristian Gromm static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf)
14097a6f772SChristian Gromm {
14197a6f772SChristian Gromm 	int retval;
14297a6f772SChristian Gromm 	__le16 *dma_buf;
14397a6f772SChristian Gromm 	u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
14497a6f772SChristian Gromm 
14597a6f772SChristian Gromm 	dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
14697a6f772SChristian Gromm 	if (!dma_buf)
14797a6f772SChristian Gromm 		return -ENOMEM;
14897a6f772SChristian Gromm 
14997a6f772SChristian Gromm 	retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
15097a6f772SChristian Gromm 				 DRCI_READ_REQ, req_type,
15197a6f772SChristian Gromm 				 0x0000,
15263b3e810SJohan Hovold 				 reg, dma_buf, sizeof(*dma_buf),
15363b3e810SJohan Hovold 				 USB_CTRL_GET_TIMEOUT);
15497a6f772SChristian Gromm 	*buf = le16_to_cpu(*dma_buf);
15597a6f772SChristian Gromm 	kfree(dma_buf);
15697a6f772SChristian Gromm 
15797a6f772SChristian Gromm 	if (retval < 0)
15897a6f772SChristian Gromm 		return retval;
15997a6f772SChristian Gromm 	return 0;
16097a6f772SChristian Gromm }
16197a6f772SChristian Gromm 
16297a6f772SChristian Gromm /**
16397a6f772SChristian Gromm  * drci_wr_reg - write a DCI register
16497a6f772SChristian Gromm  * @dev: usb device
16597a6f772SChristian Gromm  * @reg: register address
16697a6f772SChristian Gromm  * @data: data to write
16797a6f772SChristian Gromm  *
16897a6f772SChristian Gromm  * This is writes data to INIC's direct register communication interface
16997a6f772SChristian Gromm  */
drci_wr_reg(struct usb_device * dev,u16 reg,u16 data)17097a6f772SChristian Gromm static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
17197a6f772SChristian Gromm {
17297a6f772SChristian Gromm 	return usb_control_msg(dev,
17397a6f772SChristian Gromm 			       usb_sndctrlpipe(dev, 0),
17497a6f772SChristian Gromm 			       DRCI_WRITE_REQ,
17597a6f772SChristian Gromm 			       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
17697a6f772SChristian Gromm 			       data,
17797a6f772SChristian Gromm 			       reg,
17897a6f772SChristian Gromm 			       NULL,
17997a6f772SChristian Gromm 			       0,
18063b3e810SJohan Hovold 			       USB_CTRL_SET_TIMEOUT);
18197a6f772SChristian Gromm }
18297a6f772SChristian Gromm 
start_sync_ep(struct usb_device * usb_dev,u16 ep)18397a6f772SChristian Gromm static inline int start_sync_ep(struct usb_device *usb_dev, u16 ep)
18497a6f772SChristian Gromm {
18597a6f772SChristian Gromm 	return drci_wr_reg(usb_dev, DRCI_REG_BASE + DRCI_COMMAND + ep * 16, 1);
18697a6f772SChristian Gromm }
18797a6f772SChristian Gromm 
18897a6f772SChristian Gromm /**
18997a6f772SChristian Gromm  * get_stream_frame_size - calculate frame size of current configuration
19097a6f772SChristian Gromm  * @dev: device structure
19197a6f772SChristian Gromm  * @cfg: channel configuration
19297a6f772SChristian Gromm  */
get_stream_frame_size(struct device * dev,struct most_channel_config * cfg)19397a6f772SChristian Gromm static unsigned int get_stream_frame_size(struct device *dev,
19497a6f772SChristian Gromm 					  struct most_channel_config *cfg)
19597a6f772SChristian Gromm {
19697a6f772SChristian Gromm 	unsigned int frame_size;
19797a6f772SChristian Gromm 	unsigned int sub_size = cfg->subbuffer_size;
19897a6f772SChristian Gromm 
19997a6f772SChristian Gromm 	if (!sub_size) {
20097a6f772SChristian Gromm 		dev_warn(dev, "Misconfig: Subbuffer size zero.\n");
20197a6f772SChristian Gromm 		return 0;
20297a6f772SChristian Gromm 	}
20397a6f772SChristian Gromm 	switch (cfg->data_type) {
20497a6f772SChristian Gromm 	case MOST_CH_ISOC:
20597a6f772SChristian Gromm 		frame_size = AV_PACKETS_PER_XACT * sub_size;
20697a6f772SChristian Gromm 		break;
20797a6f772SChristian Gromm 	case MOST_CH_SYNC:
20897a6f772SChristian Gromm 		if (cfg->packets_per_xact == 0) {
20997a6f772SChristian Gromm 			dev_warn(dev, "Misconfig: Packets per XACT zero\n");
21097a6f772SChristian Gromm 			frame_size = 0;
21197a6f772SChristian Gromm 		} else if (cfg->packets_per_xact == 0xFF) {
21297a6f772SChristian Gromm 			frame_size = (USB_MTU / sub_size) * sub_size;
21397a6f772SChristian Gromm 		} else {
21497a6f772SChristian Gromm 			frame_size = cfg->packets_per_xact * sub_size;
21597a6f772SChristian Gromm 		}
21697a6f772SChristian Gromm 		break;
21797a6f772SChristian Gromm 	default:
21897a6f772SChristian Gromm 		dev_warn(dev, "Query frame size of non-streaming channel\n");
21997a6f772SChristian Gromm 		frame_size = 0;
22097a6f772SChristian Gromm 		break;
22197a6f772SChristian Gromm 	}
22297a6f772SChristian Gromm 	return frame_size;
22397a6f772SChristian Gromm }
22497a6f772SChristian Gromm 
22597a6f772SChristian Gromm /**
22697a6f772SChristian Gromm  * hdm_poison_channel - mark buffers of this channel as invalid
22797a6f772SChristian Gromm  * @iface: pointer to the interface
22897a6f772SChristian Gromm  * @channel: channel ID
22997a6f772SChristian Gromm  *
23097a6f772SChristian Gromm  * This unlinks all URBs submitted to the HCD,
23197a6f772SChristian Gromm  * calls the associated completion function of the core and removes
23297a6f772SChristian Gromm  * them from the list.
23397a6f772SChristian Gromm  *
23497a6f772SChristian Gromm  * Returns 0 on success or error code otherwise.
23597a6f772SChristian Gromm  */
hdm_poison_channel(struct most_interface * iface,int channel)23697a6f772SChristian Gromm static int hdm_poison_channel(struct most_interface *iface, int channel)
23797a6f772SChristian Gromm {
23897a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(iface);
23997a6f772SChristian Gromm 	unsigned long flags;
24097a6f772SChristian Gromm 	spinlock_t *lock; /* temp. lock */
24197a6f772SChristian Gromm 
24297a6f772SChristian Gromm 	if (channel < 0 || channel >= iface->num_channels) {
24397a6f772SChristian Gromm 		dev_warn(&mdev->usb_device->dev, "Channel ID out of range.\n");
24497a6f772SChristian Gromm 		return -ECHRNG;
24597a6f772SChristian Gromm 	}
24697a6f772SChristian Gromm 
24797a6f772SChristian Gromm 	lock = mdev->channel_lock + channel;
24897a6f772SChristian Gromm 	spin_lock_irqsave(lock, flags);
24997a6f772SChristian Gromm 	mdev->is_channel_healthy[channel] = false;
25097a6f772SChristian Gromm 	spin_unlock_irqrestore(lock, flags);
25197a6f772SChristian Gromm 
25297a6f772SChristian Gromm 	cancel_work_sync(&mdev->clear_work[channel].ws);
25397a6f772SChristian Gromm 
25497a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
25597a6f772SChristian Gromm 	usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
25697a6f772SChristian Gromm 	if (mdev->padding_active[channel])
25797a6f772SChristian Gromm 		mdev->padding_active[channel] = false;
25897a6f772SChristian Gromm 
25997a6f772SChristian Gromm 	if (mdev->conf[channel].data_type == MOST_CH_ASYNC) {
26097a6f772SChristian Gromm 		del_timer_sync(&mdev->link_stat_timer);
26197a6f772SChristian Gromm 		cancel_work_sync(&mdev->poll_work_obj);
26297a6f772SChristian Gromm 	}
26397a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
26497a6f772SChristian Gromm 	return 0;
26597a6f772SChristian Gromm }
26697a6f772SChristian Gromm 
26797a6f772SChristian Gromm /**
26897a6f772SChristian Gromm  * hdm_add_padding - add padding bytes
26997a6f772SChristian Gromm  * @mdev: most device
27097a6f772SChristian Gromm  * @channel: channel ID
27197a6f772SChristian Gromm  * @mbo: buffer object
27297a6f772SChristian Gromm  *
27397a6f772SChristian Gromm  * This inserts the INIC hardware specific padding bytes into a streaming
27497a6f772SChristian Gromm  * channel's buffer
27597a6f772SChristian Gromm  */
hdm_add_padding(struct most_dev * mdev,int channel,struct mbo * mbo)27697a6f772SChristian Gromm static int hdm_add_padding(struct most_dev *mdev, int channel, struct mbo *mbo)
27797a6f772SChristian Gromm {
27897a6f772SChristian Gromm 	struct most_channel_config *conf = &mdev->conf[channel];
27997a6f772SChristian Gromm 	unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
28097a6f772SChristian Gromm 	unsigned int j, num_frames;
28197a6f772SChristian Gromm 
28297a6f772SChristian Gromm 	if (!frame_size)
28397a6f772SChristian Gromm 		return -EINVAL;
28497a6f772SChristian Gromm 	num_frames = mbo->buffer_length / frame_size;
28597a6f772SChristian Gromm 
28697a6f772SChristian Gromm 	if (num_frames < 1) {
28797a6f772SChristian Gromm 		dev_err(&mdev->usb_device->dev,
28897a6f772SChristian Gromm 			"Missed minimal transfer unit.\n");
28997a6f772SChristian Gromm 		return -EINVAL;
29097a6f772SChristian Gromm 	}
29197a6f772SChristian Gromm 
29297a6f772SChristian Gromm 	for (j = num_frames - 1; j > 0; j--)
29397a6f772SChristian Gromm 		memmove(mbo->virt_address + j * USB_MTU,
29497a6f772SChristian Gromm 			mbo->virt_address + j * frame_size,
29597a6f772SChristian Gromm 			frame_size);
29697a6f772SChristian Gromm 	mbo->buffer_length = num_frames * USB_MTU;
29797a6f772SChristian Gromm 	return 0;
29897a6f772SChristian Gromm }
29997a6f772SChristian Gromm 
30097a6f772SChristian Gromm /**
30197a6f772SChristian Gromm  * hdm_remove_padding - remove padding bytes
30297a6f772SChristian Gromm  * @mdev: most device
30397a6f772SChristian Gromm  * @channel: channel ID
30497a6f772SChristian Gromm  * @mbo: buffer object
30597a6f772SChristian Gromm  *
30697a6f772SChristian Gromm  * This takes the INIC hardware specific padding bytes off a streaming
30797a6f772SChristian Gromm  * channel's buffer.
30897a6f772SChristian Gromm  */
hdm_remove_padding(struct most_dev * mdev,int channel,struct mbo * mbo)30997a6f772SChristian Gromm static int hdm_remove_padding(struct most_dev *mdev, int channel,
31097a6f772SChristian Gromm 			      struct mbo *mbo)
31197a6f772SChristian Gromm {
31297a6f772SChristian Gromm 	struct most_channel_config *const conf = &mdev->conf[channel];
31397a6f772SChristian Gromm 	unsigned int frame_size = get_stream_frame_size(&mdev->dev, conf);
31497a6f772SChristian Gromm 	unsigned int j, num_frames;
31597a6f772SChristian Gromm 
31697a6f772SChristian Gromm 	if (!frame_size)
31797a6f772SChristian Gromm 		return -EINVAL;
31897a6f772SChristian Gromm 	num_frames = mbo->processed_length / USB_MTU;
31997a6f772SChristian Gromm 
32097a6f772SChristian Gromm 	for (j = 1; j < num_frames; j++)
32197a6f772SChristian Gromm 		memmove(mbo->virt_address + frame_size * j,
32297a6f772SChristian Gromm 			mbo->virt_address + USB_MTU * j,
32397a6f772SChristian Gromm 			frame_size);
32497a6f772SChristian Gromm 
32597a6f772SChristian Gromm 	mbo->processed_length = frame_size * num_frames;
32697a6f772SChristian Gromm 	return 0;
32797a6f772SChristian Gromm }
32897a6f772SChristian Gromm 
32997a6f772SChristian Gromm /**
33097a6f772SChristian Gromm  * hdm_write_completion - completion function for submitted Tx URBs
33197a6f772SChristian Gromm  * @urb: the URB that has been completed
33297a6f772SChristian Gromm  *
33397a6f772SChristian Gromm  * This checks the status of the completed URB. In case the URB has been
33497a6f772SChristian Gromm  * unlinked before, it is immediately freed. On any other error the MBO
33597a6f772SChristian Gromm  * transfer flag is set. On success it frees allocated resources and calls
33697a6f772SChristian Gromm  * the completion function.
33797a6f772SChristian Gromm  *
33897a6f772SChristian Gromm  * Context: interrupt!
33997a6f772SChristian Gromm  */
hdm_write_completion(struct urb * urb)34097a6f772SChristian Gromm static void hdm_write_completion(struct urb *urb)
34197a6f772SChristian Gromm {
34297a6f772SChristian Gromm 	struct mbo *mbo = urb->context;
34397a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(mbo->ifp);
34497a6f772SChristian Gromm 	unsigned int channel = mbo->hdm_channel_id;
34597a6f772SChristian Gromm 	spinlock_t *lock = mdev->channel_lock + channel;
34697a6f772SChristian Gromm 	unsigned long flags;
34797a6f772SChristian Gromm 
34897a6f772SChristian Gromm 	spin_lock_irqsave(lock, flags);
34997a6f772SChristian Gromm 
35097a6f772SChristian Gromm 	mbo->processed_length = 0;
35197a6f772SChristian Gromm 	mbo->status = MBO_E_INVAL;
35297a6f772SChristian Gromm 	if (likely(mdev->is_channel_healthy[channel])) {
35397a6f772SChristian Gromm 		switch (urb->status) {
35497a6f772SChristian Gromm 		case 0:
35597a6f772SChristian Gromm 		case -ESHUTDOWN:
35697a6f772SChristian Gromm 			mbo->processed_length = urb->actual_length;
35797a6f772SChristian Gromm 			mbo->status = MBO_SUCCESS;
35897a6f772SChristian Gromm 			break;
35997a6f772SChristian Gromm 		case -EPIPE:
36097a6f772SChristian Gromm 			dev_warn(&mdev->usb_device->dev,
36197a6f772SChristian Gromm 				 "Broken pipe on ep%02x\n",
36297a6f772SChristian Gromm 				 mdev->ep_address[channel]);
36397a6f772SChristian Gromm 			mdev->is_channel_healthy[channel] = false;
36497a6f772SChristian Gromm 			mdev->clear_work[channel].pipe = urb->pipe;
36597a6f772SChristian Gromm 			schedule_work(&mdev->clear_work[channel].ws);
36697a6f772SChristian Gromm 			break;
36797a6f772SChristian Gromm 		case -ENODEV:
36897a6f772SChristian Gromm 		case -EPROTO:
36997a6f772SChristian Gromm 			mbo->status = MBO_E_CLOSE;
37097a6f772SChristian Gromm 			break;
37197a6f772SChristian Gromm 		}
37297a6f772SChristian Gromm 	}
37397a6f772SChristian Gromm 
37497a6f772SChristian Gromm 	spin_unlock_irqrestore(lock, flags);
37597a6f772SChristian Gromm 
37697a6f772SChristian Gromm 	if (likely(mbo->complete))
37797a6f772SChristian Gromm 		mbo->complete(mbo);
37897a6f772SChristian Gromm 	usb_free_urb(urb);
37997a6f772SChristian Gromm }
38097a6f772SChristian Gromm 
38197a6f772SChristian Gromm /**
38297a6f772SChristian Gromm  * hdm_read_completion - completion function for submitted Rx URBs
38397a6f772SChristian Gromm  * @urb: the URB that has been completed
38497a6f772SChristian Gromm  *
38597a6f772SChristian Gromm  * This checks the status of the completed URB. In case the URB has been
38697a6f772SChristian Gromm  * unlinked before it is immediately freed. On any other error the MBO transfer
38797a6f772SChristian Gromm  * flag is set. On success it frees allocated resources, removes
38897a6f772SChristian Gromm  * padding bytes -if necessary- and calls the completion function.
38997a6f772SChristian Gromm  *
39097a6f772SChristian Gromm  * Context: interrupt!
39197a6f772SChristian Gromm  */
hdm_read_completion(struct urb * urb)39297a6f772SChristian Gromm static void hdm_read_completion(struct urb *urb)
39397a6f772SChristian Gromm {
39497a6f772SChristian Gromm 	struct mbo *mbo = urb->context;
39597a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(mbo->ifp);
39697a6f772SChristian Gromm 	unsigned int channel = mbo->hdm_channel_id;
39797a6f772SChristian Gromm 	struct device *dev = &mdev->usb_device->dev;
39897a6f772SChristian Gromm 	spinlock_t *lock = mdev->channel_lock + channel;
39997a6f772SChristian Gromm 	unsigned long flags;
40097a6f772SChristian Gromm 
40197a6f772SChristian Gromm 	spin_lock_irqsave(lock, flags);
40297a6f772SChristian Gromm 
40397a6f772SChristian Gromm 	mbo->processed_length = 0;
40497a6f772SChristian Gromm 	mbo->status = MBO_E_INVAL;
40597a6f772SChristian Gromm 	if (likely(mdev->is_channel_healthy[channel])) {
40697a6f772SChristian Gromm 		switch (urb->status) {
40797a6f772SChristian Gromm 		case 0:
40897a6f772SChristian Gromm 		case -ESHUTDOWN:
40997a6f772SChristian Gromm 			mbo->processed_length = urb->actual_length;
41097a6f772SChristian Gromm 			mbo->status = MBO_SUCCESS;
41197a6f772SChristian Gromm 			if (mdev->padding_active[channel] &&
41297a6f772SChristian Gromm 			    hdm_remove_padding(mdev, channel, mbo)) {
41397a6f772SChristian Gromm 				mbo->processed_length = 0;
41497a6f772SChristian Gromm 				mbo->status = MBO_E_INVAL;
41597a6f772SChristian Gromm 			}
41697a6f772SChristian Gromm 			break;
41797a6f772SChristian Gromm 		case -EPIPE:
41897a6f772SChristian Gromm 			dev_warn(dev, "Broken pipe on ep%02x\n",
41997a6f772SChristian Gromm 				 mdev->ep_address[channel]);
42097a6f772SChristian Gromm 			mdev->is_channel_healthy[channel] = false;
42197a6f772SChristian Gromm 			mdev->clear_work[channel].pipe = urb->pipe;
42297a6f772SChristian Gromm 			schedule_work(&mdev->clear_work[channel].ws);
42397a6f772SChristian Gromm 			break;
42497a6f772SChristian Gromm 		case -ENODEV:
42597a6f772SChristian Gromm 		case -EPROTO:
42697a6f772SChristian Gromm 			mbo->status = MBO_E_CLOSE;
42797a6f772SChristian Gromm 			break;
42897a6f772SChristian Gromm 		case -EOVERFLOW:
42997a6f772SChristian Gromm 			dev_warn(dev, "Babble on ep%02x\n",
43097a6f772SChristian Gromm 				 mdev->ep_address[channel]);
43197a6f772SChristian Gromm 			break;
43297a6f772SChristian Gromm 		}
43397a6f772SChristian Gromm 	}
43497a6f772SChristian Gromm 
43597a6f772SChristian Gromm 	spin_unlock_irqrestore(lock, flags);
43697a6f772SChristian Gromm 
43797a6f772SChristian Gromm 	if (likely(mbo->complete))
43897a6f772SChristian Gromm 		mbo->complete(mbo);
43997a6f772SChristian Gromm 	usb_free_urb(urb);
44097a6f772SChristian Gromm }
44197a6f772SChristian Gromm 
44297a6f772SChristian Gromm /**
44397a6f772SChristian Gromm  * hdm_enqueue - receive a buffer to be used for data transfer
44497a6f772SChristian Gromm  * @iface: interface to enqueue to
44597a6f772SChristian Gromm  * @channel: ID of the channel
44697a6f772SChristian Gromm  * @mbo: pointer to the buffer object
44797a6f772SChristian Gromm  *
44897a6f772SChristian Gromm  * This allocates a new URB and fills it according to the channel
44997a6f772SChristian Gromm  * that is being used for transmission of data. Before the URB is
45097a6f772SChristian Gromm  * submitted it is stored in the private anchor list.
45197a6f772SChristian Gromm  *
45297a6f772SChristian Gromm  * Returns 0 on success. On any error the URB is freed and a error code
45397a6f772SChristian Gromm  * is returned.
45497a6f772SChristian Gromm  *
45597a6f772SChristian Gromm  * Context: Could in _some_ cases be interrupt!
45697a6f772SChristian Gromm  */
hdm_enqueue(struct most_interface * iface,int channel,struct mbo * mbo)45797a6f772SChristian Gromm static int hdm_enqueue(struct most_interface *iface, int channel,
45897a6f772SChristian Gromm 		       struct mbo *mbo)
45997a6f772SChristian Gromm {
46097a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(iface);
46197a6f772SChristian Gromm 	struct most_channel_config *conf;
46297a6f772SChristian Gromm 	int retval = 0;
46397a6f772SChristian Gromm 	struct urb *urb;
46497a6f772SChristian Gromm 	unsigned long length;
46597a6f772SChristian Gromm 	void *virt_address;
46697a6f772SChristian Gromm 
46797a6f772SChristian Gromm 	if (!mbo)
46897a6f772SChristian Gromm 		return -EINVAL;
46997a6f772SChristian Gromm 	if (iface->num_channels <= channel || channel < 0)
47097a6f772SChristian Gromm 		return -ECHRNG;
47197a6f772SChristian Gromm 
47297a6f772SChristian Gromm 	urb = usb_alloc_urb(NO_ISOCHRONOUS_URB, GFP_KERNEL);
47397a6f772SChristian Gromm 	if (!urb)
47497a6f772SChristian Gromm 		return -ENOMEM;
47597a6f772SChristian Gromm 
47697a6f772SChristian Gromm 	conf = &mdev->conf[channel];
47797a6f772SChristian Gromm 
47897a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
47997a6f772SChristian Gromm 	if (!mdev->usb_device) {
48097a6f772SChristian Gromm 		retval = -ENODEV;
48197a6f772SChristian Gromm 		goto err_free_urb;
48297a6f772SChristian Gromm 	}
48397a6f772SChristian Gromm 
48497a6f772SChristian Gromm 	if ((conf->direction & MOST_CH_TX) && mdev->padding_active[channel] &&
48597a6f772SChristian Gromm 	    hdm_add_padding(mdev, channel, mbo)) {
48697a6f772SChristian Gromm 		retval = -EINVAL;
48797a6f772SChristian Gromm 		goto err_free_urb;
48897a6f772SChristian Gromm 	}
48997a6f772SChristian Gromm 
49097a6f772SChristian Gromm 	urb->transfer_dma = mbo->bus_address;
49197a6f772SChristian Gromm 	virt_address = mbo->virt_address;
49297a6f772SChristian Gromm 	length = mbo->buffer_length;
49397a6f772SChristian Gromm 
49497a6f772SChristian Gromm 	if (conf->direction & MOST_CH_TX) {
49597a6f772SChristian Gromm 		usb_fill_bulk_urb(urb, mdev->usb_device,
49697a6f772SChristian Gromm 				  usb_sndbulkpipe(mdev->usb_device,
49797a6f772SChristian Gromm 						  mdev->ep_address[channel]),
49897a6f772SChristian Gromm 				  virt_address,
49997a6f772SChristian Gromm 				  length,
50097a6f772SChristian Gromm 				  hdm_write_completion,
50197a6f772SChristian Gromm 				  mbo);
50297a6f772SChristian Gromm 		if (conf->data_type != MOST_CH_ISOC &&
50397a6f772SChristian Gromm 		    conf->data_type != MOST_CH_SYNC)
50497a6f772SChristian Gromm 			urb->transfer_flags |= URB_ZERO_PACKET;
50597a6f772SChristian Gromm 	} else {
50697a6f772SChristian Gromm 		usb_fill_bulk_urb(urb, mdev->usb_device,
50797a6f772SChristian Gromm 				  usb_rcvbulkpipe(mdev->usb_device,
50897a6f772SChristian Gromm 						  mdev->ep_address[channel]),
50997a6f772SChristian Gromm 				  virt_address,
51097a6f772SChristian Gromm 				  length + conf->extra_len,
51197a6f772SChristian Gromm 				  hdm_read_completion,
51297a6f772SChristian Gromm 				  mbo);
51397a6f772SChristian Gromm 	}
51497a6f772SChristian Gromm 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
51597a6f772SChristian Gromm 
51697a6f772SChristian Gromm 	usb_anchor_urb(urb, &mdev->busy_urbs[channel]);
51797a6f772SChristian Gromm 
51897a6f772SChristian Gromm 	retval = usb_submit_urb(urb, GFP_KERNEL);
51997a6f772SChristian Gromm 	if (retval) {
52097a6f772SChristian Gromm 		dev_err(&mdev->usb_device->dev,
52197a6f772SChristian Gromm 			"URB submit failed with error %d.\n", retval);
52297a6f772SChristian Gromm 		goto err_unanchor_urb;
52397a6f772SChristian Gromm 	}
52497a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
52597a6f772SChristian Gromm 	return 0;
52697a6f772SChristian Gromm 
52797a6f772SChristian Gromm err_unanchor_urb:
52897a6f772SChristian Gromm 	usb_unanchor_urb(urb);
52997a6f772SChristian Gromm err_free_urb:
53097a6f772SChristian Gromm 	usb_free_urb(urb);
53197a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
53297a6f772SChristian Gromm 	return retval;
53397a6f772SChristian Gromm }
53497a6f772SChristian Gromm 
hdm_dma_alloc(struct mbo * mbo,u32 size)53597a6f772SChristian Gromm static void *hdm_dma_alloc(struct mbo *mbo, u32 size)
53697a6f772SChristian Gromm {
53797a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(mbo->ifp);
53897a6f772SChristian Gromm 
53997a6f772SChristian Gromm 	return usb_alloc_coherent(mdev->usb_device, size, GFP_KERNEL,
54097a6f772SChristian Gromm 				  &mbo->bus_address);
54197a6f772SChristian Gromm }
54297a6f772SChristian Gromm 
hdm_dma_free(struct mbo * mbo,u32 size)54397a6f772SChristian Gromm static void hdm_dma_free(struct mbo *mbo, u32 size)
54497a6f772SChristian Gromm {
54597a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(mbo->ifp);
54697a6f772SChristian Gromm 
54797a6f772SChristian Gromm 	usb_free_coherent(mdev->usb_device, size, mbo->virt_address,
54897a6f772SChristian Gromm 			  mbo->bus_address);
54997a6f772SChristian Gromm }
55097a6f772SChristian Gromm 
55197a6f772SChristian Gromm /**
55297a6f772SChristian Gromm  * hdm_configure_channel - receive channel configuration from core
55397a6f772SChristian Gromm  * @iface: interface
55497a6f772SChristian Gromm  * @channel: channel ID
55597a6f772SChristian Gromm  * @conf: structure that holds the configuration information
55697a6f772SChristian Gromm  *
55797a6f772SChristian Gromm  * The attached network interface controller (NIC) supports a padding mode
55897a6f772SChristian Gromm  * to avoid short packets on USB, hence increasing the performance due to a
55997a6f772SChristian Gromm  * lower interrupt load. This mode is default for synchronous data and can
56097a6f772SChristian Gromm  * be switched on for isochronous data. In case padding is active the
56197a6f772SChristian Gromm  * driver needs to know the frame size of the payload in order to calculate
56297a6f772SChristian Gromm  * the number of bytes it needs to pad when transmitting or to cut off when
56397a6f772SChristian Gromm  * receiving data.
56497a6f772SChristian Gromm  *
56597a6f772SChristian Gromm  */
hdm_configure_channel(struct most_interface * iface,int channel,struct most_channel_config * conf)56697a6f772SChristian Gromm static int hdm_configure_channel(struct most_interface *iface, int channel,
56797a6f772SChristian Gromm 				 struct most_channel_config *conf)
56897a6f772SChristian Gromm {
56997a6f772SChristian Gromm 	unsigned int num_frames;
57097a6f772SChristian Gromm 	unsigned int frame_size;
57197a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(iface);
57297a6f772SChristian Gromm 	struct device *dev = &mdev->usb_device->dev;
57397a6f772SChristian Gromm 
57497a6f772SChristian Gromm 	if (!conf) {
57597a6f772SChristian Gromm 		dev_err(dev, "Bad config pointer.\n");
57697a6f772SChristian Gromm 		return -EINVAL;
57797a6f772SChristian Gromm 	}
57897a6f772SChristian Gromm 	if (channel < 0 || channel >= iface->num_channels) {
57997a6f772SChristian Gromm 		dev_err(dev, "Channel ID out of range.\n");
58097a6f772SChristian Gromm 		return -EINVAL;
58197a6f772SChristian Gromm 	}
58297a6f772SChristian Gromm 
58397a6f772SChristian Gromm 	mdev->is_channel_healthy[channel] = true;
58497a6f772SChristian Gromm 	mdev->clear_work[channel].channel = channel;
58597a6f772SChristian Gromm 	mdev->clear_work[channel].mdev = mdev;
58697a6f772SChristian Gromm 	INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt);
58797a6f772SChristian Gromm 
58897a6f772SChristian Gromm 	if (!conf->num_buffers || !conf->buffer_size) {
58997a6f772SChristian Gromm 		dev_err(dev, "Misconfig: buffer size or #buffers zero.\n");
59097a6f772SChristian Gromm 		return -EINVAL;
59197a6f772SChristian Gromm 	}
59297a6f772SChristian Gromm 
59397a6f772SChristian Gromm 	if (conf->data_type != MOST_CH_SYNC &&
59497a6f772SChristian Gromm 	    !(conf->data_type == MOST_CH_ISOC &&
59597a6f772SChristian Gromm 	      conf->packets_per_xact != 0xFF)) {
59697a6f772SChristian Gromm 		mdev->padding_active[channel] = false;
59797a6f772SChristian Gromm 		/*
59897a6f772SChristian Gromm 		 * Since the NIC's padding mode is not going to be
59997a6f772SChristian Gromm 		 * used, we can skip the frame size calculations and
60097a6f772SChristian Gromm 		 * move directly on to exit.
60197a6f772SChristian Gromm 		 */
60297a6f772SChristian Gromm 		goto exit;
60397a6f772SChristian Gromm 	}
60497a6f772SChristian Gromm 
60597a6f772SChristian Gromm 	mdev->padding_active[channel] = true;
60697a6f772SChristian Gromm 
60797a6f772SChristian Gromm 	frame_size = get_stream_frame_size(&mdev->dev, conf);
60897a6f772SChristian Gromm 	if (frame_size == 0 || frame_size > USB_MTU) {
60997a6f772SChristian Gromm 		dev_warn(dev, "Misconfig: frame size wrong\n");
61097a6f772SChristian Gromm 		return -EINVAL;
61197a6f772SChristian Gromm 	}
61297a6f772SChristian Gromm 
61397a6f772SChristian Gromm 	num_frames = conf->buffer_size / frame_size;
61497a6f772SChristian Gromm 
61597a6f772SChristian Gromm 	if (conf->buffer_size % frame_size) {
61697a6f772SChristian Gromm 		u16 old_size = conf->buffer_size;
61797a6f772SChristian Gromm 
61897a6f772SChristian Gromm 		conf->buffer_size = num_frames * frame_size;
61997a6f772SChristian Gromm 		dev_warn(dev, "%s: fixed buffer size (%d -> %d)\n",
62097a6f772SChristian Gromm 			 mdev->suffix[channel], old_size, conf->buffer_size);
62197a6f772SChristian Gromm 	}
62297a6f772SChristian Gromm 
62397a6f772SChristian Gromm 	/* calculate extra length to comply w/ HW padding */
62497a6f772SChristian Gromm 	conf->extra_len = num_frames * (USB_MTU - frame_size);
62597a6f772SChristian Gromm 
62697a6f772SChristian Gromm exit:
62797a6f772SChristian Gromm 	mdev->conf[channel] = *conf;
62897a6f772SChristian Gromm 	if (conf->data_type == MOST_CH_ASYNC) {
62997a6f772SChristian Gromm 		u16 ep = mdev->ep_address[channel];
63097a6f772SChristian Gromm 
63197a6f772SChristian Gromm 		if (start_sync_ep(mdev->usb_device, ep) < 0)
63297a6f772SChristian Gromm 			dev_warn(dev, "sync for ep%02x failed", ep);
63397a6f772SChristian Gromm 	}
63497a6f772SChristian Gromm 	return 0;
63597a6f772SChristian Gromm }
63697a6f772SChristian Gromm 
63797a6f772SChristian Gromm /**
63897a6f772SChristian Gromm  * hdm_request_netinfo - request network information
63997a6f772SChristian Gromm  * @iface: pointer to interface
64097a6f772SChristian Gromm  * @channel: channel ID
64197a6f772SChristian Gromm  *
64297a6f772SChristian Gromm  * This is used as trigger to set up the link status timer that
64397a6f772SChristian Gromm  * polls for the NI state of the INIC every 2 seconds.
64497a6f772SChristian Gromm  *
64597a6f772SChristian Gromm  */
hdm_request_netinfo(struct most_interface * iface,int channel,void (* on_netinfo)(struct most_interface *,unsigned char,unsigned char *))64697a6f772SChristian Gromm static void hdm_request_netinfo(struct most_interface *iface, int channel,
64797a6f772SChristian Gromm 				void (*on_netinfo)(struct most_interface *,
64897a6f772SChristian Gromm 						   unsigned char,
64997a6f772SChristian Gromm 						   unsigned char *))
65097a6f772SChristian Gromm {
65197a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev(iface);
65297a6f772SChristian Gromm 
65397a6f772SChristian Gromm 	mdev->on_netinfo = on_netinfo;
65497a6f772SChristian Gromm 	if (!on_netinfo)
65597a6f772SChristian Gromm 		return;
65697a6f772SChristian Gromm 
65797a6f772SChristian Gromm 	mdev->link_stat_timer.expires = jiffies + HZ;
65897a6f772SChristian Gromm 	mod_timer(&mdev->link_stat_timer, mdev->link_stat_timer.expires);
65997a6f772SChristian Gromm }
66097a6f772SChristian Gromm 
66197a6f772SChristian Gromm /**
66297a6f772SChristian Gromm  * link_stat_timer_handler - schedule work obtaining mac address and link status
663*fba3993eSRandy Dunlap  * @t: pointer to timer_list which holds a pointer to the USB device instance
66497a6f772SChristian Gromm  *
66597a6f772SChristian Gromm  * The handler runs in interrupt context. That's why we need to defer the
66697a6f772SChristian Gromm  * tasks to a work queue.
66797a6f772SChristian Gromm  */
link_stat_timer_handler(struct timer_list * t)66897a6f772SChristian Gromm static void link_stat_timer_handler(struct timer_list *t)
66997a6f772SChristian Gromm {
67097a6f772SChristian Gromm 	struct most_dev *mdev = from_timer(mdev, t, link_stat_timer);
67197a6f772SChristian Gromm 
67297a6f772SChristian Gromm 	schedule_work(&mdev->poll_work_obj);
67397a6f772SChristian Gromm 	mdev->link_stat_timer.expires = jiffies + (2 * HZ);
67497a6f772SChristian Gromm 	add_timer(&mdev->link_stat_timer);
67597a6f772SChristian Gromm }
67697a6f772SChristian Gromm 
67797a6f772SChristian Gromm /**
67897a6f772SChristian Gromm  * wq_netinfo - work queue function to deliver latest networking information
67997a6f772SChristian Gromm  * @wq_obj: object that holds data for our deferred work to do
68097a6f772SChristian Gromm  *
68197a6f772SChristian Gromm  * This retrieves the network interface status of the USB INIC
68297a6f772SChristian Gromm  */
wq_netinfo(struct work_struct * wq_obj)68397a6f772SChristian Gromm static void wq_netinfo(struct work_struct *wq_obj)
68497a6f772SChristian Gromm {
68597a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev_from_work(wq_obj);
68697a6f772SChristian Gromm 	struct usb_device *usb_device = mdev->usb_device;
68797a6f772SChristian Gromm 	struct device *dev = &usb_device->dev;
68897a6f772SChristian Gromm 	u16 hi, mi, lo, link;
68997a6f772SChristian Gromm 	u8 hw_addr[6];
69097a6f772SChristian Gromm 
69197a6f772SChristian Gromm 	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_HI, &hi)) {
69297a6f772SChristian Gromm 		dev_err(dev, "Vendor request 'hw_addr_hi' failed\n");
69397a6f772SChristian Gromm 		return;
69497a6f772SChristian Gromm 	}
69597a6f772SChristian Gromm 
69697a6f772SChristian Gromm 	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_MI, &mi)) {
69797a6f772SChristian Gromm 		dev_err(dev, "Vendor request 'hw_addr_mid' failed\n");
69897a6f772SChristian Gromm 		return;
69997a6f772SChristian Gromm 	}
70097a6f772SChristian Gromm 
70197a6f772SChristian Gromm 	if (drci_rd_reg(usb_device, DRCI_REG_HW_ADDR_LO, &lo)) {
70297a6f772SChristian Gromm 		dev_err(dev, "Vendor request 'hw_addr_low' failed\n");
70397a6f772SChristian Gromm 		return;
70497a6f772SChristian Gromm 	}
70597a6f772SChristian Gromm 
70697a6f772SChristian Gromm 	if (drci_rd_reg(usb_device, DRCI_REG_NI_STATE, &link)) {
70797a6f772SChristian Gromm 		dev_err(dev, "Vendor request 'link status' failed\n");
70897a6f772SChristian Gromm 		return;
70997a6f772SChristian Gromm 	}
71097a6f772SChristian Gromm 
71197a6f772SChristian Gromm 	hw_addr[0] = hi >> 8;
71297a6f772SChristian Gromm 	hw_addr[1] = hi;
71397a6f772SChristian Gromm 	hw_addr[2] = mi >> 8;
71497a6f772SChristian Gromm 	hw_addr[3] = mi;
71597a6f772SChristian Gromm 	hw_addr[4] = lo >> 8;
71697a6f772SChristian Gromm 	hw_addr[5] = lo;
71797a6f772SChristian Gromm 
71897a6f772SChristian Gromm 	if (mdev->on_netinfo)
71997a6f772SChristian Gromm 		mdev->on_netinfo(&mdev->iface, link, hw_addr);
72097a6f772SChristian Gromm }
72197a6f772SChristian Gromm 
72297a6f772SChristian Gromm /**
72397a6f772SChristian Gromm  * wq_clear_halt - work queue function
72497a6f772SChristian Gromm  * @wq_obj: work_struct object to execute
72597a6f772SChristian Gromm  *
72697a6f772SChristian Gromm  * This sends a clear_halt to the given USB pipe.
72797a6f772SChristian Gromm  */
wq_clear_halt(struct work_struct * wq_obj)72897a6f772SChristian Gromm static void wq_clear_halt(struct work_struct *wq_obj)
72997a6f772SChristian Gromm {
73097a6f772SChristian Gromm 	struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj);
73197a6f772SChristian Gromm 	struct most_dev *mdev = clear_work->mdev;
73297a6f772SChristian Gromm 	unsigned int channel = clear_work->channel;
73397a6f772SChristian Gromm 	int pipe = clear_work->pipe;
73497a6f772SChristian Gromm 	int snd_pipe;
73597a6f772SChristian Gromm 	int peer;
73697a6f772SChristian Gromm 
73797a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
73897a6f772SChristian Gromm 	most_stop_enqueue(&mdev->iface, channel);
73997a6f772SChristian Gromm 	usb_kill_anchored_urbs(&mdev->busy_urbs[channel]);
74097a6f772SChristian Gromm 	if (usb_clear_halt(mdev->usb_device, pipe))
74197a6f772SChristian Gromm 		dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
74297a6f772SChristian Gromm 
74397a6f772SChristian Gromm 	/* If the functional Stall condition has been set on an
74497a6f772SChristian Gromm 	 * asynchronous rx channel, we need to clear the tx channel
74597a6f772SChristian Gromm 	 * too, since the hardware runs its clean-up sequence on both
74697a6f772SChristian Gromm 	 * channels, as they are physically one on the network.
74797a6f772SChristian Gromm 	 *
74897a6f772SChristian Gromm 	 * The USB interface that exposes the asynchronous channels
74997a6f772SChristian Gromm 	 * contains always two endpoints, and two only.
75097a6f772SChristian Gromm 	 */
75197a6f772SChristian Gromm 	if (mdev->conf[channel].data_type == MOST_CH_ASYNC &&
75297a6f772SChristian Gromm 	    mdev->conf[channel].direction == MOST_CH_RX) {
75397a6f772SChristian Gromm 		if (channel == 0)
75497a6f772SChristian Gromm 			peer = 1;
75597a6f772SChristian Gromm 		else
75697a6f772SChristian Gromm 			peer = 0;
75797a6f772SChristian Gromm 		snd_pipe = usb_sndbulkpipe(mdev->usb_device,
75897a6f772SChristian Gromm 					   mdev->ep_address[peer]);
75997a6f772SChristian Gromm 		usb_clear_halt(mdev->usb_device, snd_pipe);
76097a6f772SChristian Gromm 	}
76197a6f772SChristian Gromm 	mdev->is_channel_healthy[channel] = true;
76297a6f772SChristian Gromm 	most_resume_enqueue(&mdev->iface, channel);
76397a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
76497a6f772SChristian Gromm }
76597a6f772SChristian Gromm 
766*fba3993eSRandy Dunlap /*
76797a6f772SChristian Gromm  * hdm_usb_fops - file operation table for USB driver
76897a6f772SChristian Gromm  */
76997a6f772SChristian Gromm static const struct file_operations hdm_usb_fops = {
77097a6f772SChristian Gromm 	.owner = THIS_MODULE,
77197a6f772SChristian Gromm };
77297a6f772SChristian Gromm 
773*fba3993eSRandy Dunlap /*
77497a6f772SChristian Gromm  * usb_device_id - ID table for HCD device probing
77597a6f772SChristian Gromm  */
77697a6f772SChristian Gromm static const struct usb_device_id usbid[] = {
77797a6f772SChristian Gromm 	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_BRDG), },
77897a6f772SChristian Gromm 	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81118), },
77997a6f772SChristian Gromm 	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81119), },
78097a6f772SChristian Gromm 	{ USB_DEVICE(USB_VENDOR_ID_SMSC, USB_DEV_ID_OS81210), },
78197a6f772SChristian Gromm 	{ } /* Terminating entry */
78297a6f772SChristian Gromm };
78397a6f772SChristian Gromm 
78497a6f772SChristian Gromm struct regs {
78597a6f772SChristian Gromm 	const char *name;
78697a6f772SChristian Gromm 	u16 reg;
78797a6f772SChristian Gromm };
78897a6f772SChristian Gromm 
78997a6f772SChristian Gromm static const struct regs ro_regs[] = {
79097a6f772SChristian Gromm 	{ "ni_state", DRCI_REG_NI_STATE },
79197a6f772SChristian Gromm 	{ "packet_bandwidth", DRCI_REG_PACKET_BW },
79297a6f772SChristian Gromm 	{ "node_address", DRCI_REG_NODE_ADDR },
79397a6f772SChristian Gromm 	{ "node_position", DRCI_REG_NODE_POS },
79497a6f772SChristian Gromm };
79597a6f772SChristian Gromm 
79697a6f772SChristian Gromm static const struct regs rw_regs[] = {
79797a6f772SChristian Gromm 	{ "mep_filter", DRCI_REG_MEP_FILTER },
79897a6f772SChristian Gromm 	{ "mep_hash0", DRCI_REG_HASH_TBL0 },
79997a6f772SChristian Gromm 	{ "mep_hash1", DRCI_REG_HASH_TBL1 },
80097a6f772SChristian Gromm 	{ "mep_hash2", DRCI_REG_HASH_TBL2 },
80197a6f772SChristian Gromm 	{ "mep_hash3", DRCI_REG_HASH_TBL3 },
80297a6f772SChristian Gromm 	{ "mep_eui48_hi", DRCI_REG_HW_ADDR_HI },
80397a6f772SChristian Gromm 	{ "mep_eui48_mi", DRCI_REG_HW_ADDR_MI },
80497a6f772SChristian Gromm 	{ "mep_eui48_lo", DRCI_REG_HW_ADDR_LO },
80597a6f772SChristian Gromm };
80697a6f772SChristian Gromm 
get_stat_reg_addr(const struct regs * regs,int size,const char * name,u16 * reg_addr)80797a6f772SChristian Gromm static int get_stat_reg_addr(const struct regs *regs, int size,
80897a6f772SChristian Gromm 			     const char *name, u16 *reg_addr)
80997a6f772SChristian Gromm {
81097a6f772SChristian Gromm 	int i;
81197a6f772SChristian Gromm 
81297a6f772SChristian Gromm 	for (i = 0; i < size; i++) {
81397a6f772SChristian Gromm 		if (sysfs_streq(name, regs[i].name)) {
81497a6f772SChristian Gromm 			*reg_addr = regs[i].reg;
81597a6f772SChristian Gromm 			return 0;
81697a6f772SChristian Gromm 		}
81797a6f772SChristian Gromm 	}
81897a6f772SChristian Gromm 	return -EINVAL;
81997a6f772SChristian Gromm }
82097a6f772SChristian Gromm 
82197a6f772SChristian Gromm #define get_static_reg_addr(regs, name, reg_addr) \
82297a6f772SChristian Gromm 	get_stat_reg_addr(regs, ARRAY_SIZE(regs), name, reg_addr)
82397a6f772SChristian Gromm 
value_show(struct device * dev,struct device_attribute * attr,char * buf)82497a6f772SChristian Gromm static ssize_t value_show(struct device *dev, struct device_attribute *attr,
82597a6f772SChristian Gromm 			  char *buf)
82697a6f772SChristian Gromm {
82797a6f772SChristian Gromm 	const char *name = attr->attr.name;
82897a6f772SChristian Gromm 	struct most_dci_obj *dci_obj = to_dci_obj(dev);
82997a6f772SChristian Gromm 	u16 val;
83097a6f772SChristian Gromm 	u16 reg_addr;
83197a6f772SChristian Gromm 	int err;
83297a6f772SChristian Gromm 
83397a6f772SChristian Gromm 	if (sysfs_streq(name, "arb_address"))
834cd455ebbSJing Yao 		return sysfs_emit(buf, "%04x\n", dci_obj->reg_addr);
83597a6f772SChristian Gromm 
83697a6f772SChristian Gromm 	if (sysfs_streq(name, "arb_value"))
83797a6f772SChristian Gromm 		reg_addr = dci_obj->reg_addr;
83897a6f772SChristian Gromm 	else if (get_static_reg_addr(ro_regs, name, &reg_addr) &&
83997a6f772SChristian Gromm 		 get_static_reg_addr(rw_regs, name, &reg_addr))
84097a6f772SChristian Gromm 		return -EINVAL;
84197a6f772SChristian Gromm 
84297a6f772SChristian Gromm 	err = drci_rd_reg(dci_obj->usb_device, reg_addr, &val);
84397a6f772SChristian Gromm 	if (err < 0)
84497a6f772SChristian Gromm 		return err;
84597a6f772SChristian Gromm 
846cd455ebbSJing Yao 	return sysfs_emit(buf, "%04x\n", val);
84797a6f772SChristian Gromm }
84897a6f772SChristian Gromm 
value_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)84997a6f772SChristian Gromm static ssize_t value_store(struct device *dev, struct device_attribute *attr,
85097a6f772SChristian Gromm 			   const char *buf, size_t count)
85197a6f772SChristian Gromm {
85297a6f772SChristian Gromm 	u16 val;
85397a6f772SChristian Gromm 	u16 reg_addr;
85497a6f772SChristian Gromm 	const char *name = attr->attr.name;
85597a6f772SChristian Gromm 	struct most_dci_obj *dci_obj = to_dci_obj(dev);
85697a6f772SChristian Gromm 	struct usb_device *usb_dev = dci_obj->usb_device;
85797a6f772SChristian Gromm 	int err;
85897a6f772SChristian Gromm 
85997a6f772SChristian Gromm 	err = kstrtou16(buf, 16, &val);
86097a6f772SChristian Gromm 	if (err)
86197a6f772SChristian Gromm 		return err;
86297a6f772SChristian Gromm 
86397a6f772SChristian Gromm 	if (sysfs_streq(name, "arb_address")) {
86497a6f772SChristian Gromm 		dci_obj->reg_addr = val;
86597a6f772SChristian Gromm 		return count;
86697a6f772SChristian Gromm 	}
86797a6f772SChristian Gromm 
86897a6f772SChristian Gromm 	if (sysfs_streq(name, "arb_value"))
86997a6f772SChristian Gromm 		err = drci_wr_reg(usb_dev, dci_obj->reg_addr, val);
87097a6f772SChristian Gromm 	else if (sysfs_streq(name, "sync_ep"))
87197a6f772SChristian Gromm 		err = start_sync_ep(usb_dev, val);
87297a6f772SChristian Gromm 	else if (!get_static_reg_addr(rw_regs, name, &reg_addr))
87397a6f772SChristian Gromm 		err = drci_wr_reg(usb_dev, reg_addr, val);
87497a6f772SChristian Gromm 	else
87597a6f772SChristian Gromm 		return -EINVAL;
87697a6f772SChristian Gromm 
87797a6f772SChristian Gromm 	if (err < 0)
87897a6f772SChristian Gromm 		return err;
87997a6f772SChristian Gromm 
88097a6f772SChristian Gromm 	return count;
88197a6f772SChristian Gromm }
88297a6f772SChristian Gromm 
88397a6f772SChristian Gromm static DEVICE_ATTR(ni_state, 0444, value_show, NULL);
88497a6f772SChristian Gromm static DEVICE_ATTR(packet_bandwidth, 0444, value_show, NULL);
88597a6f772SChristian Gromm static DEVICE_ATTR(node_address, 0444, value_show, NULL);
88697a6f772SChristian Gromm static DEVICE_ATTR(node_position, 0444, value_show, NULL);
88797a6f772SChristian Gromm static DEVICE_ATTR(sync_ep, 0200, NULL, value_store);
88897a6f772SChristian Gromm static DEVICE_ATTR(mep_filter, 0644, value_show, value_store);
88997a6f772SChristian Gromm static DEVICE_ATTR(mep_hash0, 0644, value_show, value_store);
89097a6f772SChristian Gromm static DEVICE_ATTR(mep_hash1, 0644, value_show, value_store);
89197a6f772SChristian Gromm static DEVICE_ATTR(mep_hash2, 0644, value_show, value_store);
89297a6f772SChristian Gromm static DEVICE_ATTR(mep_hash3, 0644, value_show, value_store);
89397a6f772SChristian Gromm static DEVICE_ATTR(mep_eui48_hi, 0644, value_show, value_store);
89497a6f772SChristian Gromm static DEVICE_ATTR(mep_eui48_mi, 0644, value_show, value_store);
89597a6f772SChristian Gromm static DEVICE_ATTR(mep_eui48_lo, 0644, value_show, value_store);
89697a6f772SChristian Gromm static DEVICE_ATTR(arb_address, 0644, value_show, value_store);
89797a6f772SChristian Gromm static DEVICE_ATTR(arb_value, 0644, value_show, value_store);
89897a6f772SChristian Gromm 
89997a6f772SChristian Gromm static struct attribute *dci_attrs[] = {
90097a6f772SChristian Gromm 	&dev_attr_ni_state.attr,
90197a6f772SChristian Gromm 	&dev_attr_packet_bandwidth.attr,
90297a6f772SChristian Gromm 	&dev_attr_node_address.attr,
90397a6f772SChristian Gromm 	&dev_attr_node_position.attr,
90497a6f772SChristian Gromm 	&dev_attr_sync_ep.attr,
90597a6f772SChristian Gromm 	&dev_attr_mep_filter.attr,
90697a6f772SChristian Gromm 	&dev_attr_mep_hash0.attr,
90797a6f772SChristian Gromm 	&dev_attr_mep_hash1.attr,
90897a6f772SChristian Gromm 	&dev_attr_mep_hash2.attr,
90997a6f772SChristian Gromm 	&dev_attr_mep_hash3.attr,
91097a6f772SChristian Gromm 	&dev_attr_mep_eui48_hi.attr,
91197a6f772SChristian Gromm 	&dev_attr_mep_eui48_mi.attr,
91297a6f772SChristian Gromm 	&dev_attr_mep_eui48_lo.attr,
91397a6f772SChristian Gromm 	&dev_attr_arb_address.attr,
91497a6f772SChristian Gromm 	&dev_attr_arb_value.attr,
91597a6f772SChristian Gromm 	NULL,
91697a6f772SChristian Gromm };
91797a6f772SChristian Gromm 
91897a6f772SChristian Gromm ATTRIBUTE_GROUPS(dci);
91997a6f772SChristian Gromm 
release_dci(struct device * dev)92097a6f772SChristian Gromm static void release_dci(struct device *dev)
92197a6f772SChristian Gromm {
92297a6f772SChristian Gromm 	struct most_dci_obj *dci = to_dci_obj(dev);
92397a6f772SChristian Gromm 
92497a6f772SChristian Gromm 	put_device(dev->parent);
92597a6f772SChristian Gromm 	kfree(dci);
92697a6f772SChristian Gromm }
92797a6f772SChristian Gromm 
release_mdev(struct device * dev)92897a6f772SChristian Gromm static void release_mdev(struct device *dev)
92997a6f772SChristian Gromm {
93097a6f772SChristian Gromm 	struct most_dev *mdev = to_mdev_from_dev(dev);
93197a6f772SChristian Gromm 
93297a6f772SChristian Gromm 	kfree(mdev);
93397a6f772SChristian Gromm }
93497a6f772SChristian Gromm /**
93597a6f772SChristian Gromm  * hdm_probe - probe function of USB device driver
93697a6f772SChristian Gromm  * @interface: Interface of the attached USB device
93797a6f772SChristian Gromm  * @id: Pointer to the USB ID table.
93897a6f772SChristian Gromm  *
93997a6f772SChristian Gromm  * This allocates and initializes the device instance, adds the new
94097a6f772SChristian Gromm  * entry to the internal list, scans the USB descriptors and registers
94197a6f772SChristian Gromm  * the interface with the core.
94297a6f772SChristian Gromm  * Additionally, the DCI objects are created and the hardware is sync'd.
94397a6f772SChristian Gromm  *
94497a6f772SChristian Gromm  * Return 0 on success. In case of an error a negative number is returned.
94597a6f772SChristian Gromm  */
94697a6f772SChristian Gromm static int
hdm_probe(struct usb_interface * interface,const struct usb_device_id * id)94797a6f772SChristian Gromm hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
94897a6f772SChristian Gromm {
94997a6f772SChristian Gromm 	struct usb_host_interface *usb_iface_desc = interface->cur_altsetting;
95097a6f772SChristian Gromm 	struct usb_device *usb_dev = interface_to_usbdev(interface);
95197a6f772SChristian Gromm 	struct device *dev = &usb_dev->dev;
95297a6f772SChristian Gromm 	struct most_dev *mdev;
95397a6f772SChristian Gromm 	unsigned int i;
95497a6f772SChristian Gromm 	unsigned int num_endpoints;
95597a6f772SChristian Gromm 	struct most_channel_capability *tmp_cap;
95697a6f772SChristian Gromm 	struct usb_endpoint_descriptor *ep_desc;
95797a6f772SChristian Gromm 	int ret = -ENOMEM;
95897a6f772SChristian Gromm 
95997a6f772SChristian Gromm 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
96097a6f772SChristian Gromm 	if (!mdev)
96197a6f772SChristian Gromm 		return -ENOMEM;
96297a6f772SChristian Gromm 
96397a6f772SChristian Gromm 	usb_set_intfdata(interface, mdev);
96497a6f772SChristian Gromm 	num_endpoints = usb_iface_desc->desc.bNumEndpoints;
96597a6f772SChristian Gromm 	if (num_endpoints > MAX_NUM_ENDPOINTS) {
96697a6f772SChristian Gromm 		kfree(mdev);
96797a6f772SChristian Gromm 		return -EINVAL;
96897a6f772SChristian Gromm 	}
96997a6f772SChristian Gromm 	mutex_init(&mdev->io_mutex);
97097a6f772SChristian Gromm 	INIT_WORK(&mdev->poll_work_obj, wq_netinfo);
97197a6f772SChristian Gromm 	timer_setup(&mdev->link_stat_timer, link_stat_timer_handler, 0);
97297a6f772SChristian Gromm 
97397a6f772SChristian Gromm 	mdev->usb_device = usb_dev;
97497a6f772SChristian Gromm 	mdev->link_stat_timer.expires = jiffies + (2 * HZ);
97597a6f772SChristian Gromm 
97697a6f772SChristian Gromm 	mdev->iface.mod = hdm_usb_fops.owner;
97797a6f772SChristian Gromm 	mdev->iface.dev = &mdev->dev;
97897a6f772SChristian Gromm 	mdev->iface.driver_dev = &interface->dev;
97997a6f772SChristian Gromm 	mdev->iface.interface = ITYPE_USB;
98097a6f772SChristian Gromm 	mdev->iface.configure = hdm_configure_channel;
98197a6f772SChristian Gromm 	mdev->iface.request_netinfo = hdm_request_netinfo;
98297a6f772SChristian Gromm 	mdev->iface.enqueue = hdm_enqueue;
98397a6f772SChristian Gromm 	mdev->iface.poison_channel = hdm_poison_channel;
98497a6f772SChristian Gromm 	mdev->iface.dma_alloc = hdm_dma_alloc;
98597a6f772SChristian Gromm 	mdev->iface.dma_free = hdm_dma_free;
98697a6f772SChristian Gromm 	mdev->iface.description = mdev->description;
98797a6f772SChristian Gromm 	mdev->iface.num_channels = num_endpoints;
98897a6f772SChristian Gromm 
98997a6f772SChristian Gromm 	snprintf(mdev->description, sizeof(mdev->description),
99097a6f772SChristian Gromm 		 "%d-%s:%d.%d",
99197a6f772SChristian Gromm 		 usb_dev->bus->busnum,
99297a6f772SChristian Gromm 		 usb_dev->devpath,
99397a6f772SChristian Gromm 		 usb_dev->config->desc.bConfigurationValue,
99497a6f772SChristian Gromm 		 usb_iface_desc->desc.bInterfaceNumber);
99597a6f772SChristian Gromm 
99697a6f772SChristian Gromm 	mdev->dev.init_name = mdev->description;
99797a6f772SChristian Gromm 	mdev->dev.parent = &interface->dev;
99897a6f772SChristian Gromm 	mdev->dev.release = release_mdev;
99997a6f772SChristian Gromm 	mdev->conf = kcalloc(num_endpoints, sizeof(*mdev->conf), GFP_KERNEL);
100097a6f772SChristian Gromm 	if (!mdev->conf)
100197a6f772SChristian Gromm 		goto err_free_mdev;
100297a6f772SChristian Gromm 
100397a6f772SChristian Gromm 	mdev->cap = kcalloc(num_endpoints, sizeof(*mdev->cap), GFP_KERNEL);
100497a6f772SChristian Gromm 	if (!mdev->cap)
100597a6f772SChristian Gromm 		goto err_free_conf;
100697a6f772SChristian Gromm 
100797a6f772SChristian Gromm 	mdev->iface.channel_vector = mdev->cap;
100897a6f772SChristian Gromm 	mdev->ep_address =
100997a6f772SChristian Gromm 		kcalloc(num_endpoints, sizeof(*mdev->ep_address), GFP_KERNEL);
101097a6f772SChristian Gromm 	if (!mdev->ep_address)
101197a6f772SChristian Gromm 		goto err_free_cap;
101297a6f772SChristian Gromm 
101397a6f772SChristian Gromm 	mdev->busy_urbs =
101497a6f772SChristian Gromm 		kcalloc(num_endpoints, sizeof(*mdev->busy_urbs), GFP_KERNEL);
101597a6f772SChristian Gromm 	if (!mdev->busy_urbs)
101697a6f772SChristian Gromm 		goto err_free_ep_address;
101797a6f772SChristian Gromm 
101897a6f772SChristian Gromm 	tmp_cap = mdev->cap;
101997a6f772SChristian Gromm 	for (i = 0; i < num_endpoints; i++) {
102097a6f772SChristian Gromm 		ep_desc = &usb_iface_desc->endpoint[i].desc;
102197a6f772SChristian Gromm 		mdev->ep_address[i] = ep_desc->bEndpointAddress;
102297a6f772SChristian Gromm 		mdev->padding_active[i] = false;
102397a6f772SChristian Gromm 		mdev->is_channel_healthy[i] = true;
102497a6f772SChristian Gromm 
102597a6f772SChristian Gromm 		snprintf(&mdev->suffix[i][0], MAX_SUFFIX_LEN, "ep%02x",
102697a6f772SChristian Gromm 			 mdev->ep_address[i]);
102797a6f772SChristian Gromm 
102897a6f772SChristian Gromm 		tmp_cap->name_suffix = &mdev->suffix[i][0];
102997a6f772SChristian Gromm 		tmp_cap->buffer_size_packet = MAX_BUF_SIZE;
103097a6f772SChristian Gromm 		tmp_cap->buffer_size_streaming = MAX_BUF_SIZE;
103197a6f772SChristian Gromm 		tmp_cap->num_buffers_packet = BUF_CHAIN_SIZE;
103297a6f772SChristian Gromm 		tmp_cap->num_buffers_streaming = BUF_CHAIN_SIZE;
103397a6f772SChristian Gromm 		tmp_cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
103497a6f772SChristian Gromm 				     MOST_CH_ISOC | MOST_CH_SYNC;
103597a6f772SChristian Gromm 		if (usb_endpoint_dir_in(ep_desc))
103697a6f772SChristian Gromm 			tmp_cap->direction = MOST_CH_RX;
103797a6f772SChristian Gromm 		else
103897a6f772SChristian Gromm 			tmp_cap->direction = MOST_CH_TX;
103997a6f772SChristian Gromm 		tmp_cap++;
104097a6f772SChristian Gromm 		init_usb_anchor(&mdev->busy_urbs[i]);
104197a6f772SChristian Gromm 		spin_lock_init(&mdev->channel_lock[i]);
104297a6f772SChristian Gromm 	}
104397a6f772SChristian Gromm 	dev_dbg(dev, "claimed gadget: Vendor=%4.4x ProdID=%4.4x Bus=%02x Device=%02x\n",
104497a6f772SChristian Gromm 		le16_to_cpu(usb_dev->descriptor.idVendor),
104597a6f772SChristian Gromm 		le16_to_cpu(usb_dev->descriptor.idProduct),
104697a6f772SChristian Gromm 		usb_dev->bus->busnum,
104797a6f772SChristian Gromm 		usb_dev->devnum);
104897a6f772SChristian Gromm 
104997a6f772SChristian Gromm 	dev_dbg(dev, "device path: /sys/bus/usb/devices/%d-%s:%d.%d\n",
105097a6f772SChristian Gromm 		usb_dev->bus->busnum,
105197a6f772SChristian Gromm 		usb_dev->devpath,
105297a6f772SChristian Gromm 		usb_dev->config->desc.bConfigurationValue,
105397a6f772SChristian Gromm 		usb_iface_desc->desc.bInterfaceNumber);
105497a6f772SChristian Gromm 
105597a6f772SChristian Gromm 	ret = most_register_interface(&mdev->iface);
105697a6f772SChristian Gromm 	if (ret)
105797a6f772SChristian Gromm 		goto err_free_busy_urbs;
105897a6f772SChristian Gromm 
105997a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
106097a6f772SChristian Gromm 	if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 ||
106197a6f772SChristian Gromm 	    le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81119 ||
106297a6f772SChristian Gromm 	    le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81210) {
106397a6f772SChristian Gromm 		mdev->dci = kzalloc(sizeof(*mdev->dci), GFP_KERNEL);
106497a6f772SChristian Gromm 		if (!mdev->dci) {
106597a6f772SChristian Gromm 			mutex_unlock(&mdev->io_mutex);
106697a6f772SChristian Gromm 			most_deregister_interface(&mdev->iface);
106797a6f772SChristian Gromm 			ret = -ENOMEM;
106897a6f772SChristian Gromm 			goto err_free_busy_urbs;
106997a6f772SChristian Gromm 		}
107097a6f772SChristian Gromm 
107197a6f772SChristian Gromm 		mdev->dci->dev.init_name = "dci";
107297a6f772SChristian Gromm 		mdev->dci->dev.parent = get_device(mdev->iface.dev);
107397a6f772SChristian Gromm 		mdev->dci->dev.groups = dci_groups;
107497a6f772SChristian Gromm 		mdev->dci->dev.release = release_dci;
107597a6f772SChristian Gromm 		if (device_register(&mdev->dci->dev)) {
107697a6f772SChristian Gromm 			mutex_unlock(&mdev->io_mutex);
107797a6f772SChristian Gromm 			most_deregister_interface(&mdev->iface);
107897a6f772SChristian Gromm 			ret = -ENOMEM;
107997a6f772SChristian Gromm 			goto err_free_dci;
108097a6f772SChristian Gromm 		}
108197a6f772SChristian Gromm 		mdev->dci->usb_device = mdev->usb_device;
108297a6f772SChristian Gromm 	}
108397a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
108497a6f772SChristian Gromm 	return 0;
108597a6f772SChristian Gromm err_free_dci:
108697a6f772SChristian Gromm 	put_device(&mdev->dci->dev);
108797a6f772SChristian Gromm err_free_busy_urbs:
108897a6f772SChristian Gromm 	kfree(mdev->busy_urbs);
108997a6f772SChristian Gromm err_free_ep_address:
109097a6f772SChristian Gromm 	kfree(mdev->ep_address);
109197a6f772SChristian Gromm err_free_cap:
109297a6f772SChristian Gromm 	kfree(mdev->cap);
109397a6f772SChristian Gromm err_free_conf:
109497a6f772SChristian Gromm 	kfree(mdev->conf);
109597a6f772SChristian Gromm err_free_mdev:
109697a6f772SChristian Gromm 	put_device(&mdev->dev);
109797a6f772SChristian Gromm 	return ret;
109897a6f772SChristian Gromm }
109997a6f772SChristian Gromm 
110097a6f772SChristian Gromm /**
110197a6f772SChristian Gromm  * hdm_disconnect - disconnect function of USB device driver
110297a6f772SChristian Gromm  * @interface: Interface of the attached USB device
110397a6f772SChristian Gromm  *
110497a6f772SChristian Gromm  * This deregisters the interface with the core, removes the kernel timer
110597a6f772SChristian Gromm  * and frees resources.
110697a6f772SChristian Gromm  *
110797a6f772SChristian Gromm  * Context: hub kernel thread
110897a6f772SChristian Gromm  */
hdm_disconnect(struct usb_interface * interface)110997a6f772SChristian Gromm static void hdm_disconnect(struct usb_interface *interface)
111097a6f772SChristian Gromm {
111197a6f772SChristian Gromm 	struct most_dev *mdev = usb_get_intfdata(interface);
111297a6f772SChristian Gromm 
111397a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
111497a6f772SChristian Gromm 	usb_set_intfdata(interface, NULL);
111597a6f772SChristian Gromm 	mdev->usb_device = NULL;
111697a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
111797a6f772SChristian Gromm 
111897a6f772SChristian Gromm 	del_timer_sync(&mdev->link_stat_timer);
111997a6f772SChristian Gromm 	cancel_work_sync(&mdev->poll_work_obj);
112097a6f772SChristian Gromm 
112197a6f772SChristian Gromm 	if (mdev->dci)
112297a6f772SChristian Gromm 		device_unregister(&mdev->dci->dev);
112397a6f772SChristian Gromm 	most_deregister_interface(&mdev->iface);
112497a6f772SChristian Gromm 
112597a6f772SChristian Gromm 	kfree(mdev->busy_urbs);
112697a6f772SChristian Gromm 	kfree(mdev->cap);
112797a6f772SChristian Gromm 	kfree(mdev->conf);
112897a6f772SChristian Gromm 	kfree(mdev->ep_address);
112997a6f772SChristian Gromm 	put_device(&mdev->dci->dev);
113097a6f772SChristian Gromm 	put_device(&mdev->dev);
113197a6f772SChristian Gromm }
113297a6f772SChristian Gromm 
hdm_suspend(struct usb_interface * interface,pm_message_t message)113397a6f772SChristian Gromm static int hdm_suspend(struct usb_interface *interface, pm_message_t message)
113497a6f772SChristian Gromm {
113597a6f772SChristian Gromm 	struct most_dev *mdev = usb_get_intfdata(interface);
113697a6f772SChristian Gromm 	int i;
113797a6f772SChristian Gromm 
113897a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
113997a6f772SChristian Gromm 	for (i = 0; i < mdev->iface.num_channels; i++) {
114097a6f772SChristian Gromm 		most_stop_enqueue(&mdev->iface, i);
114197a6f772SChristian Gromm 		usb_kill_anchored_urbs(&mdev->busy_urbs[i]);
114297a6f772SChristian Gromm 	}
114397a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
114497a6f772SChristian Gromm 	return 0;
114597a6f772SChristian Gromm }
114697a6f772SChristian Gromm 
hdm_resume(struct usb_interface * interface)114797a6f772SChristian Gromm static int hdm_resume(struct usb_interface *interface)
114897a6f772SChristian Gromm {
114997a6f772SChristian Gromm 	struct most_dev *mdev = usb_get_intfdata(interface);
115097a6f772SChristian Gromm 	int i;
115197a6f772SChristian Gromm 
115297a6f772SChristian Gromm 	mutex_lock(&mdev->io_mutex);
115397a6f772SChristian Gromm 	for (i = 0; i < mdev->iface.num_channels; i++)
115497a6f772SChristian Gromm 		most_resume_enqueue(&mdev->iface, i);
115597a6f772SChristian Gromm 	mutex_unlock(&mdev->io_mutex);
115697a6f772SChristian Gromm 	return 0;
115797a6f772SChristian Gromm }
115897a6f772SChristian Gromm 
115997a6f772SChristian Gromm static struct usb_driver hdm_usb = {
116097a6f772SChristian Gromm 	.name = "hdm_usb",
116197a6f772SChristian Gromm 	.id_table = usbid,
116297a6f772SChristian Gromm 	.probe = hdm_probe,
116397a6f772SChristian Gromm 	.disconnect = hdm_disconnect,
116497a6f772SChristian Gromm 	.resume = hdm_resume,
116597a6f772SChristian Gromm 	.suspend = hdm_suspend,
116697a6f772SChristian Gromm };
116797a6f772SChristian Gromm 
116897a6f772SChristian Gromm module_usb_driver(hdm_usb);
116997a6f772SChristian Gromm MODULE_LICENSE("GPL");
117097a6f772SChristian Gromm MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
117197a6f772SChristian Gromm MODULE_DESCRIPTION("HDM_4_USB");
1172