1f21e6c58SLee Jones /* 2dad0d04fSFariya Fatima * Copyright (c) 2014 Redpine Signals Inc. 3dad0d04fSFariya Fatima * 4dad0d04fSFariya Fatima * Permission to use, copy, modify, and/or distribute this software for any 5dad0d04fSFariya Fatima * purpose with or without fee is hereby granted, provided that the above 6dad0d04fSFariya Fatima * copyright notice and this permission notice appear in all copies. 7dad0d04fSFariya Fatima * 8dad0d04fSFariya Fatima * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9dad0d04fSFariya Fatima * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10dad0d04fSFariya Fatima * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11dad0d04fSFariya Fatima * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12dad0d04fSFariya Fatima * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13dad0d04fSFariya Fatima * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14dad0d04fSFariya Fatima * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15dad0d04fSFariya Fatima * 16dad0d04fSFariya Fatima */ 17dad0d04fSFariya Fatima 18dad0d04fSFariya Fatima #include <linux/module.h> 19b9b9f9feSJohan Hovold #include <linux/types.h> 202108df3cSPrameela Rani Garnepudi #include <net/rsi_91x.h> 21dad0d04fSFariya Fatima #include "rsi_usb.h" 22b78e91bcSPrameela Rani Garnepudi #include "rsi_hal.h" 232108df3cSPrameela Rani Garnepudi #include "rsi_coex.h" 24dad0d04fSFariya Fatima 25898b2553SPrameela Rani Garnepudi /* Default operating mode is wlan STA + BT */ 26898b2553SPrameela Rani Garnepudi static u16 dev_oper_mode = DEV_OPMODE_STA_BT_DUAL; 27898b2553SPrameela Rani Garnepudi module_param(dev_oper_mode, ushort, 0444); 28898b2553SPrameela Rani Garnepudi MODULE_PARM_DESC(dev_oper_mode, 29898b2553SPrameela Rani Garnepudi "1[Wi-Fi], 4[BT], 8[BT LE], 5[Wi-Fi STA + BT classic]\n" 30898b2553SPrameela Rani Garnepudi "9[Wi-Fi STA + BT LE], 13[Wi-Fi STA + BT classic + BT LE]\n" 31898b2553SPrameela Rani Garnepudi "6[AP + BT classic], 14[AP + BT classic + BT LE]"); 32898b2553SPrameela Rani Garnepudi 33b9b9f9feSJohan Hovold static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t flags); 34a1854faeSPrameela Rani Garnepudi 35dad0d04fSFariya Fatima /** 36dad0d04fSFariya Fatima * rsi_usb_card_write() - This function writes to the USB Card. 37dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 38dad0d04fSFariya Fatima * @buf: Pointer to the buffer from where the data has to be taken. 39dad0d04fSFariya Fatima * @len: Length to be written. 40dad0d04fSFariya Fatima * @endpoint: Type of endpoint. 41dad0d04fSFariya Fatima * 4250591c60SAlexey Khoroshilov * Return: status: 0 on success, a negative error code on failure. 43dad0d04fSFariya Fatima */ 44dad0d04fSFariya Fatima static int rsi_usb_card_write(struct rsi_hw *adapter, 45ed833be6SKarun Eagalapati u8 *buf, 46dad0d04fSFariya Fatima u16 len, 47dad0d04fSFariya Fatima u8 endpoint) 48dad0d04fSFariya Fatima { 49dad0d04fSFariya Fatima struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 50dad0d04fSFariya Fatima int status; 51ed833be6SKarun Eagalapati u8 *seg = dev->tx_buffer; 52ed833be6SKarun Eagalapati int transfer; 53ed833be6SKarun Eagalapati int ep = dev->bulkout_endpoint_addr[endpoint - 1]; 54dad0d04fSFariya Fatima 55ed833be6SKarun Eagalapati memset(seg, 0, len + RSI_USB_TX_HEAD_ROOM); 56ed833be6SKarun Eagalapati memcpy(seg + RSI_USB_TX_HEAD_ROOM, buf, len); 57ed833be6SKarun Eagalapati len += RSI_USB_TX_HEAD_ROOM; 58ed833be6SKarun Eagalapati transfer = len; 59dad0d04fSFariya Fatima status = usb_bulk_msg(dev->usbdev, 60ed833be6SKarun Eagalapati usb_sndbulkpipe(dev->usbdev, ep), 61ed833be6SKarun Eagalapati (void *)seg, 62ed833be6SKarun Eagalapati (int)len, 63dad0d04fSFariya Fatima &transfer, 64dad0d04fSFariya Fatima HZ * 5); 65dad0d04fSFariya Fatima 66dad0d04fSFariya Fatima if (status < 0) { 67dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, 68dad0d04fSFariya Fatima "Card write failed with error code :%10d\n", status); 69dad0d04fSFariya Fatima dev->write_fail = 1; 70dad0d04fSFariya Fatima } 71dad0d04fSFariya Fatima return status; 72dad0d04fSFariya Fatima } 73dad0d04fSFariya Fatima 74dad0d04fSFariya Fatima /** 75dad0d04fSFariya Fatima * rsi_write_multiple() - This function writes multiple bytes of information 76dad0d04fSFariya Fatima * to the USB card. 77dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 78f21e6c58SLee Jones * @endpoint: Type of endpoint. 79dad0d04fSFariya Fatima * @data: Pointer to the data that has to be written. 80dad0d04fSFariya Fatima * @count: Number of multiple bytes to be written. 81dad0d04fSFariya Fatima * 8250591c60SAlexey Khoroshilov * Return: 0 on success, a negative error code on failure. 83dad0d04fSFariya Fatima */ 84dad0d04fSFariya Fatima static int rsi_write_multiple(struct rsi_hw *adapter, 85dad0d04fSFariya Fatima u8 endpoint, 86dad0d04fSFariya Fatima u8 *data, 87dad0d04fSFariya Fatima u32 count) 88dad0d04fSFariya Fatima { 896508497cSColin Ian King struct rsi_91x_usbdev *dev; 90ed833be6SKarun Eagalapati 91ed833be6SKarun Eagalapati if (!adapter) 92ed833be6SKarun Eagalapati return -ENODEV; 93ed833be6SKarun Eagalapati 94ed833be6SKarun Eagalapati if (endpoint == 0) 95ed833be6SKarun Eagalapati return -EINVAL; 96dad0d04fSFariya Fatima 976508497cSColin Ian King dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 98dad0d04fSFariya Fatima if (dev->write_fail) 99ed833be6SKarun Eagalapati return -ENETDOWN; 100dad0d04fSFariya Fatima 101ed833be6SKarun Eagalapati return rsi_usb_card_write(adapter, data, count, endpoint); 102dad0d04fSFariya Fatima } 103dad0d04fSFariya Fatima 104dad0d04fSFariya Fatima /** 105dad0d04fSFariya Fatima * rsi_find_bulk_in_and_out_endpoints() - This function initializes the bulk 106dad0d04fSFariya Fatima * endpoints to the device. 107dad0d04fSFariya Fatima * @interface: Pointer to the USB interface structure. 108dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 109dad0d04fSFariya Fatima * 110dad0d04fSFariya Fatima * Return: ret_val: 0 on success, -ENOMEM on failure. 111dad0d04fSFariya Fatima */ 112dad0d04fSFariya Fatima static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface, 113dad0d04fSFariya Fatima struct rsi_hw *adapter) 114dad0d04fSFariya Fatima { 115dad0d04fSFariya Fatima struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 116dad0d04fSFariya Fatima struct usb_host_interface *iface_desc; 117dad0d04fSFariya Fatima struct usb_endpoint_descriptor *endpoint; 118dad0d04fSFariya Fatima __le16 buffer_size; 119a4302bffSSiva Rebbagondla int ii, bin_found = 0, bout_found = 0; 120dad0d04fSFariya Fatima 1213139b180SJohan Hovold iface_desc = interface->cur_altsetting; 122dad0d04fSFariya Fatima 123dad0d04fSFariya Fatima for (ii = 0; ii < iface_desc->desc.bNumEndpoints; ++ii) { 124dad0d04fSFariya Fatima endpoint = &(iface_desc->endpoint[ii].desc); 125dad0d04fSFariya Fatima 126a4302bffSSiva Rebbagondla if (!dev->bulkin_endpoint_addr[bin_found] && 127dad0d04fSFariya Fatima (endpoint->bEndpointAddress & USB_DIR_IN) && 128a4302bffSSiva Rebbagondla ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 129dad0d04fSFariya Fatima USB_ENDPOINT_XFER_BULK)) { 130dad0d04fSFariya Fatima buffer_size = endpoint->wMaxPacketSize; 131a4302bffSSiva Rebbagondla dev->bulkin_size[bin_found] = buffer_size; 132a4302bffSSiva Rebbagondla dev->bulkin_endpoint_addr[bin_found] = 133dad0d04fSFariya Fatima endpoint->bEndpointAddress; 134a4302bffSSiva Rebbagondla bin_found++; 135dad0d04fSFariya Fatima } 136dad0d04fSFariya Fatima 137a4302bffSSiva Rebbagondla if (!dev->bulkout_endpoint_addr[bout_found] && 138dad0d04fSFariya Fatima !(endpoint->bEndpointAddress & USB_DIR_IN) && 139dad0d04fSFariya Fatima ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 140dad0d04fSFariya Fatima USB_ENDPOINT_XFER_BULK)) { 141a4302bffSSiva Rebbagondla buffer_size = endpoint->wMaxPacketSize; 142a4302bffSSiva Rebbagondla dev->bulkout_endpoint_addr[bout_found] = 143dad0d04fSFariya Fatima endpoint->bEndpointAddress; 144a4302bffSSiva Rebbagondla dev->bulkout_size[bout_found] = buffer_size; 145a4302bffSSiva Rebbagondla bout_found++; 146dad0d04fSFariya Fatima } 147dad0d04fSFariya Fatima 148a4302bffSSiva Rebbagondla if (bin_found >= MAX_BULK_EP || bout_found >= MAX_BULK_EP) 149dad0d04fSFariya Fatima break; 150dad0d04fSFariya Fatima } 151dad0d04fSFariya Fatima 152960da557SJohan Hovold if (!(dev->bulkin_endpoint_addr[0] && dev->bulkout_endpoint_addr[0])) { 153960da557SJohan Hovold dev_err(&interface->dev, "missing wlan bulk endpoints\n"); 154dad0d04fSFariya Fatima return -EINVAL; 155960da557SJohan Hovold } 156960da557SJohan Hovold 157960da557SJohan Hovold if (adapter->priv->coex_mode > 1) { 158960da557SJohan Hovold if (!dev->bulkin_endpoint_addr[1]) { 159960da557SJohan Hovold dev_err(&interface->dev, "missing bt bulk-in endpoint\n"); 160960da557SJohan Hovold return -EINVAL; 161960da557SJohan Hovold } 162960da557SJohan Hovold } 163dad0d04fSFariya Fatima 164dad0d04fSFariya Fatima return 0; 165dad0d04fSFariya Fatima } 166dad0d04fSFariya Fatima 1674b1fc881SPrameela Rani Garnepudi #define RSI_USB_REQ_OUT (USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE) 1684b1fc881SPrameela Rani Garnepudi #define RSI_USB_REQ_IN (USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE) 1694b1fc881SPrameela Rani Garnepudi 170dad0d04fSFariya Fatima /* rsi_usb_reg_read() - This function reads data from given register address. 171dad0d04fSFariya Fatima * @usbdev: Pointer to the usb_device structure. 172dad0d04fSFariya Fatima * @reg: Address of the register to be read. 173dad0d04fSFariya Fatima * @value: Value to be read. 174dad0d04fSFariya Fatima * @len: length of data to be read. 175dad0d04fSFariya Fatima * 17650591c60SAlexey Khoroshilov * Return: status: 0 on success, a negative error code on failure. 177dad0d04fSFariya Fatima */ 178dad0d04fSFariya Fatima static int rsi_usb_reg_read(struct usb_device *usbdev, 179dad0d04fSFariya Fatima u32 reg, 180dad0d04fSFariya Fatima u16 *value, 181dad0d04fSFariya Fatima u16 len) 182dad0d04fSFariya Fatima { 183d453ba81SFariya Fatima u8 *buf; 184d453ba81SFariya Fatima int status = -ENOMEM; 185d453ba81SFariya Fatima 186d35ef8f8SColin Ian King if (len > RSI_USB_CTRL_BUF_SIZE) 187d35ef8f8SColin Ian King return -EINVAL; 188d35ef8f8SColin Ian King 189523b724aSAmitkumar Karwar buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); 190d453ba81SFariya Fatima if (!buf) 191d453ba81SFariya Fatima return status; 192dad0d04fSFariya Fatima 193dad0d04fSFariya Fatima status = usb_control_msg(usbdev, 194dad0d04fSFariya Fatima usb_rcvctrlpipe(usbdev, 0), 195dad0d04fSFariya Fatima USB_VENDOR_REGISTER_READ, 1964b1fc881SPrameela Rani Garnepudi RSI_USB_REQ_IN, 197dad0d04fSFariya Fatima ((reg & 0xffff0000) >> 16), (reg & 0xffff), 198d453ba81SFariya Fatima (void *)buf, 199dad0d04fSFariya Fatima len, 2004b1fc881SPrameela Rani Garnepudi USB_CTRL_GET_TIMEOUT); 201dad0d04fSFariya Fatima 202d453ba81SFariya Fatima *value = (buf[0] | (buf[1] << 8)); 203dad0d04fSFariya Fatima if (status < 0) { 204dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, 205dad0d04fSFariya Fatima "%s: Reg read failed with error code :%d\n", 206dad0d04fSFariya Fatima __func__, status); 207dad0d04fSFariya Fatima } 208d453ba81SFariya Fatima kfree(buf); 209d453ba81SFariya Fatima 210dad0d04fSFariya Fatima return status; 211dad0d04fSFariya Fatima } 212dad0d04fSFariya Fatima 213dad0d04fSFariya Fatima /** 214dad0d04fSFariya Fatima * rsi_usb_reg_write() - This function writes the given data into the given 215dad0d04fSFariya Fatima * register address. 216dad0d04fSFariya Fatima * @usbdev: Pointer to the usb_device structure. 217dad0d04fSFariya Fatima * @reg: Address of the register. 218dad0d04fSFariya Fatima * @value: Value to write. 219dad0d04fSFariya Fatima * @len: Length of data to be written. 220dad0d04fSFariya Fatima * 22150591c60SAlexey Khoroshilov * Return: status: 0 on success, a negative error code on failure. 222dad0d04fSFariya Fatima */ 223dad0d04fSFariya Fatima static int rsi_usb_reg_write(struct usb_device *usbdev, 224dad0d04fSFariya Fatima u32 reg, 2250a60014bSSiva Rebbagondla u32 value, 226dad0d04fSFariya Fatima u16 len) 227dad0d04fSFariya Fatima { 228d453ba81SFariya Fatima u8 *usb_reg_buf; 229d453ba81SFariya Fatima int status = -ENOMEM; 230d453ba81SFariya Fatima 231d35ef8f8SColin Ian King if (len > RSI_USB_CTRL_BUF_SIZE) 232d35ef8f8SColin Ian King return -EINVAL; 233d35ef8f8SColin Ian King 234523b724aSAmitkumar Karwar usb_reg_buf = kmalloc(RSI_USB_CTRL_BUF_SIZE, GFP_KERNEL); 235d453ba81SFariya Fatima if (!usb_reg_buf) 236d453ba81SFariya Fatima return status; 237dad0d04fSFariya Fatima 2380a60014bSSiva Rebbagondla usb_reg_buf[0] = (cpu_to_le32(value) & 0x00ff); 2390a60014bSSiva Rebbagondla usb_reg_buf[1] = (cpu_to_le32(value) & 0xff00) >> 8; 2400a60014bSSiva Rebbagondla usb_reg_buf[2] = (cpu_to_le32(value) & 0x00ff0000) >> 16; 2410a60014bSSiva Rebbagondla usb_reg_buf[3] = (cpu_to_le32(value) & 0xff000000) >> 24; 242dad0d04fSFariya Fatima 243dad0d04fSFariya Fatima status = usb_control_msg(usbdev, 244dad0d04fSFariya Fatima usb_sndctrlpipe(usbdev, 0), 245dad0d04fSFariya Fatima USB_VENDOR_REGISTER_WRITE, 2464b1fc881SPrameela Rani Garnepudi RSI_USB_REQ_OUT, 2470a60014bSSiva Rebbagondla ((cpu_to_le32(reg) & 0xffff0000) >> 16), 2480a60014bSSiva Rebbagondla (cpu_to_le32(reg) & 0xffff), 249dad0d04fSFariya Fatima (void *)usb_reg_buf, 250dad0d04fSFariya Fatima len, 2514b1fc881SPrameela Rani Garnepudi USB_CTRL_SET_TIMEOUT); 252dad0d04fSFariya Fatima if (status < 0) { 253dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, 254dad0d04fSFariya Fatima "%s: Reg write failed with error code :%d\n", 255dad0d04fSFariya Fatima __func__, status); 256dad0d04fSFariya Fatima } 257d453ba81SFariya Fatima kfree(usb_reg_buf); 258d453ba81SFariya Fatima 259dad0d04fSFariya Fatima return status; 260dad0d04fSFariya Fatima } 261dad0d04fSFariya Fatima 262dad0d04fSFariya Fatima /** 263dad0d04fSFariya Fatima * rsi_rx_done_handler() - This function is called when a packet is received 264b9c767fdSSiva Rebbagondla * from USB stack. This is callback to receive done. 265dad0d04fSFariya Fatima * @urb: Received URB. 266dad0d04fSFariya Fatima * 267dad0d04fSFariya Fatima * Return: None. 268dad0d04fSFariya Fatima */ 269dad0d04fSFariya Fatima static void rsi_rx_done_handler(struct urb *urb) 270dad0d04fSFariya Fatima { 2711100f81bSPrameela Rani Garnepudi struct rx_usb_ctrl_block *rx_cb = urb->context; 2721100f81bSPrameela Rani Garnepudi struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)rx_cb->data; 273a1854faeSPrameela Rani Garnepudi int status = -EINVAL; 274dad0d04fSFariya Fatima 2750a60014bSSiva Rebbagondla if (urb->status) { 2760a60014bSSiva Rebbagondla dev_kfree_skb(rx_cb->rx_skb); 2770a60014bSSiva Rebbagondla return; 2780a60014bSSiva Rebbagondla } 279dad0d04fSFariya Fatima 280baa8caf4SSiva Rebbagondla if (urb->actual_length <= 0 || 281baa8caf4SSiva Rebbagondla urb->actual_length > rx_cb->rx_skb->len) { 282baa8caf4SSiva Rebbagondla rsi_dbg(INFO_ZONE, "%s: Invalid packet length = %d\n", 283baa8caf4SSiva Rebbagondla __func__, urb->actual_length); 284a1854faeSPrameela Rani Garnepudi goto out; 285a1854faeSPrameela Rani Garnepudi } 286a1854faeSPrameela Rani Garnepudi if (skb_queue_len(&dev->rx_q) >= RSI_MAX_RX_PKTS) { 287a1854faeSPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "Max RX packets reached\n"); 288a1854faeSPrameela Rani Garnepudi goto out; 289a1854faeSPrameela Rani Garnepudi } 290baa8caf4SSiva Rebbagondla skb_trim(rx_cb->rx_skb, urb->actual_length); 291a1854faeSPrameela Rani Garnepudi skb_queue_tail(&dev->rx_q, rx_cb->rx_skb); 292a1854faeSPrameela Rani Garnepudi 293dad0d04fSFariya Fatima rsi_set_event(&dev->rx_thread.event); 294a1854faeSPrameela Rani Garnepudi status = 0; 295a1854faeSPrameela Rani Garnepudi 296a1854faeSPrameela Rani Garnepudi out: 297b9b9f9feSJohan Hovold if (rsi_rx_urb_submit(dev->priv, rx_cb->ep_num, GFP_ATOMIC)) 298a1854faeSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Failed in urb submission", __func__); 299a1854faeSPrameela Rani Garnepudi 300a1854faeSPrameela Rani Garnepudi if (status) 301a1854faeSPrameela Rani Garnepudi dev_kfree_skb(rx_cb->rx_skb); 302dad0d04fSFariya Fatima } 303dad0d04fSFariya Fatima 304e93cd351SJohan Hovold static void rsi_rx_urb_kill(struct rsi_hw *adapter, u8 ep_num) 305e93cd351SJohan Hovold { 306e93cd351SJohan Hovold struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 307e93cd351SJohan Hovold struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; 308e93cd351SJohan Hovold struct urb *urb = rx_cb->rx_urb; 309e93cd351SJohan Hovold 310e93cd351SJohan Hovold usb_kill_urb(urb); 311e93cd351SJohan Hovold } 312e93cd351SJohan Hovold 313dad0d04fSFariya Fatima /** 314dad0d04fSFariya Fatima * rsi_rx_urb_submit() - This function submits the given URB to the USB stack. 315dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 316f21e6c58SLee Jones * @ep_num: Endpoint number. 317f21e6c58SLee Jones * @mem_flags: The type of memory to allocate. 318dad0d04fSFariya Fatima * 31950591c60SAlexey Khoroshilov * Return: 0 on success, a negative error code on failure. 320dad0d04fSFariya Fatima */ 321b9b9f9feSJohan Hovold static int rsi_rx_urb_submit(struct rsi_hw *adapter, u8 ep_num, gfp_t mem_flags) 322dad0d04fSFariya Fatima { 323dad0d04fSFariya Fatima struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 3241100f81bSPrameela Rani Garnepudi struct rx_usb_ctrl_block *rx_cb = &dev->rx_cb[ep_num - 1]; 3251100f81bSPrameela Rani Garnepudi struct urb *urb = rx_cb->rx_urb; 326dad0d04fSFariya Fatima int status; 327a1854faeSPrameela Rani Garnepudi struct sk_buff *skb; 328a1854faeSPrameela Rani Garnepudi u8 dword_align_bytes = 0; 329a1854faeSPrameela Rani Garnepudi 330a1854faeSPrameela Rani Garnepudi #define RSI_MAX_RX_USB_PKT_SIZE 3000 331a1854faeSPrameela Rani Garnepudi skb = dev_alloc_skb(RSI_MAX_RX_USB_PKT_SIZE); 332a1854faeSPrameela Rani Garnepudi if (!skb) 333a1854faeSPrameela Rani Garnepudi return -ENOMEM; 334a1854faeSPrameela Rani Garnepudi skb_reserve(skb, MAX_DWORD_ALIGN_BYTES); 335baa8caf4SSiva Rebbagondla skb_put(skb, RSI_MAX_RX_USB_PKT_SIZE - MAX_DWORD_ALIGN_BYTES); 336a1854faeSPrameela Rani Garnepudi dword_align_bytes = (unsigned long)skb->data & 0x3f; 337a1854faeSPrameela Rani Garnepudi if (dword_align_bytes > 0) 338a1854faeSPrameela Rani Garnepudi skb_push(skb, dword_align_bytes); 339a1854faeSPrameela Rani Garnepudi urb->transfer_buffer = skb->data; 340a1854faeSPrameela Rani Garnepudi rx_cb->rx_skb = skb; 341dad0d04fSFariya Fatima 342dad0d04fSFariya Fatima usb_fill_bulk_urb(urb, 343dad0d04fSFariya Fatima dev->usbdev, 344dad0d04fSFariya Fatima usb_rcvbulkpipe(dev->usbdev, 345a4302bffSSiva Rebbagondla dev->bulkin_endpoint_addr[ep_num - 1]), 346dad0d04fSFariya Fatima urb->transfer_buffer, 347baa8caf4SSiva Rebbagondla skb->len, 348dad0d04fSFariya Fatima rsi_rx_done_handler, 3491100f81bSPrameela Rani Garnepudi rx_cb); 350dad0d04fSFariya Fatima 351b9b9f9feSJohan Hovold status = usb_submit_urb(urb, mem_flags); 35247768297SJohan Hovold if (status) { 353dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed in urb submission\n", __func__); 35447768297SJohan Hovold dev_kfree_skb(skb); 35547768297SJohan Hovold } 356dad0d04fSFariya Fatima 357dad0d04fSFariya Fatima return status; 358dad0d04fSFariya Fatima } 359dad0d04fSFariya Fatima 36088fa51e1SPrameela Rani Garnepudi static int rsi_usb_read_register_multiple(struct rsi_hw *adapter, u32 addr, 36188fa51e1SPrameela Rani Garnepudi u8 *data, u16 count) 36288fa51e1SPrameela Rani Garnepudi { 36388fa51e1SPrameela Rani Garnepudi struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 36488fa51e1SPrameela Rani Garnepudi u8 *buf; 36588fa51e1SPrameela Rani Garnepudi u16 transfer; 36688fa51e1SPrameela Rani Garnepudi int status; 36788fa51e1SPrameela Rani Garnepudi 36888fa51e1SPrameela Rani Garnepudi if (!addr) 36988fa51e1SPrameela Rani Garnepudi return -EINVAL; 37088fa51e1SPrameela Rani Garnepudi 37188fa51e1SPrameela Rani Garnepudi buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL); 37288fa51e1SPrameela Rani Garnepudi if (!buf) 37388fa51e1SPrameela Rani Garnepudi return -ENOMEM; 37488fa51e1SPrameela Rani Garnepudi 37588fa51e1SPrameela Rani Garnepudi while (count) { 37688fa51e1SPrameela Rani Garnepudi transfer = min_t(u16, count, RSI_USB_BUF_SIZE); 37788fa51e1SPrameela Rani Garnepudi status = usb_control_msg(dev->usbdev, 37888fa51e1SPrameela Rani Garnepudi usb_rcvctrlpipe(dev->usbdev, 0), 37988fa51e1SPrameela Rani Garnepudi USB_VENDOR_REGISTER_READ, 38088fa51e1SPrameela Rani Garnepudi RSI_USB_REQ_IN, 38188fa51e1SPrameela Rani Garnepudi ((addr & 0xffff0000) >> 16), 38288fa51e1SPrameela Rani Garnepudi (addr & 0xffff), (void *)buf, 38388fa51e1SPrameela Rani Garnepudi transfer, USB_CTRL_GET_TIMEOUT); 38488fa51e1SPrameela Rani Garnepudi if (status < 0) { 38588fa51e1SPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, 38688fa51e1SPrameela Rani Garnepudi "Reg read failed with error code :%d\n", 38788fa51e1SPrameela Rani Garnepudi status); 38888fa51e1SPrameela Rani Garnepudi kfree(buf); 38988fa51e1SPrameela Rani Garnepudi return status; 39088fa51e1SPrameela Rani Garnepudi } 39188fa51e1SPrameela Rani Garnepudi memcpy(data, buf, transfer); 39288fa51e1SPrameela Rani Garnepudi count -= transfer; 39388fa51e1SPrameela Rani Garnepudi data += transfer; 39488fa51e1SPrameela Rani Garnepudi addr += transfer; 39588fa51e1SPrameela Rani Garnepudi } 39688fa51e1SPrameela Rani Garnepudi kfree(buf); 39788fa51e1SPrameela Rani Garnepudi return 0; 39888fa51e1SPrameela Rani Garnepudi } 39988fa51e1SPrameela Rani Garnepudi 400dad0d04fSFariya Fatima /** 401dad0d04fSFariya Fatima * rsi_usb_write_register_multiple() - This function writes multiple bytes of 402dad0d04fSFariya Fatima * information to multiple registers. 403dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 404dad0d04fSFariya Fatima * @addr: Address of the register. 405dad0d04fSFariya Fatima * @data: Pointer to the data that has to be written. 406dad0d04fSFariya Fatima * @count: Number of multiple bytes to be written on to the registers. 407dad0d04fSFariya Fatima * 40850591c60SAlexey Khoroshilov * Return: status: 0 on success, a negative error code on failure. 409dad0d04fSFariya Fatima */ 4105578b1ffSPrameela Rani Garnepudi static int rsi_usb_write_register_multiple(struct rsi_hw *adapter, u32 addr, 4115578b1ffSPrameela Rani Garnepudi u8 *data, u16 count) 412dad0d04fSFariya Fatima { 413dad0d04fSFariya Fatima struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 414dad0d04fSFariya Fatima u8 *buf; 4152fbbe517SPrameela Rani Garnepudi u16 transfer; 416dad0d04fSFariya Fatima int status = 0; 417dad0d04fSFariya Fatima 4187bdead7aSamit karwar buf = kzalloc(RSI_USB_BUF_SIZE, GFP_KERNEL); 419dad0d04fSFariya Fatima if (!buf) 420dad0d04fSFariya Fatima return -ENOMEM; 421dad0d04fSFariya Fatima 422dad0d04fSFariya Fatima while (count) { 4232fbbe517SPrameela Rani Garnepudi transfer = min_t(u16, count, RSI_USB_BUF_SIZE); 424dad0d04fSFariya Fatima memcpy(buf, data, transfer); 425dad0d04fSFariya Fatima status = usb_control_msg(dev->usbdev, 426dad0d04fSFariya Fatima usb_sndctrlpipe(dev->usbdev, 0), 427dad0d04fSFariya Fatima USB_VENDOR_REGISTER_WRITE, 4284b1fc881SPrameela Rani Garnepudi RSI_USB_REQ_OUT, 429dad0d04fSFariya Fatima ((addr & 0xffff0000) >> 16), 430dad0d04fSFariya Fatima (addr & 0xffff), 431dad0d04fSFariya Fatima (void *)buf, 432dad0d04fSFariya Fatima transfer, 4334b1fc881SPrameela Rani Garnepudi USB_CTRL_SET_TIMEOUT); 434dad0d04fSFariya Fatima if (status < 0) { 435dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, 436dad0d04fSFariya Fatima "Reg write failed with error code :%d\n", 437dad0d04fSFariya Fatima status); 438ea3336acSPrameela Rani Garnepudi kfree(buf); 439ea3336acSPrameela Rani Garnepudi return status; 440ea3336acSPrameela Rani Garnepudi } 441dad0d04fSFariya Fatima count -= transfer; 442dad0d04fSFariya Fatima data += transfer; 443dad0d04fSFariya Fatima addr += transfer; 444dad0d04fSFariya Fatima } 445dad0d04fSFariya Fatima 446dad0d04fSFariya Fatima kfree(buf); 447dad0d04fSFariya Fatima return 0; 448dad0d04fSFariya Fatima } 449dad0d04fSFariya Fatima 450dad0d04fSFariya Fatima /** 451dad0d04fSFariya Fatima *rsi_usb_host_intf_write_pkt() - This function writes the packet to the 452dad0d04fSFariya Fatima * USB card. 453dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 454dad0d04fSFariya Fatima * @pkt: Pointer to the data to be written on to the card. 455dad0d04fSFariya Fatima * @len: Length of the data to be written on to the card. 456dad0d04fSFariya Fatima * 45750591c60SAlexey Khoroshilov * Return: 0 on success, a negative error code on failure. 458dad0d04fSFariya Fatima */ 459dad0d04fSFariya Fatima static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter, 460dad0d04fSFariya Fatima u8 *pkt, 461dad0d04fSFariya Fatima u32 len) 462dad0d04fSFariya Fatima { 463ac6107caSAmitkumar Karwar u32 queueno = ((pkt[1] >> 4) & 0x7); 464dad0d04fSFariya Fatima u8 endpoint; 465dad0d04fSFariya Fatima 466d1f69e41SKarun Eagalapati endpoint = ((queueno == RSI_WIFI_MGMT_Q || queueno == RSI_WIFI_DATA_Q || 467d1f69e41SKarun Eagalapati queueno == RSI_COEX_Q) ? WLAN_EP : BT_EP); 468dad0d04fSFariya Fatima 469dad0d04fSFariya Fatima return rsi_write_multiple(adapter, 470dad0d04fSFariya Fatima endpoint, 471dad0d04fSFariya Fatima (u8 *)pkt, 472dad0d04fSFariya Fatima len); 473dad0d04fSFariya Fatima } 474dad0d04fSFariya Fatima 475b97e9b94SPrameela Rani Garnepudi static int rsi_usb_master_reg_read(struct rsi_hw *adapter, u32 reg, 476b97e9b94SPrameela Rani Garnepudi u32 *value, u16 len) 477b97e9b94SPrameela Rani Garnepudi { 478b97e9b94SPrameela Rani Garnepudi struct usb_device *usbdev = 479b97e9b94SPrameela Rani Garnepudi ((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev; 480e6249e15SAmitkumar Karwar u16 temp; 481e6249e15SAmitkumar Karwar int ret; 482b97e9b94SPrameela Rani Garnepudi 483e6249e15SAmitkumar Karwar ret = rsi_usb_reg_read(usbdev, reg, &temp, len); 484e6249e15SAmitkumar Karwar if (ret < 0) 485e6249e15SAmitkumar Karwar return ret; 486e6249e15SAmitkumar Karwar *value = temp; 487e6249e15SAmitkumar Karwar 488e6249e15SAmitkumar Karwar return 0; 489b97e9b94SPrameela Rani Garnepudi } 490b97e9b94SPrameela Rani Garnepudi 491b97e9b94SPrameela Rani Garnepudi static int rsi_usb_master_reg_write(struct rsi_hw *adapter, 492b97e9b94SPrameela Rani Garnepudi unsigned long reg, 493b97e9b94SPrameela Rani Garnepudi unsigned long value, u16 len) 494b97e9b94SPrameela Rani Garnepudi { 495b97e9b94SPrameela Rani Garnepudi struct usb_device *usbdev = 496b97e9b94SPrameela Rani Garnepudi ((struct rsi_91x_usbdev *)adapter->rsi_dev)->usbdev; 497b97e9b94SPrameela Rani Garnepudi 498b97e9b94SPrameela Rani Garnepudi return rsi_usb_reg_write(usbdev, reg, value, len); 499b97e9b94SPrameela Rani Garnepudi } 500b97e9b94SPrameela Rani Garnepudi 501b97e9b94SPrameela Rani Garnepudi static int rsi_usb_load_data_master_write(struct rsi_hw *adapter, 502b97e9b94SPrameela Rani Garnepudi u32 base_address, 503b97e9b94SPrameela Rani Garnepudi u32 instructions_sz, u16 block_size, 504b97e9b94SPrameela Rani Garnepudi u8 *ta_firmware) 505b97e9b94SPrameela Rani Garnepudi { 506b97e9b94SPrameela Rani Garnepudi u16 num_blocks; 507b97e9b94SPrameela Rani Garnepudi u32 cur_indx, i; 508b97e9b94SPrameela Rani Garnepudi u8 temp_buf[256]; 509b97e9b94SPrameela Rani Garnepudi int status; 510b97e9b94SPrameela Rani Garnepudi 511b97e9b94SPrameela Rani Garnepudi num_blocks = instructions_sz / block_size; 512b97e9b94SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "num_blocks: %d\n", num_blocks); 513b97e9b94SPrameela Rani Garnepudi 514b97e9b94SPrameela Rani Garnepudi for (cur_indx = 0, i = 0; i < num_blocks; i++, cur_indx += block_size) { 515b97e9b94SPrameela Rani Garnepudi memcpy(temp_buf, ta_firmware + cur_indx, block_size); 516b97e9b94SPrameela Rani Garnepudi status = rsi_usb_write_register_multiple(adapter, base_address, 517b97e9b94SPrameela Rani Garnepudi (u8 *)(temp_buf), 518b97e9b94SPrameela Rani Garnepudi block_size); 519b97e9b94SPrameela Rani Garnepudi if (status < 0) 520b97e9b94SPrameela Rani Garnepudi return status; 521b97e9b94SPrameela Rani Garnepudi 522b97e9b94SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, "%s: loading block: %d\n", __func__, i); 523b97e9b94SPrameela Rani Garnepudi base_address += block_size; 524b97e9b94SPrameela Rani Garnepudi } 525b97e9b94SPrameela Rani Garnepudi 526b97e9b94SPrameela Rani Garnepudi if (instructions_sz % block_size) { 527b97e9b94SPrameela Rani Garnepudi memset(temp_buf, 0, block_size); 528b97e9b94SPrameela Rani Garnepudi memcpy(temp_buf, ta_firmware + cur_indx, 529b97e9b94SPrameela Rani Garnepudi instructions_sz % block_size); 530b97e9b94SPrameela Rani Garnepudi status = rsi_usb_write_register_multiple 531b97e9b94SPrameela Rani Garnepudi (adapter, base_address, 532b97e9b94SPrameela Rani Garnepudi (u8 *)temp_buf, 533b97e9b94SPrameela Rani Garnepudi instructions_sz % block_size); 534b97e9b94SPrameela Rani Garnepudi if (status < 0) 535b97e9b94SPrameela Rani Garnepudi return status; 536b97e9b94SPrameela Rani Garnepudi rsi_dbg(INFO_ZONE, 537b97e9b94SPrameela Rani Garnepudi "Written Last Block in Address 0x%x Successfully\n", 538b97e9b94SPrameela Rani Garnepudi cur_indx); 539b97e9b94SPrameela Rani Garnepudi } 540b97e9b94SPrameela Rani Garnepudi return 0; 541b97e9b94SPrameela Rani Garnepudi } 542b97e9b94SPrameela Rani Garnepudi 543a2ce952cSPrameela Rani Garnepudi static struct rsi_host_intf_ops usb_host_intf_ops = { 544a2ce952cSPrameela Rani Garnepudi .write_pkt = rsi_usb_host_intf_write_pkt, 545a2ce952cSPrameela Rani Garnepudi .read_reg_multiple = rsi_usb_read_register_multiple, 546a2ce952cSPrameela Rani Garnepudi .write_reg_multiple = rsi_usb_write_register_multiple, 547b97e9b94SPrameela Rani Garnepudi .master_reg_read = rsi_usb_master_reg_read, 548b97e9b94SPrameela Rani Garnepudi .master_reg_write = rsi_usb_master_reg_write, 549b97e9b94SPrameela Rani Garnepudi .load_data_master_write = rsi_usb_load_data_master_write, 550a2ce952cSPrameela Rani Garnepudi }; 551a2ce952cSPrameela Rani Garnepudi 552dad0d04fSFariya Fatima /** 553dad0d04fSFariya Fatima * rsi_deinit_usb_interface() - This function deinitializes the usb interface. 554dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 555dad0d04fSFariya Fatima * 556dad0d04fSFariya Fatima * Return: None. 557dad0d04fSFariya Fatima */ 558dad0d04fSFariya Fatima static void rsi_deinit_usb_interface(struct rsi_hw *adapter) 559dad0d04fSFariya Fatima { 560dad0d04fSFariya Fatima struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 561dad0d04fSFariya Fatima 562dad0d04fSFariya Fatima rsi_kill_thread(&dev->rx_thread); 5631100f81bSPrameela Rani Garnepudi 5648809f08cSPrameela Rani Garnepudi usb_free_urb(dev->rx_cb[0].rx_urb); 565a1854faeSPrameela Rani Garnepudi if (adapter->priv->coex_mode > 1) 5668809f08cSPrameela Rani Garnepudi usb_free_urb(dev->rx_cb[1].rx_urb); 5671100f81bSPrameela Rani Garnepudi 568dad0d04fSFariya Fatima kfree(dev->tx_buffer); 569dad0d04fSFariya Fatima } 570dad0d04fSFariya Fatima 5711100f81bSPrameela Rani Garnepudi static int rsi_usb_init_rx(struct rsi_hw *adapter) 5721100f81bSPrameela Rani Garnepudi { 5731100f81bSPrameela Rani Garnepudi struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 5741100f81bSPrameela Rani Garnepudi struct rx_usb_ctrl_block *rx_cb; 5758809f08cSPrameela Rani Garnepudi u8 idx, num_rx_cb; 5761100f81bSPrameela Rani Garnepudi 5778809f08cSPrameela Rani Garnepudi num_rx_cb = (adapter->priv->coex_mode > 1 ? 2 : 1); 5788809f08cSPrameela Rani Garnepudi 5798809f08cSPrameela Rani Garnepudi for (idx = 0; idx < num_rx_cb; idx++) { 5801100f81bSPrameela Rani Garnepudi rx_cb = &dev->rx_cb[idx]; 5811100f81bSPrameela Rani Garnepudi 5821100f81bSPrameela Rani Garnepudi rx_cb->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 5831100f81bSPrameela Rani Garnepudi if (!rx_cb->rx_urb) { 5841100f81bSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Failed alloc rx urb[%d]\n", idx); 5851100f81bSPrameela Rani Garnepudi goto err; 5861100f81bSPrameela Rani Garnepudi } 5871100f81bSPrameela Rani Garnepudi rx_cb->ep_num = idx + 1; 5881100f81bSPrameela Rani Garnepudi rx_cb->data = (void *)dev; 5891100f81bSPrameela Rani Garnepudi } 590a1854faeSPrameela Rani Garnepudi skb_queue_head_init(&dev->rx_q); 591a1854faeSPrameela Rani Garnepudi rsi_init_event(&dev->rx_thread.event); 592a1854faeSPrameela Rani Garnepudi if (rsi_create_kthread(adapter->priv, &dev->rx_thread, 593a1854faeSPrameela Rani Garnepudi rsi_usb_rx_thread, "RX-Thread")) { 594a1854faeSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__); 595a1854faeSPrameela Rani Garnepudi goto err; 596a1854faeSPrameela Rani Garnepudi } 597a1854faeSPrameela Rani Garnepudi 5981100f81bSPrameela Rani Garnepudi return 0; 5991100f81bSPrameela Rani Garnepudi 6001100f81bSPrameela Rani Garnepudi err: 6018809f08cSPrameela Rani Garnepudi usb_free_urb(dev->rx_cb[0].rx_urb); 602a1854faeSPrameela Rani Garnepudi if (adapter->priv->coex_mode > 1) 6038809f08cSPrameela Rani Garnepudi usb_free_urb(dev->rx_cb[1].rx_urb); 604a1854faeSPrameela Rani Garnepudi 6051100f81bSPrameela Rani Garnepudi return -1; 6061100f81bSPrameela Rani Garnepudi } 6071100f81bSPrameela Rani Garnepudi 608dad0d04fSFariya Fatima /** 609dad0d04fSFariya Fatima * rsi_init_usb_interface() - This function initializes the usb interface. 610dad0d04fSFariya Fatima * @adapter: Pointer to the adapter structure. 611dad0d04fSFariya Fatima * @pfunction: Pointer to USB interface structure. 612dad0d04fSFariya Fatima * 61350591c60SAlexey Khoroshilov * Return: 0 on success, a negative error code on failure. 614dad0d04fSFariya Fatima */ 615dad0d04fSFariya Fatima static int rsi_init_usb_interface(struct rsi_hw *adapter, 616dad0d04fSFariya Fatima struct usb_interface *pfunction) 617dad0d04fSFariya Fatima { 618dad0d04fSFariya Fatima struct rsi_91x_usbdev *rsi_dev; 6198809f08cSPrameela Rani Garnepudi int status; 620dad0d04fSFariya Fatima 621dad0d04fSFariya Fatima rsi_dev = kzalloc(sizeof(*rsi_dev), GFP_KERNEL); 622dad0d04fSFariya Fatima if (!rsi_dev) 623dad0d04fSFariya Fatima return -ENOMEM; 624dad0d04fSFariya Fatima 625dad0d04fSFariya Fatima adapter->rsi_dev = rsi_dev; 626dad0d04fSFariya Fatima rsi_dev->usbdev = interface_to_usbdev(pfunction); 627a1854faeSPrameela Rani Garnepudi rsi_dev->priv = (void *)adapter; 628dad0d04fSFariya Fatima 629a1854faeSPrameela Rani Garnepudi if (rsi_find_bulk_in_and_out_endpoints(pfunction, adapter)) { 630a1854faeSPrameela Rani Garnepudi status = -EINVAL; 631a1854faeSPrameela Rani Garnepudi goto fail_eps; 632a1854faeSPrameela Rani Garnepudi } 633dad0d04fSFariya Fatima 634dad0d04fSFariya Fatima adapter->device = &pfunction->dev; 635dad0d04fSFariya Fatima usb_set_intfdata(pfunction, adapter); 636dad0d04fSFariya Fatima 6375bc5ca85SAlexey Khoroshilov rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL); 63850591c60SAlexey Khoroshilov if (!rsi_dev->tx_buffer) { 63950591c60SAlexey Khoroshilov status = -ENOMEM; 640a1854faeSPrameela Rani Garnepudi goto fail_eps; 64150591c60SAlexey Khoroshilov } 6421100f81bSPrameela Rani Garnepudi 6431100f81bSPrameela Rani Garnepudi if (rsi_usb_init_rx(adapter)) { 6441100f81bSPrameela Rani Garnepudi rsi_dbg(ERR_ZONE, "Failed to init RX handle\n"); 645a1854faeSPrameela Rani Garnepudi status = -ENOMEM; 646a1854faeSPrameela Rani Garnepudi goto fail_rx; 64750591c60SAlexey Khoroshilov } 6481100f81bSPrameela Rani Garnepudi 649dad0d04fSFariya Fatima rsi_dev->tx_blk_size = 252; 650b78e91bcSPrameela Rani Garnepudi adapter->block_size = rsi_dev->tx_blk_size; 651dad0d04fSFariya Fatima 652dad0d04fSFariya Fatima /* Initializing function callbacks */ 653dad0d04fSFariya Fatima adapter->check_hw_queue_status = rsi_usb_check_queue_status; 654dad0d04fSFariya Fatima adapter->determine_event_timeout = rsi_usb_event_timeout; 655a2ce952cSPrameela Rani Garnepudi adapter->rsi_host_intf = RSI_HOST_INTF_USB; 656a2ce952cSPrameela Rani Garnepudi adapter->host_intf_ops = &usb_host_intf_ops; 657dad0d04fSFariya Fatima 658dad0d04fSFariya Fatima #ifdef CONFIG_RSI_DEBUGFS 659dad0d04fSFariya Fatima /* In USB, one less than the MAX_DEBUGFS_ENTRIES entries is required */ 660dad0d04fSFariya Fatima adapter->num_debugfs_entries = (MAX_DEBUGFS_ENTRIES - 1); 661dad0d04fSFariya Fatima #endif 662dad0d04fSFariya Fatima 663dad0d04fSFariya Fatima rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__); 664dad0d04fSFariya Fatima return 0; 665dad0d04fSFariya Fatima 666a1854faeSPrameela Rani Garnepudi fail_rx: 667a1854faeSPrameela Rani Garnepudi kfree(rsi_dev->tx_buffer); 668a1854faeSPrameela Rani Garnepudi 669a1854faeSPrameela Rani Garnepudi fail_eps: 670a1854faeSPrameela Rani Garnepudi 671dad0d04fSFariya Fatima return status; 672dad0d04fSFariya Fatima } 673dad0d04fSFariya Fatima 674ea0676c4SKarun Eagalapati static int usb_ulp_read_write(struct rsi_hw *adapter, u16 addr, u32 data, 675ea0676c4SKarun Eagalapati u16 len_in_bits) 676ea0676c4SKarun Eagalapati { 677ea0676c4SKarun Eagalapati int ret; 678ea0676c4SKarun Eagalapati 679ea0676c4SKarun Eagalapati ret = rsi_usb_master_reg_write 680ea0676c4SKarun Eagalapati (adapter, RSI_GSPI_DATA_REG1, 681ea0676c4SKarun Eagalapati ((addr << 6) | ((data >> 16) & 0xffff)), 2); 682ea0676c4SKarun Eagalapati if (ret < 0) 683ea0676c4SKarun Eagalapati return ret; 684ea0676c4SKarun Eagalapati 685ea0676c4SKarun Eagalapati ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_DATA_REG0, 686ea0676c4SKarun Eagalapati (data & 0xffff), 2); 687ea0676c4SKarun Eagalapati if (ret < 0) 688ea0676c4SKarun Eagalapati return ret; 689ea0676c4SKarun Eagalapati 690ea0676c4SKarun Eagalapati /* Initializing GSPI for ULP read/writes */ 691ea0676c4SKarun Eagalapati rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG0, 692ea0676c4SKarun Eagalapati RSI_GSPI_CTRL_REG0_VALUE, 2); 693ea0676c4SKarun Eagalapati 694ea0676c4SKarun Eagalapati ret = rsi_usb_master_reg_write(adapter, RSI_GSPI_CTRL_REG1, 695ea0676c4SKarun Eagalapati ((len_in_bits - 1) | RSI_GSPI_TRIG), 2); 696ea0676c4SKarun Eagalapati if (ret < 0) 697ea0676c4SKarun Eagalapati return ret; 698ea0676c4SKarun Eagalapati 699ea0676c4SKarun Eagalapati msleep(20); 700ea0676c4SKarun Eagalapati 701ea0676c4SKarun Eagalapati return 0; 702ea0676c4SKarun Eagalapati } 703ea0676c4SKarun Eagalapati 704ea0676c4SKarun Eagalapati static int rsi_reset_card(struct rsi_hw *adapter) 705ea0676c4SKarun Eagalapati { 706ea0676c4SKarun Eagalapati int ret; 707ea0676c4SKarun Eagalapati 708ea0676c4SKarun Eagalapati rsi_dbg(INFO_ZONE, "Resetting Card...\n"); 709ea0676c4SKarun Eagalapati rsi_usb_master_reg_write(adapter, RSI_TA_HOLD_REG, 0xE, 4); 710ea0676c4SKarun Eagalapati 711ea0676c4SKarun Eagalapati /* This msleep will ensure Thread-Arch processor to go to hold 712ea0676c4SKarun Eagalapati * and any pending dma transfers to rf in device to finish. 713ea0676c4SKarun Eagalapati */ 714ea0676c4SKarun Eagalapati msleep(100); 715ea0676c4SKarun Eagalapati 71648c6b5c9SGustavo A. R. Silva ret = rsi_usb_master_reg_write(adapter, SWBL_REGOUT, 71716d3bb7bSAmitkumar Karwar RSI_FW_WDT_DISABLE_REQ, 71848c6b5c9SGustavo A. R. Silva RSI_COMMON_REG_SIZE); 71948c6b5c9SGustavo A. R. Silva if (ret < 0) { 72016d3bb7bSAmitkumar Karwar rsi_dbg(ERR_ZONE, "Disabling firmware watchdog timer failed\n"); 72116d3bb7bSAmitkumar Karwar goto fail; 72216d3bb7bSAmitkumar Karwar } 72316d3bb7bSAmitkumar Karwar 72417ff2c79SSiva Rebbagondla if (adapter->device_model != RSI_DEV_9116) { 725ea0676c4SKarun Eagalapati ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_1, 726ea0676c4SKarun Eagalapati RSI_ULP_WRITE_2, 32); 727ea0676c4SKarun Eagalapati if (ret < 0) 728ea0676c4SKarun Eagalapati goto fail; 729ea0676c4SKarun Eagalapati ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_2, 730ea0676c4SKarun Eagalapati RSI_ULP_WRITE_0, 32); 731ea0676c4SKarun Eagalapati if (ret < 0) 732ea0676c4SKarun Eagalapati goto fail; 733ea0676c4SKarun Eagalapati ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_1, 734ea0676c4SKarun Eagalapati RSI_ULP_WRITE_50, 32); 735ea0676c4SKarun Eagalapati if (ret < 0) 736ea0676c4SKarun Eagalapati goto fail; 737ea0676c4SKarun Eagalapati ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_DELAY_TIMER_2, 738ea0676c4SKarun Eagalapati RSI_ULP_WRITE_0, 32); 739ea0676c4SKarun Eagalapati if (ret < 0) 740ea0676c4SKarun Eagalapati goto fail; 741ea0676c4SKarun Eagalapati ret = usb_ulp_read_write(adapter, RSI_WATCH_DOG_TIMER_ENABLE, 742ea0676c4SKarun Eagalapati RSI_ULP_TIMER_ENABLE, 32); 743ea0676c4SKarun Eagalapati if (ret < 0) 744ea0676c4SKarun Eagalapati goto fail; 74517ff2c79SSiva Rebbagondla } else { 746*fb21d146SZhang Changzhong ret = rsi_usb_master_reg_write(adapter, 74717ff2c79SSiva Rebbagondla NWP_WWD_INTERRUPT_TIMER, 74817ff2c79SSiva Rebbagondla NWP_WWD_INT_TIMER_CLKS, 749*fb21d146SZhang Changzhong RSI_9116_REG_SIZE); 750*fb21d146SZhang Changzhong if (ret < 0) 75117ff2c79SSiva Rebbagondla goto fail; 752*fb21d146SZhang Changzhong ret = rsi_usb_master_reg_write(adapter, 75317ff2c79SSiva Rebbagondla NWP_WWD_SYSTEM_RESET_TIMER, 75417ff2c79SSiva Rebbagondla NWP_WWD_SYS_RESET_TIMER_CLKS, 755*fb21d146SZhang Changzhong RSI_9116_REG_SIZE); 756*fb21d146SZhang Changzhong if (ret < 0) 75717ff2c79SSiva Rebbagondla goto fail; 758*fb21d146SZhang Changzhong ret = rsi_usb_master_reg_write(adapter, 75917ff2c79SSiva Rebbagondla NWP_WWD_MODE_AND_RSTART, 76017ff2c79SSiva Rebbagondla NWP_WWD_TIMER_DISABLE, 761*fb21d146SZhang Changzhong RSI_9116_REG_SIZE); 762*fb21d146SZhang Changzhong if (ret < 0) 76317ff2c79SSiva Rebbagondla goto fail; 76417ff2c79SSiva Rebbagondla } 765ea0676c4SKarun Eagalapati 766ea0676c4SKarun Eagalapati rsi_dbg(INFO_ZONE, "Reset card done\n"); 767ea0676c4SKarun Eagalapati return ret; 768ea0676c4SKarun Eagalapati 769ea0676c4SKarun Eagalapati fail: 770ea0676c4SKarun Eagalapati rsi_dbg(ERR_ZONE, "Reset card failed\n"); 771ea0676c4SKarun Eagalapati return ret; 772ea0676c4SKarun Eagalapati } 773ea0676c4SKarun Eagalapati 774dad0d04fSFariya Fatima /** 775dad0d04fSFariya Fatima * rsi_probe() - This function is called by kernel when the driver provided 776dad0d04fSFariya Fatima * Vendor and device IDs are matched. All the initialization 777dad0d04fSFariya Fatima * work is done here. 778dad0d04fSFariya Fatima * @pfunction: Pointer to the USB interface structure. 779dad0d04fSFariya Fatima * @id: Pointer to the usb_device_id structure. 780dad0d04fSFariya Fatima * 78150591c60SAlexey Khoroshilov * Return: 0 on success, a negative error code on failure. 782dad0d04fSFariya Fatima */ 783dad0d04fSFariya Fatima static int rsi_probe(struct usb_interface *pfunction, 784dad0d04fSFariya Fatima const struct usb_device_id *id) 785dad0d04fSFariya Fatima { 786dad0d04fSFariya Fatima struct rsi_hw *adapter; 787dad0d04fSFariya Fatima struct rsi_91x_usbdev *dev; 788dad0d04fSFariya Fatima u16 fw_status; 78950591c60SAlexey Khoroshilov int status; 790dad0d04fSFariya Fatima 791dad0d04fSFariya Fatima rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__); 792dad0d04fSFariya Fatima 793898b2553SPrameela Rani Garnepudi adapter = rsi_91x_init(dev_oper_mode); 794dad0d04fSFariya Fatima if (!adapter) { 795dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n", 796dad0d04fSFariya Fatima __func__); 79750591c60SAlexey Khoroshilov return -ENOMEM; 798dad0d04fSFariya Fatima } 799b78e91bcSPrameela Rani Garnepudi adapter->rsi_host_intf = RSI_HOST_INTF_USB; 800dad0d04fSFariya Fatima 80150591c60SAlexey Khoroshilov status = rsi_init_usb_interface(adapter, pfunction); 80250591c60SAlexey Khoroshilov if (status) { 803dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n", 804dad0d04fSFariya Fatima __func__); 805dad0d04fSFariya Fatima goto err; 806dad0d04fSFariya Fatima } 807dad0d04fSFariya Fatima 808dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Initialized os intf ops\n", __func__); 809dad0d04fSFariya Fatima 81039e50f5cSJohan Hovold if (id->idProduct == RSI_USB_PID_9113) { 81154fdb318SSiva Rebbagondla rsi_dbg(INIT_ZONE, "%s: 9113 module detected\n", __func__); 81254fdb318SSiva Rebbagondla adapter->device_model = RSI_DEV_9113; 81339e50f5cSJohan Hovold } else if (id->idProduct == RSI_USB_PID_9116) { 81454fdb318SSiva Rebbagondla rsi_dbg(INIT_ZONE, "%s: 9116 module detected\n", __func__); 81554fdb318SSiva Rebbagondla adapter->device_model = RSI_DEV_9116; 81654fdb318SSiva Rebbagondla } else { 81754fdb318SSiva Rebbagondla rsi_dbg(ERR_ZONE, "%s: Unsupported RSI device id 0x%x\n", 818c5dcf8f0SJohan Hovold __func__, id->idProduct); 81954fdb318SSiva Rebbagondla goto err1; 82054fdb318SSiva Rebbagondla } 82154fdb318SSiva Rebbagondla 822dad0d04fSFariya Fatima dev = (struct rsi_91x_usbdev *)adapter->rsi_dev; 823dad0d04fSFariya Fatima 82450591c60SAlexey Khoroshilov status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2); 825b78e91bcSPrameela Rani Garnepudi if (status < 0) 826dad0d04fSFariya Fatima goto err1; 827dad0d04fSFariya Fatima else 828dad0d04fSFariya Fatima fw_status &= 1; 829dad0d04fSFariya Fatima 830dad0d04fSFariya Fatima if (!fw_status) { 831b78e91bcSPrameela Rani Garnepudi rsi_dbg(INIT_ZONE, "Loading firmware...\n"); 832b78e91bcSPrameela Rani Garnepudi status = rsi_hal_device_init(adapter); 83350591c60SAlexey Khoroshilov if (status) { 834dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed in device init\n", 835dad0d04fSFariya Fatima __func__); 836dad0d04fSFariya Fatima goto err1; 837dad0d04fSFariya Fatima } 838b78e91bcSPrameela Rani Garnepudi rsi_dbg(INIT_ZONE, "%s: Device Init Done\n", __func__); 839dad0d04fSFariya Fatima } 840dad0d04fSFariya Fatima 841b9b9f9feSJohan Hovold status = rsi_rx_urb_submit(adapter, WLAN_EP, GFP_KERNEL); 84250591c60SAlexey Khoroshilov if (status) 843dad0d04fSFariya Fatima goto err1; 844dad0d04fSFariya Fatima 845a4302bffSSiva Rebbagondla if (adapter->priv->coex_mode > 1) { 846b9b9f9feSJohan Hovold status = rsi_rx_urb_submit(adapter, BT_EP, GFP_KERNEL); 847a4302bffSSiva Rebbagondla if (status) 848e93cd351SJohan Hovold goto err_kill_wlan_urb; 849a4302bffSSiva Rebbagondla } 850a4302bffSSiva Rebbagondla 851dad0d04fSFariya Fatima return 0; 852e93cd351SJohan Hovold 853e93cd351SJohan Hovold err_kill_wlan_urb: 854e93cd351SJohan Hovold rsi_rx_urb_kill(adapter, WLAN_EP); 855dad0d04fSFariya Fatima err1: 856dad0d04fSFariya Fatima rsi_deinit_usb_interface(adapter); 857dad0d04fSFariya Fatima err: 858dad0d04fSFariya Fatima rsi_91x_deinit(adapter); 859dad0d04fSFariya Fatima rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__); 86050591c60SAlexey Khoroshilov return status; 861dad0d04fSFariya Fatima } 862dad0d04fSFariya Fatima 863dad0d04fSFariya Fatima /** 864dad0d04fSFariya Fatima * rsi_disconnect() - This function performs the reverse of the probe function, 8651cce2000SMasahiro Yamada * it deinitialize the driver structure. 866dad0d04fSFariya Fatima * @pfunction: Pointer to the USB interface structure. 867dad0d04fSFariya Fatima * 868dad0d04fSFariya Fatima * Return: None. 869dad0d04fSFariya Fatima */ 870dad0d04fSFariya Fatima static void rsi_disconnect(struct usb_interface *pfunction) 871dad0d04fSFariya Fatima { 872dad0d04fSFariya Fatima struct rsi_hw *adapter = usb_get_intfdata(pfunction); 873dad0d04fSFariya Fatima 874dad0d04fSFariya Fatima if (!adapter) 875dad0d04fSFariya Fatima return; 876dad0d04fSFariya Fatima 877dad0d04fSFariya Fatima rsi_mac80211_detach(adapter); 878cbde979bSSiva Rebbagondla 879cbde979bSSiva Rebbagondla if (IS_ENABLED(CONFIG_RSI_COEX) && adapter->priv->coex_mode > 1 && 880cbde979bSSiva Rebbagondla adapter->priv->bt_adapter) { 881cbde979bSSiva Rebbagondla rsi_bt_ops.detach(adapter->priv->bt_adapter); 882cbde979bSSiva Rebbagondla adapter->priv->bt_adapter = NULL; 883cbde979bSSiva Rebbagondla } 884cbde979bSSiva Rebbagondla 885e93cd351SJohan Hovold if (adapter->priv->coex_mode > 1) 886e93cd351SJohan Hovold rsi_rx_urb_kill(adapter, BT_EP); 887e93cd351SJohan Hovold rsi_rx_urb_kill(adapter, WLAN_EP); 888e93cd351SJohan Hovold 889ea0676c4SKarun Eagalapati rsi_reset_card(adapter); 890dad0d04fSFariya Fatima rsi_deinit_usb_interface(adapter); 891dad0d04fSFariya Fatima rsi_91x_deinit(adapter); 892dad0d04fSFariya Fatima 893dad0d04fSFariya Fatima rsi_dbg(INFO_ZONE, "%s: Deinitialization completed\n", __func__); 894dad0d04fSFariya Fatima } 895dad0d04fSFariya Fatima 896dad0d04fSFariya Fatima #ifdef CONFIG_PM 897dad0d04fSFariya Fatima static int rsi_suspend(struct usb_interface *intf, pm_message_t message) 898dad0d04fSFariya Fatima { 899dad0d04fSFariya Fatima /* Not yet implemented */ 900dad0d04fSFariya Fatima return -ENOSYS; 901dad0d04fSFariya Fatima } 902dad0d04fSFariya Fatima 903dad0d04fSFariya Fatima static int rsi_resume(struct usb_interface *intf) 904dad0d04fSFariya Fatima { 905dad0d04fSFariya Fatima /* Not yet implemented */ 906dad0d04fSFariya Fatima return -ENOSYS; 907dad0d04fSFariya Fatima } 908dad0d04fSFariya Fatima #endif 909dad0d04fSFariya Fatima 910dad0d04fSFariya Fatima static const struct usb_device_id rsi_dev_table[] = { 91154fdb318SSiva Rebbagondla { USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9113) }, 91254fdb318SSiva Rebbagondla { USB_DEVICE(RSI_USB_VENDOR_ID, RSI_USB_PID_9116) }, 913dad0d04fSFariya Fatima { /* Blank */}, 914dad0d04fSFariya Fatima }; 915dad0d04fSFariya Fatima 916dad0d04fSFariya Fatima static struct usb_driver rsi_driver = { 917dad0d04fSFariya Fatima .name = "RSI-USB WLAN", 918dad0d04fSFariya Fatima .probe = rsi_probe, 919dad0d04fSFariya Fatima .disconnect = rsi_disconnect, 920dad0d04fSFariya Fatima .id_table = rsi_dev_table, 921dad0d04fSFariya Fatima #ifdef CONFIG_PM 922dad0d04fSFariya Fatima .suspend = rsi_suspend, 923dad0d04fSFariya Fatima .resume = rsi_resume, 924dad0d04fSFariya Fatima #endif 925dad0d04fSFariya Fatima }; 926dad0d04fSFariya Fatima 927b101426aSAlexey Khoroshilov module_usb_driver(rsi_driver); 928dad0d04fSFariya Fatima 929dad0d04fSFariya Fatima MODULE_AUTHOR("Redpine Signals Inc"); 930dad0d04fSFariya Fatima MODULE_DESCRIPTION("Common USB layer for RSI drivers"); 931dad0d04fSFariya Fatima MODULE_SUPPORTED_DEVICE("RSI-91x"); 932dad0d04fSFariya Fatima MODULE_DEVICE_TABLE(usb, rsi_dev_table); 933dad0d04fSFariya Fatima MODULE_FIRMWARE(FIRMWARE_RSI9113); 934dad0d04fSFariya Fatima MODULE_VERSION("0.1"); 935dad0d04fSFariya Fatima MODULE_LICENSE("Dual BSD/GPL"); 936