// SPDX-License-Identifier: GPL-2.0
/*
* usb.c - Hardware dependent module for USB
*
* Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/sysfs.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/uaccess.h>
#include <linux/most.h>
#define USB_MTU 512
#define NO_ISOCHRONOUS_URB 0
#define AV_PACKETS_PER_XACT 2
#define BUF_CHAIN_SIZE 0xFFFF
#define MAX_NUM_ENDPOINTS 30
#define MAX_SUFFIX_LEN 10
#define MAX_STRING_LEN 80
#define MAX_BUF_SIZE 0xFFFF
#define USB_VENDOR_ID_SMSC 0x0424 /* VID: SMSC */
#define USB_DEV_ID_BRDG 0xC001 /* PID: USB Bridge */
#define USB_DEV_ID_OS81118 0xCF18 /* PID: USB OS81118 */
#define USB_DEV_ID_OS81119 0xCF19 /* PID: USB OS81119 */
#define USB_DEV_ID_OS81210 0xCF30 /* PID: USB OS81210 */
/* DRCI Addresses */
#define DRCI_REG_NI_STATE 0x0100
#define DRCI_REG_PACKET_BW 0x0101
#define DRCI_REG_NODE_ADDR 0x0102
#define DRCI_REG_NODE_POS 0x0103
#define DRCI_REG_MEP_FILTER 0x0140
#define DRCI_REG_HASH_TBL0 0x0141
#define DRCI_REG_HASH_TBL1 0x0142
#define DRCI_REG_HASH_TBL2 0x0143
#define DRCI_REG_HASH_TBL3 0x0144
#define DRCI_REG_HW_ADDR_HI 0x0145
#define DRCI_REG_HW_ADDR_MI 0x0146
#define DRCI_REG_HW_ADDR_LO 0x0147
#define DRCI_REG_BASE 0x1100
#define DRCI_COMMAND 0x02
#define DRCI_READ_REQ 0xA0
#define DRCI_WRITE_REQ 0xA1
/**
* struct most_dci_obj - Direct Communication Interface
* @kobj:position in sysfs
* @usb_device: pointer to the usb device
* @reg_addr: register address for arbitrary DCI access
*/
struct most_dci_obj {
struct device dev;
struct usb_device *usb_device;
u16 reg_addr;
};
#define to_dci_obj(p) container_of(p, struct most_dci_obj, dev)
struct most_dev;
struct clear_hold_work {
struct work_struct ws;
struct most_dev *mdev;
unsigned int channel;
int pipe;
};
#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
/**
* struct most_dev - holds all usb interface specific stuff
* @usb_device: pointer to usb device
* @iface: hardware interface
* @cap: channel capabilities
* @conf: channel configuration
* @dci: direct communication interface of hardware
* @ep_address: endpoint address table
* @description: device description
* @suffix: suffix for channel name
* @channel_lock: synchronize channel access
* @padding_active: indicates channel uses padding
* @is_channel_healthy: health status table of each channel
* @busy_urbs: list of anchored items
* @io_mutex: synchronize I/O with disconnect
* @link_stat_timer: timer for link status reports
* @poll_work_obj: work for polling link status
*/
struct most_dev {
struct device dev;
struct usb_device *usb_device;
struct most_interface iface;
struct most_channel_capability *cap;
struct most_channel_config *conf;
struct most_dci_obj *dci;
u8 *ep_address;
char description[MAX_STRING_LEN];
char suffix[MAX_NUM_ENDPOINTS][MAX_SUFFIX_LEN];
spinlock_t channel_lock[MAX_NUM_ENDPOINTS]; /* sync channel access */
bool padding_active[MAX_NUM_ENDPOINTS];
bool is_channel_healthy[MAX_NUM_ENDPOINTS];
struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
struct usb_anchor *busy_urbs;
struct mutex io_mutex;
struct timer_list link_stat_timer;
struct work_struct poll_work_obj;
void (*on_netinfo)(struct most_interface *most_iface,
unsigned char link_state, unsigned char *addrs);
};
#define to_mdev(d) container_of(d, struct most_dev, iface)
#define to_mdev_from_dev(d) container_of(d, struct most_dev, dev)
#define to_mdev_from_work(w) container_of(w, struct most_dev, poll_work_obj)
static void wq_clear_halt(struct work_struct *wq_obj);
static void wq_netinfo(struct work_struct *wq_obj);
/**
* drci_rd_reg - read a DCI register
* @dev: usb device
* @reg: register address
* @buf: buffer to store data
*
* This is reads data from INIC's direct register communication interface
*/
static inline int drci_rd_reg(struct usb_device *dev, u16 reg, u16 *buf)
{
int retval;
__le16 *dma_buf;
u8 req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
if (!dma_buf)
return -ENOMEM;
retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
DRCI_READ_REQ, req_type,
0x0000,
reg, dma_buf, sizeof(*dma_buf),
USB_CTRL_GET_TIMEOUT);
*buf = le16_to_cpu(*dma_buf);
kfree(dma_buf);
if (retval < 0)
return retval;
return 0;
}
/**
* drci_wr_reg - write a DCI register
* @dev: usb device
* @reg: register address
* @data: data to write
*
* This is writes data to INIC's direct register communication interface
*/
static inline int drci_wr_reg(struct usb_device *dev, u16 reg, u16 data)
{
return usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
DRCI_WRITE_REQ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
data,
reg,
NULL,
0,
USB_CTRL_SET_TIMEOUT);
}
static inline int start_sync_ep<