18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * Copyright (c) 2014 Redpine Signals Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <net/rsi_91x.h> 218c2ecf20Sopenharmony_ci#include "rsi_usb.h" 228c2ecf20Sopenharmony_ci#include "rsi_hal.h" 238c2ecf20Sopenharmony_ci#include "rsi_coex.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Default operating mode is wlan STA + BT */ 268c2ecf20Sopenharmony_cistatic u16 dev_oper_mode = DEV_OPMODE_STA_BT_DUAL; 278c2ecf20Sopenharmony_cimodule_param(dev_oper_mode, ushort, 0444); 288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev_oper_mode, DEV_OPMODE_PARAM_DESC); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t flags); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * rsi_usb_card_write() - This function writes to the USB Card. 348c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 358c2ecf20Sopenharmony_ci * @buf: Pointer to the buffer from where the data has to be taken. 368c2ecf20Sopenharmony_ci * @len: Length to be written. 378c2ecf20Sopenharmony_ci * @endpoint: Type of endpoint. 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * Return: status: 0 on success, a negative error code on failure. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_cistatic int rsi_usb_card_write(struct rsi_hw *adapter, 428c2ecf20Sopenharmony_ci u8 *buf, 438c2ecf20Sopenharmony_ci u16 len, 448c2ecf20Sopenharmony_ci u8 endpoint) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 478c2ecf20Sopenharmony_ci int status; 488c2ecf20Sopenharmony_ci u8 *seg = dev->tx_buffer; 498c2ecf20Sopenharmony_ci int transfer; 508c2ecf20Sopenharmony_ci int ep = dev->bulkout_endpoint_addr[endpoint - 1]; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci memset(seg, 0, len + RSI_USB_TX_HEAD_ROOM); 538c2ecf20Sopenharmony_ci memcpy(seg + RSI_USB_TX_HEAD_ROOM, buf, len); 548c2ecf20Sopenharmony_ci len += RSI_USB_TX_HEAD_ROOM; 558c2ecf20Sopenharmony_ci transfer = len; 568c2ecf20Sopenharmony_ci status = usb_bulk_msg(dev->usbdev, 578c2ecf20Sopenharmony_ci usb_sndbulkpipe(dev->usbdev, ep), 588c2ecf20Sopenharmony_ci (void *)seg, 598c2ecf20Sopenharmony_ci (int)len, 608c2ecf20Sopenharmony_ci &transfer, 618c2ecf20Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (status < 0) { 648c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, 658c2ecf20Sopenharmony_ci "Card write failed with error code :%10d\n", status); 668c2ecf20Sopenharmony_ci dev->write_fail = 1; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci return status; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/** 728c2ecf20Sopenharmony_ci * rsi_write_multiple() - This function writes multiple bytes of information 738c2ecf20Sopenharmony_ci * to the USB card. 748c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 758c2ecf20Sopenharmony_ci * @addr: Address of the register. 768c2ecf20Sopenharmony_ci * @data: Pointer to the data that has to be written. 778c2ecf20Sopenharmony_ci * @count: Number of multiple bytes to be written. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error code on failure. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic int rsi_write_multiple(struct rsi_hw *adapter, 828c2ecf20Sopenharmony_ci u8 endpoint, 838c2ecf20Sopenharmony_ci u8 *data, 848c2ecf20Sopenharmony_ci u32 count) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!adapter) 898c2ecf20Sopenharmony_ci return -ENODEV; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (endpoint == 0) 928c2ecf20Sopenharmony_ci return -EINVAL; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 958c2ecf20Sopenharmony_ci if (dev->write_fail) 968c2ecf20Sopenharmony_ci return -ENETDOWN; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return rsi_usb_card_write(adapter, data, count, endpoint); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/** 1028c2ecf20Sopenharmony_ci * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk 1038c2ecf20Sopenharmony_ci * endpoints to the device. 1048c2ecf20Sopenharmony_ci * @interface: Pointer to the USB interface structure. 1058c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * Return: ret_val: 0 on success, -ENOMEM on failure. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, 1108c2ecf20Sopenharmony_ci struct rsi_hw *adapter) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 1138c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 1148c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 1158c2ecf20Sopenharmony_ci __le16 buffer_size; 1168c2ecf20Sopenharmony_ci int ii, bin_found = 0, bout_found = 0; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci iface_desc = interface->cur_altsetting; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { 1218c2ecf20Sopenharmony_ci endpoint = &(iface_desc->endpoint[ii].desc); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!dev->bulkin_endpoint_addr[bin_found] && 1248c2ecf20Sopenharmony_ci (endpoint->bEndpointAddress & USB_DIR_IN) && 1258c2ecf20Sopenharmony_ci ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 1268c2ecf20Sopenharmony_ci USB_ENDPOINT_XFER_BULK)) { 1278c2ecf20Sopenharmony_ci buffer_size = endpoint->wMaxPacketSize; 1288c2ecf20Sopenharmony_ci dev->bulkin_size[bin_found] = buffer_size; 1298c2ecf20Sopenharmony_ci dev->bulkin_endpoint_addr[bin_found] = 1308c2ecf20Sopenharmony_ci endpoint->bEndpointAddress; 1318c2ecf20Sopenharmony_ci bin_found++; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!dev->bulkout_endpoint_addr[bout_found] && 1358c2ecf20Sopenharmony_ci !(endpoint->bEndpointAddress & USB_DIR_IN) && 1368c2ecf20Sopenharmony_ci ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 1378c2ecf20Sopenharmony_ci USB_ENDPOINT_XFER_BULK)) { 1388c2ecf20Sopenharmony_ci buffer_size = endpoint->wMaxPacketSize; 1398c2ecf20Sopenharmony_ci dev->bulkout_endpoint_addr[bout_found] = 1408c2ecf20Sopenharmony_ci endpoint->bEndpointAddress; 1418c2ecf20Sopenharmony_ci dev->bulkout_size[bout_found] = buffer_size; 1428c2ecf20Sopenharmony_ci bout_found++; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (bin_found >= MAX_BULK_EP || bout_found >= MAX_BULK_EP) 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!(dev->bulkin_endpoint_addr[0] && dev->bulkout_endpoint_addr[0])) { 1508c2ecf20Sopenharmony_ci dev_err(&interface->dev, "missing wlan bulk endpoints\n"); 1518c2ecf20Sopenharmony_ci return -EINVAL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (adapter->priv->coex_mode > 1) { 1558c2ecf20Sopenharmony_ci if (!dev->bulkin_endpoint_addr[1]) { 1568c2ecf20Sopenharmony_ci dev_err(&interface->dev, "missing bt bulk-in endpoint\n"); 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define RSI_USB_REQ_OUT (USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE) 1658c2ecf20Sopenharmony_ci#define RSI_USB_REQ_IN (USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE) 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* rsi_usb_reg_read() - This function reads data from given register address. 1688c2ecf20Sopenharmony_ci * @usbdev: Pointer to the usb_device structure. 1698c2ecf20Sopenharmony_ci * @reg: Address of the register to be read. 1708c2ecf20Sopenharmony_ci * @value: Value to be read. 1718c2ecf20Sopenharmony_ci * @len: length of data to be read. 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * Return: status: 0 on success, a negative error code on failure. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistatic int rsi_usb_reg_read(struct usb_device *usbdev, 1768c2ecf20Sopenharmony_ci u32 reg, 1778c2ecf20Sopenharmony_ci u16 *value, 1788c2ecf20Sopenharmony_ci u16 len) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci u8 *buf; 1818c2ecf20Sopenharmony_ci int status = -ENOMEM; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (len > RSI_USB_CTRL_BUF_SIZE) 1848c2ecf20Sopenharmony_ci return -EINVAL; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); 1878c2ecf20Sopenharmony_ci if (!buf) 1888c2ecf20Sopenharmony_ci return status; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci status = usb_control_msg(usbdev, 1918c2ecf20Sopenharmony_ci usb_rcvctrlpipe(usbdev, 0), 1928c2ecf20Sopenharmony_ci USB_VENDOR_REGISTER_READ, 1938c2ecf20Sopenharmony_ci RSI_USB_REQ_IN, 1948c2ecf20Sopenharmony_ci ((reg & 0xffff0000) >> 16), (reg & 0xffff), 1958c2ecf20Sopenharmony_ci (void *)buf, 1968c2ecf20Sopenharmony_ci len, 1978c2ecf20Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci *value = (buf[0] | (buf[1] << 8)); 2008c2ecf20Sopenharmony_ci if (status < 0) { 2018c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, 2028c2ecf20Sopenharmony_ci "%s: Reg read failed with error code :%d\n", 2038c2ecf20Sopenharmony_ci __func__, status); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci kfree(buf); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return status; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/** 2118c2ecf20Sopenharmony_ci * rsi_usb_reg_write() - This function writes the given data into the given 2128c2ecf20Sopenharmony_ci * register address. 2138c2ecf20Sopenharmony_ci * @usbdev: Pointer to the usb_device structure. 2148c2ecf20Sopenharmony_ci * @reg: Address of the register. 2158c2ecf20Sopenharmony_ci * @value: Value to write. 2168c2ecf20Sopenharmony_ci * @len: Length of data to be written. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * Return: status: 0 on success, a negative error code on failure. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic int rsi_usb_reg_write(struct usb_device *usbdev, 2218c2ecf20Sopenharmony_ci u32 reg, 2228c2ecf20Sopenharmony_ci u32 value, 2238c2ecf20Sopenharmony_ci u16 len) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci u8 *usb_reg_buf; 2268c2ecf20Sopenharmony_ci int status = -ENOMEM; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (len > RSI_USB_CTRL_BUF_SIZE) 2298c2ecf20Sopenharmony_ci return -EINVAL; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci usb_reg_buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); 2328c2ecf20Sopenharmony_ci if (!usb_reg_buf) 2338c2ecf20Sopenharmony_ci return status; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci usb_reg_buf[0] = (cpu_to_le32(value) & 0x00ff); 2368c2ecf20Sopenharmony_ci usb_reg_buf[1] = (cpu_to_le32(value) & 0xff00) >> 8; 2378c2ecf20Sopenharmony_ci usb_reg_buf[2] = (cpu_to_le32(value) & 0x00ff0000) >> 16; 2388c2ecf20Sopenharmony_ci usb_reg_buf[3] = (cpu_to_le32(value) & 0xff000000) >> 24; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci status = usb_control_msg(usbdev, 2418c2ecf20Sopenharmony_ci usb_sndctrlpipe(usbdev, 0), 2428c2ecf20Sopenharmony_ci USB_VENDOR_REGISTER_WRITE, 2438c2ecf20Sopenharmony_ci RSI_USB_REQ_OUT, 2448c2ecf20Sopenharmony_ci ((cpu_to_le32(reg) & 0xffff0000) >> 16), 2458c2ecf20Sopenharmony_ci (cpu_to_le32(reg) & 0xffff), 2468c2ecf20Sopenharmony_ci (void *)usb_reg_buf, 2478c2ecf20Sopenharmony_ci len, 2488c2ecf20Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 2498c2ecf20Sopenharmony_ci if (status < 0) { 2508c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, 2518c2ecf20Sopenharmony_ci "%s: Reg write failed with error code :%d\n", 2528c2ecf20Sopenharmony_ci __func__, status); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci kfree(usb_reg_buf); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return status; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/** 2608c2ecf20Sopenharmony_ci * rsi_rx_done_handler() - This function is called when a packet is received 2618c2ecf20Sopenharmony_ci * from USB stack. This is callback to receive done. 2628c2ecf20Sopenharmony_ci * @urb: Received URB. 2638c2ecf20Sopenharmony_ci * 2648c2ecf20Sopenharmony_ci * Return: None. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic void rsi_rx_done_handler(struct urb *urb) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct rx_usb_ctrl_block *rx_cb = urb->context; 2698c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)rx_cb->data; 2708c2ecf20Sopenharmony_ci int status = -EINVAL; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!rx_cb->rx_skb) 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (urb->status) { 2768c2ecf20Sopenharmony_ci dev_kfree_skb(rx_cb->rx_skb); 2778c2ecf20Sopenharmony_ci rx_cb->rx_skb = NULL; 2788c2ecf20Sopenharmony_ci return; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (urb->actual_length <= 0 || 2828c2ecf20Sopenharmony_ci urb->actual_length > rx_cb->rx_skb->len) { 2838c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "%s: Invalid packet length = %d\n", 2848c2ecf20Sopenharmony_ci __func__, urb->actual_length); 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci if (skb_queue_len(&dev->rx_q) >= RSI_MAX_RX_PKTS) { 2888c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "Max RX packets reached\n"); 2898c2ecf20Sopenharmony_ci goto out; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci skb_trim(rx_cb->rx_skb, urb->actual_length); 2928c2ecf20Sopenharmony_ci skb_queue_tail(&dev->rx_q, rx_cb->rx_skb); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci rsi_set_event(&dev->rx_thread.event); 2958c2ecf20Sopenharmony_ci status = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciout: 2988c2ecf20Sopenharmony_ci if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num, GFP_ATOMIC)) 2998c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (status) { 3028c2ecf20Sopenharmony_ci dev_kfree_skb(rx_cb->rx_skb); 3038c2ecf20Sopenharmony_ci rx_cb->rx_skb = NULL; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 3108c2ecf20Sopenharmony_ci struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; 3118c2ecf20Sopenharmony_ci struct urb *urb = rx_cb->rx_urb; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci usb_kill_urb(urb); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/** 3178c2ecf20Sopenharmony_ci * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. 3188c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 3198c2ecf20Sopenharmony_ci * 3208c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error code on failure. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_cistatic int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t mem_flags) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 3258c2ecf20Sopenharmony_ci struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; 3268c2ecf20Sopenharmony_ci struct urb *urb = rx_cb->rx_urb; 3278c2ecf20Sopenharmony_ci int status; 3288c2ecf20Sopenharmony_ci struct sk_buff *skb; 3298c2ecf20Sopenharmony_ci u8 dword_align_bytes = 0; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci skb = dev_alloc_skb(RSI_MAX_RX_USB_PKT_SIZE); 3328c2ecf20Sopenharmony_ci if (!skb) 3338c2ecf20Sopenharmony_ci return -ENOMEM; 3348c2ecf20Sopenharmony_ci skb_reserve(skb, MAX_DWORD_ALIGN_BYTES); 3358c2ecf20Sopenharmony_ci skb_put(skb, RSI_MAX_RX_USB_PKT_SIZE - MAX_DWORD_ALIGN_BYTES); 3368c2ecf20Sopenharmony_ci dword_align_bytes = (unsigned long)skb->data & 0x3f; 3378c2ecf20Sopenharmony_ci if (dword_align_bytes > 0) 3388c2ecf20Sopenharmony_ci skb_push(skb, dword_align_bytes); 3398c2ecf20Sopenharmony_ci urb->transfer_buffer = skb->data; 3408c2ecf20Sopenharmony_ci rx_cb->rx_skb = skb; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, 3438c2ecf20Sopenharmony_ci dev->usbdev, 3448c2ecf20Sopenharmony_ci usb_rcvbulkpipe(dev->usbdev, 3458c2ecf20Sopenharmony_ci dev->bulkin_endpoint_addr[ep_num - 1]), 3468c2ecf20Sopenharmony_ci urb->transfer_buffer, 3478c2ecf20Sopenharmony_ci skb->len, 3488c2ecf20Sopenharmony_ci rsi_rx_done_handler, 3498c2ecf20Sopenharmony_ci rx_cb); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci status = usb_submit_urb(urb, mem_flags); 3528c2ecf20Sopenharmony_ci if (status) { 3538c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); 3548c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci return status; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int rsi_usb_read_register_multiple(struct rsi_hw *adapter, u32 addr, 3618c2ecf20Sopenharmony_ci u8 *data, u16 count) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 3648c2ecf20Sopenharmony_ci u8 *buf; 3658c2ecf20Sopenharmony_ci u16 transfer; 3668c2ecf20Sopenharmony_ci int status; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!addr) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL); 3728c2ecf20Sopenharmony_ci if (!buf) 3738c2ecf20Sopenharmony_ci return -ENOMEM; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci while (count) { 3768c2ecf20Sopenharmony_ci transfer = min_t(u16, count, RSI_USB_BUF_SIZE); 3778c2ecf20Sopenharmony_ci status = usb_control_msg(dev->usbdev, 3788c2ecf20Sopenharmony_ci usb_rcvctrlpipe(dev->usbdev, 0), 3798c2ecf20Sopenharmony_ci USB_VENDOR_REGISTER_READ, 3808c2ecf20Sopenharmony_ci RSI_USB_REQ_IN, 3818c2ecf20Sopenharmony_ci ((addr & 0xffff0000) >> 16), 3828c2ecf20Sopenharmony_ci (addr & 0xffff), (void *)buf, 3838c2ecf20Sopenharmony_ci transfer, USB_CTRL_GET_TIMEOUT); 3848c2ecf20Sopenharmony_ci if (status < 0) { 3858c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, 3868c2ecf20Sopenharmony_ci "Reg read failed with error code :%d\n", 3878c2ecf20Sopenharmony_ci status); 3888c2ecf20Sopenharmony_ci kfree(buf); 3898c2ecf20Sopenharmony_ci return status; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci memcpy(data, buf, transfer); 3928c2ecf20Sopenharmony_ci count -= transfer; 3938c2ecf20Sopenharmony_ci data += transfer; 3948c2ecf20Sopenharmony_ci addr += transfer; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci kfree(buf); 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci/** 4018c2ecf20Sopenharmony_ci * rsi_usb_write_register_multiple() - This function writes multiple bytes of 4028c2ecf20Sopenharmony_ci * information to multiple registers. 4038c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 4048c2ecf20Sopenharmony_ci * @addr: Address of the register. 4058c2ecf20Sopenharmony_ci * @data: Pointer to the data that has to be written. 4068c2ecf20Sopenharmony_ci * @count: Number of multiple bytes to be written on to the registers. 4078c2ecf20Sopenharmony_ci * 4088c2ecf20Sopenharmony_ci * Return: status: 0 on success, a negative error code on failure. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistatic int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr, 4118c2ecf20Sopenharmony_ci u8 *data, u16 count) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 4148c2ecf20Sopenharmony_ci u8 *buf; 4158c2ecf20Sopenharmony_ci u16 transfer; 4168c2ecf20Sopenharmony_ci int status = 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL); 4198c2ecf20Sopenharmony_ci if (!buf) 4208c2ecf20Sopenharmony_ci return -ENOMEM; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci while (count) { 4238c2ecf20Sopenharmony_ci transfer = min_t(u16, count, RSI_USB_BUF_SIZE); 4248c2ecf20Sopenharmony_ci memcpy(buf, data, transfer); 4258c2ecf20Sopenharmony_ci status = usb_control_msg(dev->usbdev, 4268c2ecf20Sopenharmony_ci usb_sndctrlpipe(dev->usbdev, 0), 4278c2ecf20Sopenharmony_ci USB_VENDOR_REGISTER_WRITE, 4288c2ecf20Sopenharmony_ci RSI_USB_REQ_OUT, 4298c2ecf20Sopenharmony_ci ((addr & 0xffff0000) >> 16), 4308c2ecf20Sopenharmony_ci (addr & 0xffff), 4318c2ecf20Sopenharmony_ci (void *)buf, 4328c2ecf20Sopenharmony_ci transfer, 4338c2ecf20Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 4348c2ecf20Sopenharmony_ci if (status < 0) { 4358c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, 4368c2ecf20Sopenharmony_ci "Reg write failed with error code :%d\n", 4378c2ecf20Sopenharmony_ci status); 4388c2ecf20Sopenharmony_ci kfree(buf); 4398c2ecf20Sopenharmony_ci return status; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci count -= transfer; 4428c2ecf20Sopenharmony_ci data += transfer; 4438c2ecf20Sopenharmony_ci addr += transfer; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci kfree(buf); 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/** 4518c2ecf20Sopenharmony_ci *rsi_usb_host_intf_write_pkt() - This function writes the packet to the 4528c2ecf20Sopenharmony_ci * USB card. 4538c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 4548c2ecf20Sopenharmony_ci * @pkt: Pointer to the data to be written on to the card. 4558c2ecf20Sopenharmony_ci * @len: Length of the data to be written on to the card. 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error code on failure. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_cistatic int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, 4608c2ecf20Sopenharmony_ci u8 *pkt, 4618c2ecf20Sopenharmony_ci u32 len) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci u32 queueno = ((pkt[1] >> 4) & 0x7); 4648c2ecf20Sopenharmony_ci u8 endpoint; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci endpoint = ((queueno == RSI_WIFI_MGMT_Q || queueno == RSI_WIFI_DATA_Q || 4678c2ecf20Sopenharmony_ci queueno == RSI_COEX_Q) ? WLAN_EP : BT_EP); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return rsi_write_multiple(adapter, 4708c2ecf20Sopenharmony_ci endpoint, 4718c2ecf20Sopenharmony_ci (u8 *)pkt, 4728c2ecf20Sopenharmony_ci len); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int rsi_usb_master_reg_read(struct rsi_hw *adapter, u32 reg, 4768c2ecf20Sopenharmony_ci u32 *value, u16 len) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct usb_device *usbdev = 4798c2ecf20Sopenharmony_ci ((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev; 4808c2ecf20Sopenharmony_ci u16 temp; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = rsi_usb_reg_read(usbdev, reg, &temp, len); 4848c2ecf20Sopenharmony_ci if (ret < 0) 4858c2ecf20Sopenharmony_ci return ret; 4868c2ecf20Sopenharmony_ci *value = temp; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int rsi_usb_master_reg_write(struct rsi_hw *adapter, 4928c2ecf20Sopenharmony_ci unsigned long reg, 4938c2ecf20Sopenharmony_ci unsigned long value, u16 len) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct usb_device *usbdev = 4968c2ecf20Sopenharmony_ci ((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return rsi_usb_reg_write(usbdev, reg, value, len); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic int rsi_usb_load_data_master_write(struct rsi_hw *adapter, 5028c2ecf20Sopenharmony_ci u32 base_address, 5038c2ecf20Sopenharmony_ci u32 instructions_sz, u16 block_size, 5048c2ecf20Sopenharmony_ci u8 *ta_firmware) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci u16 num_blocks; 5078c2ecf20Sopenharmony_ci u32 cur_indx, i; 5088c2ecf20Sopenharmony_ci u8 temp_buf[256]; 5098c2ecf20Sopenharmony_ci int status; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci num_blocks = instructions_sz / block_size; 5128c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci for (cur_indx = 0, i = 0; i < num_blocks; i++, cur_indx += block_size) { 5158c2ecf20Sopenharmony_ci memcpy(temp_buf, ta_firmware + cur_indx, block_size); 5168c2ecf20Sopenharmony_ci status = rsi_usb_write_register_multiple(adapter, base_address, 5178c2ecf20Sopenharmony_ci (u8 *)(temp_buf), 5188c2ecf20Sopenharmony_ci block_size); 5198c2ecf20Sopenharmony_ci if (status < 0) 5208c2ecf20Sopenharmony_ci return status; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, i); 5238c2ecf20Sopenharmony_ci base_address += block_size; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (instructions_sz % block_size) { 5278c2ecf20Sopenharmony_ci memset(temp_buf, 0, block_size); 5288c2ecf20Sopenharmony_ci memcpy(temp_buf, ta_firmware + cur_indx, 5298c2ecf20Sopenharmony_ci instructions_sz % block_size); 5308c2ecf20Sopenharmony_ci status = rsi_usb_write_register_multiple 5318c2ecf20Sopenharmony_ci (adapter, base_address, 5328c2ecf20Sopenharmony_ci (u8 *)temp_buf, 5338c2ecf20Sopenharmony_ci instructions_sz % block_size); 5348c2ecf20Sopenharmony_ci if (status < 0) 5358c2ecf20Sopenharmony_ci return status; 5368c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, 5378c2ecf20Sopenharmony_ci "Written Last Block in Address 0x%x Successfully\n", 5388c2ecf20Sopenharmony_ci cur_indx); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic struct rsi_host_intf_ops usb_host_intf_ops = { 5448c2ecf20Sopenharmony_ci .write_pkt = rsi_usb_host_intf_write_pkt, 5458c2ecf20Sopenharmony_ci .read_reg_multiple = rsi_usb_read_register_multiple, 5468c2ecf20Sopenharmony_ci .write_reg_multiple = rsi_usb_write_register_multiple, 5478c2ecf20Sopenharmony_ci .master_reg_read = rsi_usb_master_reg_read, 5488c2ecf20Sopenharmony_ci .master_reg_write = rsi_usb_master_reg_write, 5498c2ecf20Sopenharmony_ci .load_data_master_write = rsi_usb_load_data_master_write, 5508c2ecf20Sopenharmony_ci}; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/** 5538c2ecf20Sopenharmony_ci * rsi_deinit_usb_interface() - This function deinitializes the usb interface. 5548c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 5558c2ecf20Sopenharmony_ci * 5568c2ecf20Sopenharmony_ci * Return: None. 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_cistatic void rsi_deinit_usb_interface(struct rsi_hw *adapter) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci rsi_kill_thread(&dev->rx_thread); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci usb_free_urb(dev->rx_cb[0].rx_urb); 5658c2ecf20Sopenharmony_ci if (adapter->priv->coex_mode > 1) 5668c2ecf20Sopenharmony_ci usb_free_urb(dev->rx_cb[1].rx_urb); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci kfree(dev->tx_buffer); 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic int rsi_usb_init_rx(struct rsi_hw *adapter) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 5748c2ecf20Sopenharmony_ci struct rx_usb_ctrl_block *rx_cb; 5758c2ecf20Sopenharmony_ci u8 idx, num_rx_cb; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci num_rx_cb = (adapter->priv->coex_mode > 1 ? 2 : 1); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci for (idx = 0; idx < num_rx_cb; idx++) { 5808c2ecf20Sopenharmony_ci rx_cb = &dev->rx_cb[idx]; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci rx_cb->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 5838c2ecf20Sopenharmony_ci if (!rx_cb->rx_urb) { 5848c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "Failed alloc rx urb[%d]\n", idx); 5858c2ecf20Sopenharmony_ci goto err; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci rx_cb->ep_num = idx + 1; 5888c2ecf20Sopenharmony_ci rx_cb->data = (void *)dev; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci skb_queue_head_init(&dev->rx_q); 5918c2ecf20Sopenharmony_ci rsi_init_event(&dev->rx_thread.event); 5928c2ecf20Sopenharmony_ci if (rsi_create_kthread(adapter->priv, &dev->rx_thread, 5938c2ecf20Sopenharmony_ci rsi_usb_rx_thread, "RX-Thread")) { 5948c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__); 5958c2ecf20Sopenharmony_ci goto err; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci return 0; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cierr: 6018c2ecf20Sopenharmony_ci usb_free_urb(dev->rx_cb[0].rx_urb); 6028c2ecf20Sopenharmony_ci if (adapter->priv->coex_mode > 1) 6038c2ecf20Sopenharmony_ci usb_free_urb(dev->rx_cb[1].rx_urb); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return -1; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/** 6098c2ecf20Sopenharmony_ci * rsi_init_usb_interface() - This function initializes the usb interface. 6108c2ecf20Sopenharmony_ci * @adapter: Pointer to the adapter structure. 6118c2ecf20Sopenharmony_ci * @pfunction: Pointer to USB interface structure. 6128c2ecf20Sopenharmony_ci * 6138c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error code on failure. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_cistatic int rsi_init_usb_interface(struct rsi_hw *adapter, 6168c2ecf20Sopenharmony_ci struct usb_interface *pfunction) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *rsi_dev; 6198c2ecf20Sopenharmony_ci int status; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL); 6228c2ecf20Sopenharmony_ci if (!rsi_dev) 6238c2ecf20Sopenharmony_ci return -ENOMEM; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci adapter->rsi_dev = rsi_dev; 6268c2ecf20Sopenharmony_ci rsi_dev->usbdev = interface_to_usbdev(pfunction); 6278c2ecf20Sopenharmony_ci rsi_dev->priv = (void *)adapter; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) { 6308c2ecf20Sopenharmony_ci status = -EINVAL; 6318c2ecf20Sopenharmony_ci goto fail_eps; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci adapter->device = &pfunction->dev; 6358c2ecf20Sopenharmony_ci usb_set_intfdata(pfunction, adapter); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL); 6388c2ecf20Sopenharmony_ci if (!rsi_dev->tx_buffer) { 6398c2ecf20Sopenharmony_ci status = -ENOMEM; 6408c2ecf20Sopenharmony_ci goto fail_eps; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (rsi_usb_init_rx(adapter)) { 6448c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "Failed to init RX handle\n"); 6458c2ecf20Sopenharmony_ci status = -ENOMEM; 6468c2ecf20Sopenharmony_ci goto fail_rx; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci rsi_dev->tx_blk_size = 252; 6508c2ecf20Sopenharmony_ci adapter->block_size = rsi_dev->tx_blk_size; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* Initializing function callbacks */ 6538c2ecf20Sopenharmony_ci adapter->check_hw_queue_status = rsi_usb_check_queue_status; 6548c2ecf20Sopenharmony_ci adapter->determine_event_timeout = rsi_usb_event_timeout; 6558c2ecf20Sopenharmony_ci adapter->rsi_host_intf = RSI_HOST_INTF_USB; 6568c2ecf20Sopenharmony_ci adapter->host_intf_ops = &usb_host_intf_ops; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci#ifdef CONFIG_RSI_DEBUGFS 6598c2ecf20Sopenharmony_ci /* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */ 6608c2ecf20Sopenharmony_ci adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1); 6618c2ecf20Sopenharmony_ci#endif 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cifail_rx: 6678c2ecf20Sopenharmony_ci kfree(rsi_dev->tx_buffer); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cifail_eps: 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci return status; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int usb_ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data, 6758c2ecf20Sopenharmony_ci u16 len_in_bits) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci int ret; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write 6808c2ecf20Sopenharmony_ci (adapter, RSI_GSPI_DATA_REG1, 6818c2ecf20Sopenharmony_ci ((addr << 6) | ((data >> 16) & 0xffff)), 2); 6828c2ecf20Sopenharmony_ci if (ret < 0) 6838c2ecf20Sopenharmony_ci return ret; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_DATA_REG0, 6868c2ecf20Sopenharmony_ci (data & 0xffff), 2); 6878c2ecf20Sopenharmony_ci if (ret < 0) 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* Initializing GSPI for ULP read/writes */ 6918c2ecf20Sopenharmony_ci rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG0, 6928c2ecf20Sopenharmony_ci RSI_GSPI_CTRL_REG0_VALUE, 2); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG1, 6958c2ecf20Sopenharmony_ci ((len_in_bits - 1) | RSI_GSPI_TRIG), 2); 6968c2ecf20Sopenharmony_ci if (ret < 0) 6978c2ecf20Sopenharmony_ci return ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci msleep(20); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return 0; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int rsi_reset_card(struct rsi_hw *adapter) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci int ret; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "Resetting Card...\n"); 7098c2ecf20Sopenharmony_ci rsi_usb_master_reg_write(adapter, RSI_TA_HOLD_REG, 0xE, 4); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* This msleep will ensure Thread-Arch processor to go to hold 7128c2ecf20Sopenharmony_ci * and any pending dma transfers to rf in device to finish. 7138c2ecf20Sopenharmony_ci */ 7148c2ecf20Sopenharmony_ci msleep(100); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write(adapter, SWBL_REGOUT, 7178c2ecf20Sopenharmony_ci RSI_FW_WDT_DISABLE_REQ, 7188c2ecf20Sopenharmony_ci RSI_COMMON_REG_SIZE); 7198c2ecf20Sopenharmony_ci if (ret < 0) { 7208c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "Disabling firmware watchdog timer failed\n"); 7218c2ecf20Sopenharmony_ci goto fail; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (adapter->device_model != RSI_DEV_9116) { 7258c2ecf20Sopenharmony_ci ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1, 7268c2ecf20Sopenharmony_ci RSI_ULP_WRITE_2, 32); 7278c2ecf20Sopenharmony_ci if (ret < 0) 7288c2ecf20Sopenharmony_ci goto fail; 7298c2ecf20Sopenharmony_ci ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_2, 7308c2ecf20Sopenharmony_ci RSI_ULP_WRITE_0, 32); 7318c2ecf20Sopenharmony_ci if (ret < 0) 7328c2ecf20Sopenharmony_ci goto fail; 7338c2ecf20Sopenharmony_ci ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_1, 7348c2ecf20Sopenharmony_ci RSI_ULP_WRITE_50, 32); 7358c2ecf20Sopenharmony_ci if (ret < 0) 7368c2ecf20Sopenharmony_ci goto fail; 7378c2ecf20Sopenharmony_ci ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_2, 7388c2ecf20Sopenharmony_ci RSI_ULP_WRITE_0, 32); 7398c2ecf20Sopenharmony_ci if (ret < 0) 7408c2ecf20Sopenharmony_ci goto fail; 7418c2ecf20Sopenharmony_ci ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_ENABLE, 7428c2ecf20Sopenharmony_ci RSI_ULP_TIMER_ENABLE, 32); 7438c2ecf20Sopenharmony_ci if (ret < 0) 7448c2ecf20Sopenharmony_ci goto fail; 7458c2ecf20Sopenharmony_ci } else { 7468c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write(adapter, 7478c2ecf20Sopenharmony_ci NWP_WWD_INTERRUPT_TIMER, 7488c2ecf20Sopenharmony_ci NWP_WWD_INT_TIMER_CLKS, 7498c2ecf20Sopenharmony_ci RSI_9116_REG_SIZE); 7508c2ecf20Sopenharmony_ci if (ret < 0) 7518c2ecf20Sopenharmony_ci goto fail; 7528c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write(adapter, 7538c2ecf20Sopenharmony_ci NWP_WWD_SYSTEM_RESET_TIMER, 7548c2ecf20Sopenharmony_ci NWP_WWD_SYS_RESET_TIMER_CLKS, 7558c2ecf20Sopenharmony_ci RSI_9116_REG_SIZE); 7568c2ecf20Sopenharmony_ci if (ret < 0) 7578c2ecf20Sopenharmony_ci goto fail; 7588c2ecf20Sopenharmony_ci ret = rsi_usb_master_reg_write(adapter, 7598c2ecf20Sopenharmony_ci NWP_WWD_MODE_AND_RSTART, 7608c2ecf20Sopenharmony_ci NWP_WWD_TIMER_DISABLE, 7618c2ecf20Sopenharmony_ci RSI_9116_REG_SIZE); 7628c2ecf20Sopenharmony_ci if (ret < 0) 7638c2ecf20Sopenharmony_ci goto fail; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "Reset card done\n"); 7678c2ecf20Sopenharmony_ci return ret; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cifail: 7708c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "Reset card failed\n"); 7718c2ecf20Sopenharmony_ci return ret; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/** 7758c2ecf20Sopenharmony_ci * rsi_probe() - This function is called by kernel when the driver provided 7768c2ecf20Sopenharmony_ci * Vendor and device IDs are matched. All the initialization 7778c2ecf20Sopenharmony_ci * work is done here. 7788c2ecf20Sopenharmony_ci * @pfunction: Pointer to the USB interface structure. 7798c2ecf20Sopenharmony_ci * @id: Pointer to the usb_device_id structure. 7808c2ecf20Sopenharmony_ci * 7818c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error code on failure. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_cistatic int rsi_probe(struct usb_interface *pfunction, 7848c2ecf20Sopenharmony_ci const struct usb_device_id *id) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct rsi_hw *adapter; 7878c2ecf20Sopenharmony_ci struct rsi_91x_usbdev *dev; 7888c2ecf20Sopenharmony_ci u16 fw_status; 7898c2ecf20Sopenharmony_ci int status; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci adapter = rsi_91x_init(dev_oper_mode); 7948c2ecf20Sopenharmony_ci if (!adapter) { 7958c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", 7968c2ecf20Sopenharmony_ci __func__); 7978c2ecf20Sopenharmony_ci return -ENOMEM; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci adapter->rsi_host_intf = RSI_HOST_INTF_USB; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci status = rsi_init_usb_interface(adapter, pfunction); 8028c2ecf20Sopenharmony_ci if (status) { 8038c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n", 8048c2ecf20Sopenharmony_ci __func__); 8058c2ecf20Sopenharmony_ci goto err; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (id->idProduct == RSI_USB_PID_9113) { 8118c2ecf20Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: 9113 module detected\n", __func__); 8128c2ecf20Sopenharmony_ci adapter->device_model = RSI_DEV_9113; 8138c2ecf20Sopenharmony_ci } else if (id->idProduct == RSI_USB_PID_9116) { 8148c2ecf20Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: 9116 module detected\n", __func__); 8158c2ecf20Sopenharmony_ci adapter->device_model = RSI_DEV_9116; 8168c2ecf20Sopenharmony_ci } else { 8178c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Unsupported RSI device id 0x%x\n", 8188c2ecf20Sopenharmony_ci __func__, id->idProduct); 8198c2ecf20Sopenharmony_ci status = -ENODEV; 8208c2ecf20Sopenharmony_ci goto err1; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2); 8268c2ecf20Sopenharmony_ci if (status < 0) 8278c2ecf20Sopenharmony_ci goto err1; 8288c2ecf20Sopenharmony_ci else 8298c2ecf20Sopenharmony_ci fw_status &= 1; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (!fw_status) { 8328c2ecf20Sopenharmony_ci rsi_dbg(INIT_ZONE, "Loading firmware...\n"); 8338c2ecf20Sopenharmony_ci status = rsi_hal_device_init(adapter); 8348c2ecf20Sopenharmony_ci if (status) { 8358c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", 8368c2ecf20Sopenharmony_ci __func__); 8378c2ecf20Sopenharmony_ci goto err1; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__); 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci status = rsi_rx_urb_submit(adapter, WLAN_EP, GFP_KERNEL); 8438c2ecf20Sopenharmony_ci if (status) 8448c2ecf20Sopenharmony_ci goto err1; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (adapter->priv->coex_mode > 1) { 8478c2ecf20Sopenharmony_ci status = rsi_rx_urb_submit(adapter, BT_EP, GFP_KERNEL); 8488c2ecf20Sopenharmony_ci if (status) 8498c2ecf20Sopenharmony_ci goto err_kill_wlan_urb; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci return 0; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cierr_kill_wlan_urb: 8558c2ecf20Sopenharmony_ci rsi_rx_urb_kill(adapter, WLAN_EP); 8568c2ecf20Sopenharmony_cierr1: 8578c2ecf20Sopenharmony_ci rsi_deinit_usb_interface(adapter); 8588c2ecf20Sopenharmony_cierr: 8598c2ecf20Sopenharmony_ci rsi_91x_deinit(adapter); 8608c2ecf20Sopenharmony_ci rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); 8618c2ecf20Sopenharmony_ci return status; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci/** 8658c2ecf20Sopenharmony_ci * rsi_disconnect() - This function performs the reverse of the probe function, 8668c2ecf20Sopenharmony_ci * it deinitialize the driver structure. 8678c2ecf20Sopenharmony_ci * @pfunction: Pointer to the USB interface structure. 8688c2ecf20Sopenharmony_ci * 8698c2ecf20Sopenharmony_ci * Return: None. 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_cistatic void rsi_disconnect(struct usb_interface *pfunction) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct rsi_hw *adapter = usb_get_intfdata(pfunction); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (!adapter) 8768c2ecf20Sopenharmony_ci return; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci rsi_mac80211_detach(adapter); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 && 8818c2ecf20Sopenharmony_ci adapter->priv->bt_adapter) { 8828c2ecf20Sopenharmony_ci rsi_bt_ops.detach(adapter->priv->bt_adapter); 8838c2ecf20Sopenharmony_ci adapter->priv->bt_adapter = NULL; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (adapter->priv->coex_mode > 1) 8878c2ecf20Sopenharmony_ci rsi_rx_urb_kill(adapter, BT_EP); 8888c2ecf20Sopenharmony_ci rsi_rx_urb_kill(adapter, WLAN_EP); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci rsi_reset_card(adapter); 8918c2ecf20Sopenharmony_ci rsi_deinit_usb_interface(adapter); 8928c2ecf20Sopenharmony_ci rsi_91x_deinit(adapter); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__); 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 8988c2ecf20Sopenharmony_cistatic int rsi_suspend(struct usb_interface *intf, pm_message_t message) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci /* Not yet implemented */ 9018c2ecf20Sopenharmony_ci return -ENOSYS; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int rsi_resume(struct usb_interface *intf) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci /* Not yet implemented */ 9078c2ecf20Sopenharmony_ci return -ENOSYS; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci#endif 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic const struct usb_device_id rsi_dev_table[] = { 9128c2ecf20Sopenharmony_ci { USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9113) }, 9138c2ecf20Sopenharmony_ci { USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9116) }, 9148c2ecf20Sopenharmony_ci { /* Blank */}, 9158c2ecf20Sopenharmony_ci}; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic struct usb_driver rsi_driver = { 9188c2ecf20Sopenharmony_ci .name = "RSI-USB WLAN", 9198c2ecf20Sopenharmony_ci .probe = rsi_probe, 9208c2ecf20Sopenharmony_ci .disconnect = rsi_disconnect, 9218c2ecf20Sopenharmony_ci .id_table = rsi_dev_table, 9228c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9238c2ecf20Sopenharmony_ci .suspend = rsi_suspend, 9248c2ecf20Sopenharmony_ci .resume = rsi_resume, 9258c2ecf20Sopenharmony_ci#endif 9268c2ecf20Sopenharmony_ci}; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cimodule_usb_driver(rsi_driver); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Redpine Signals Inc"); 9318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Common USB layer for RSI drivers"); 9328c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("RSI-91x"); 9338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, rsi_dev_table); 9348c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_RSI9113); 9358c2ecf20Sopenharmony_ciMODULE_VERSION("0.1"); 9368c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 937