18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Remote VUB300 SDIO/SDmem Host Controller Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Elan Digital Systems Limited 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * based on USB Skeleton driver - 2.2 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * VUB300: is a USB 2.0 client device with a single SDIO/SDmem/MMC slot 128c2ecf20Sopenharmony_ci * Any SDIO/SDmem/MMC device plugged into the VUB300 will appear, 138c2ecf20Sopenharmony_ci * by virtue of this driver, to have been plugged into a local 148c2ecf20Sopenharmony_ci * SDIO host controller, similar to, say, a PCI Ricoh controller 158c2ecf20Sopenharmony_ci * This is because this kernel device driver is both a USB 2.0 168c2ecf20Sopenharmony_ci * client device driver AND an MMC host controller driver. Thus 178c2ecf20Sopenharmony_ci * if there is an existing driver for the inserted SDIO/SDmem/MMC 188c2ecf20Sopenharmony_ci * device then that driver will be used by the kernel to manage 198c2ecf20Sopenharmony_ci * the device in exactly the same fashion as if it had been 208c2ecf20Sopenharmony_ci * directly plugged into, say, a local pci bus Ricoh controller 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * RANT: this driver was written using a display 128x48 - converting it 238c2ecf20Sopenharmony_ci * to a line width of 80 makes it very difficult to support. In 248c2ecf20Sopenharmony_ci * particular functions have been broken down into sub functions 258c2ecf20Sopenharmony_ci * and the original meaningful names have been shortened into 268c2ecf20Sopenharmony_ci * cryptic ones. 278c2ecf20Sopenharmony_ci * The problem is that executing a fragment of code subject to 288c2ecf20Sopenharmony_ci * two conditions means an indentation of 24, thus leaving only 298c2ecf20Sopenharmony_ci * 56 characters for a C statement. And that is quite ridiculous! 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Data types: data passed to/from the VUB300 is fixed to a number of 328c2ecf20Sopenharmony_ci * bits and driver data fields reflect that limit by using 338c2ecf20Sopenharmony_ci * u8, u16, u32 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#include <linux/kernel.h> 368c2ecf20Sopenharmony_ci#include <linux/errno.h> 378c2ecf20Sopenharmony_ci#include <linux/init.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci#include <linux/module.h> 408c2ecf20Sopenharmony_ci#include <linux/kref.h> 418c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 428c2ecf20Sopenharmony_ci#include <linux/usb.h> 438c2ecf20Sopenharmony_ci#include <linux/mutex.h> 448c2ecf20Sopenharmony_ci#include <linux/mmc/host.h> 458c2ecf20Sopenharmony_ci#include <linux/mmc/card.h> 468c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h> 478c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_ids.h> 488c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 498c2ecf20Sopenharmony_ci#include <linux/ctype.h> 508c2ecf20Sopenharmony_ci#include <linux/firmware.h> 518c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct host_controller_info { 548c2ecf20Sopenharmony_ci u8 info_size; 558c2ecf20Sopenharmony_ci u16 firmware_version; 568c2ecf20Sopenharmony_ci u8 number_of_ports; 578c2ecf20Sopenharmony_ci} __packed; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define FIRMWARE_BLOCK_BOUNDARY 1024 608c2ecf20Sopenharmony_cistruct sd_command_header { 618c2ecf20Sopenharmony_ci u8 header_size; 628c2ecf20Sopenharmony_ci u8 header_type; 638c2ecf20Sopenharmony_ci u8 port_number; 648c2ecf20Sopenharmony_ci u8 command_type; /* Bit7 - Rd/Wr */ 658c2ecf20Sopenharmony_ci u8 command_index; 668c2ecf20Sopenharmony_ci u8 transfer_size[4]; /* ReadSize + ReadSize */ 678c2ecf20Sopenharmony_ci u8 response_type; 688c2ecf20Sopenharmony_ci u8 arguments[4]; 698c2ecf20Sopenharmony_ci u8 block_count[2]; 708c2ecf20Sopenharmony_ci u8 block_size[2]; 718c2ecf20Sopenharmony_ci u8 block_boundary[2]; 728c2ecf20Sopenharmony_ci u8 reserved[44]; /* to pad out to 64 bytes */ 738c2ecf20Sopenharmony_ci} __packed; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct sd_irqpoll_header { 768c2ecf20Sopenharmony_ci u8 header_size; 778c2ecf20Sopenharmony_ci u8 header_type; 788c2ecf20Sopenharmony_ci u8 port_number; 798c2ecf20Sopenharmony_ci u8 command_type; /* Bit7 - Rd/Wr */ 808c2ecf20Sopenharmony_ci u8 padding[16]; /* don't ask why !! */ 818c2ecf20Sopenharmony_ci u8 poll_timeout_msb; 828c2ecf20Sopenharmony_ci u8 poll_timeout_lsb; 838c2ecf20Sopenharmony_ci u8 reserved[42]; /* to pad out to 64 bytes */ 848c2ecf20Sopenharmony_ci} __packed; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct sd_common_header { 878c2ecf20Sopenharmony_ci u8 header_size; 888c2ecf20Sopenharmony_ci u8 header_type; 898c2ecf20Sopenharmony_ci u8 port_number; 908c2ecf20Sopenharmony_ci} __packed; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct sd_response_header { 938c2ecf20Sopenharmony_ci u8 header_size; 948c2ecf20Sopenharmony_ci u8 header_type; 958c2ecf20Sopenharmony_ci u8 port_number; 968c2ecf20Sopenharmony_ci u8 command_type; 978c2ecf20Sopenharmony_ci u8 command_index; 988c2ecf20Sopenharmony_ci u8 command_response[]; 998c2ecf20Sopenharmony_ci} __packed; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistruct sd_status_header { 1028c2ecf20Sopenharmony_ci u8 header_size; 1038c2ecf20Sopenharmony_ci u8 header_type; 1048c2ecf20Sopenharmony_ci u8 port_number; 1058c2ecf20Sopenharmony_ci u16 port_flags; 1068c2ecf20Sopenharmony_ci u32 sdio_clock; 1078c2ecf20Sopenharmony_ci u16 host_header_size; 1088c2ecf20Sopenharmony_ci u16 func_header_size; 1098c2ecf20Sopenharmony_ci u16 ctrl_header_size; 1108c2ecf20Sopenharmony_ci} __packed; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistruct sd_error_header { 1138c2ecf20Sopenharmony_ci u8 header_size; 1148c2ecf20Sopenharmony_ci u8 header_type; 1158c2ecf20Sopenharmony_ci u8 port_number; 1168c2ecf20Sopenharmony_ci u8 error_code; 1178c2ecf20Sopenharmony_ci} __packed; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistruct sd_interrupt_header { 1208c2ecf20Sopenharmony_ci u8 header_size; 1218c2ecf20Sopenharmony_ci u8 header_type; 1228c2ecf20Sopenharmony_ci u8 port_number; 1238c2ecf20Sopenharmony_ci} __packed; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistruct offload_registers_access { 1268c2ecf20Sopenharmony_ci u8 command_byte[4]; 1278c2ecf20Sopenharmony_ci u8 Respond_Byte[4]; 1288c2ecf20Sopenharmony_ci} __packed; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define INTERRUPT_REGISTER_ACCESSES 15 1318c2ecf20Sopenharmony_cistruct sd_offloaded_interrupt { 1328c2ecf20Sopenharmony_ci u8 header_size; 1338c2ecf20Sopenharmony_ci u8 header_type; 1348c2ecf20Sopenharmony_ci u8 port_number; 1358c2ecf20Sopenharmony_ci struct offload_registers_access reg[INTERRUPT_REGISTER_ACCESSES]; 1368c2ecf20Sopenharmony_ci} __packed; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistruct sd_register_header { 1398c2ecf20Sopenharmony_ci u8 header_size; 1408c2ecf20Sopenharmony_ci u8 header_type; 1418c2ecf20Sopenharmony_ci u8 port_number; 1428c2ecf20Sopenharmony_ci u8 command_type; 1438c2ecf20Sopenharmony_ci u8 command_index; 1448c2ecf20Sopenharmony_ci u8 command_response[6]; 1458c2ecf20Sopenharmony_ci} __packed; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define PIGGYBACK_REGISTER_ACCESSES 14 1488c2ecf20Sopenharmony_cistruct sd_offloaded_piggyback { 1498c2ecf20Sopenharmony_ci struct sd_register_header sdio; 1508c2ecf20Sopenharmony_ci struct offload_registers_access reg[PIGGYBACK_REGISTER_ACCESSES]; 1518c2ecf20Sopenharmony_ci} __packed; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciunion sd_response { 1548c2ecf20Sopenharmony_ci struct sd_common_header common; 1558c2ecf20Sopenharmony_ci struct sd_status_header status; 1568c2ecf20Sopenharmony_ci struct sd_error_header error; 1578c2ecf20Sopenharmony_ci struct sd_interrupt_header interrupt; 1588c2ecf20Sopenharmony_ci struct sd_response_header response; 1598c2ecf20Sopenharmony_ci struct sd_offloaded_interrupt irq; 1608c2ecf20Sopenharmony_ci struct sd_offloaded_piggyback pig; 1618c2ecf20Sopenharmony_ci} __packed; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciunion sd_command { 1648c2ecf20Sopenharmony_ci struct sd_command_header head; 1658c2ecf20Sopenharmony_ci struct sd_irqpoll_header poll; 1668c2ecf20Sopenharmony_ci} __packed; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cienum SD_RESPONSE_TYPE { 1698c2ecf20Sopenharmony_ci SDRT_UNSPECIFIED = 0, 1708c2ecf20Sopenharmony_ci SDRT_NONE, 1718c2ecf20Sopenharmony_ci SDRT_1, 1728c2ecf20Sopenharmony_ci SDRT_1B, 1738c2ecf20Sopenharmony_ci SDRT_2, 1748c2ecf20Sopenharmony_ci SDRT_3, 1758c2ecf20Sopenharmony_ci SDRT_4, 1768c2ecf20Sopenharmony_ci SDRT_5, 1778c2ecf20Sopenharmony_ci SDRT_5B, 1788c2ecf20Sopenharmony_ci SDRT_6, 1798c2ecf20Sopenharmony_ci SDRT_7, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define RESPONSE_INTERRUPT 0x01 1838c2ecf20Sopenharmony_ci#define RESPONSE_ERROR 0x02 1848c2ecf20Sopenharmony_ci#define RESPONSE_STATUS 0x03 1858c2ecf20Sopenharmony_ci#define RESPONSE_IRQ_DISABLED 0x05 1868c2ecf20Sopenharmony_ci#define RESPONSE_IRQ_ENABLED 0x06 1878c2ecf20Sopenharmony_ci#define RESPONSE_PIGGYBACKED 0x07 1888c2ecf20Sopenharmony_ci#define RESPONSE_NO_INTERRUPT 0x08 1898c2ecf20Sopenharmony_ci#define RESPONSE_PIG_DISABLED 0x09 1908c2ecf20Sopenharmony_ci#define RESPONSE_PIG_ENABLED 0x0A 1918c2ecf20Sopenharmony_ci#define SD_ERROR_1BIT_TIMEOUT 0x01 1928c2ecf20Sopenharmony_ci#define SD_ERROR_4BIT_TIMEOUT 0x02 1938c2ecf20Sopenharmony_ci#define SD_ERROR_1BIT_CRC_WRONG 0x03 1948c2ecf20Sopenharmony_ci#define SD_ERROR_4BIT_CRC_WRONG 0x04 1958c2ecf20Sopenharmony_ci#define SD_ERROR_1BIT_CRC_ERROR 0x05 1968c2ecf20Sopenharmony_ci#define SD_ERROR_4BIT_CRC_ERROR 0x06 1978c2ecf20Sopenharmony_ci#define SD_ERROR_NO_CMD_ENDBIT 0x07 1988c2ecf20Sopenharmony_ci#define SD_ERROR_NO_1BIT_DATEND 0x08 1998c2ecf20Sopenharmony_ci#define SD_ERROR_NO_4BIT_DATEND 0x09 2008c2ecf20Sopenharmony_ci#define SD_ERROR_1BIT_UNEXPECTED_TIMEOUT 0x0A 2018c2ecf20Sopenharmony_ci#define SD_ERROR_4BIT_UNEXPECTED_TIMEOUT 0x0B 2028c2ecf20Sopenharmony_ci#define SD_ERROR_ILLEGAL_COMMAND 0x0C 2038c2ecf20Sopenharmony_ci#define SD_ERROR_NO_DEVICE 0x0D 2048c2ecf20Sopenharmony_ci#define SD_ERROR_TRANSFER_LENGTH 0x0E 2058c2ecf20Sopenharmony_ci#define SD_ERROR_1BIT_DATA_TIMEOUT 0x0F 2068c2ecf20Sopenharmony_ci#define SD_ERROR_4BIT_DATA_TIMEOUT 0x10 2078c2ecf20Sopenharmony_ci#define SD_ERROR_ILLEGAL_STATE 0x11 2088c2ecf20Sopenharmony_ci#define SD_ERROR_UNKNOWN_ERROR 0x12 2098c2ecf20Sopenharmony_ci#define SD_ERROR_RESERVED_ERROR 0x13 2108c2ecf20Sopenharmony_ci#define SD_ERROR_INVALID_FUNCTION 0x14 2118c2ecf20Sopenharmony_ci#define SD_ERROR_OUT_OF_RANGE 0x15 2128c2ecf20Sopenharmony_ci#define SD_ERROR_STAT_CMD 0x16 2138c2ecf20Sopenharmony_ci#define SD_ERROR_STAT_DATA 0x17 2148c2ecf20Sopenharmony_ci#define SD_ERROR_STAT_CMD_TIMEOUT 0x18 2158c2ecf20Sopenharmony_ci#define SD_ERROR_SDCRDY_STUCK 0x19 2168c2ecf20Sopenharmony_ci#define SD_ERROR_UNHANDLED 0x1A 2178c2ecf20Sopenharmony_ci#define SD_ERROR_OVERRUN 0x1B 2188c2ecf20Sopenharmony_ci#define SD_ERROR_PIO_TIMEOUT 0x1C 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#define FUN(c) (0x000007 & (c->arg>>28)) 2218c2ecf20Sopenharmony_ci#define REG(c) (0x01FFFF & (c->arg>>9)) 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic bool limit_speed_to_24_MHz; 2248c2ecf20Sopenharmony_cimodule_param(limit_speed_to_24_MHz, bool, 0644); 2258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(limit_speed_to_24_MHz, "Limit Max SDIO Clock Speed to 24 MHz"); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic bool pad_input_to_usb_pkt; 2288c2ecf20Sopenharmony_cimodule_param(pad_input_to_usb_pkt, bool, 0644); 2298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pad_input_to_usb_pkt, 2308c2ecf20Sopenharmony_ci "Pad USB data input transfers to whole USB Packet"); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic bool disable_offload_processing; 2338c2ecf20Sopenharmony_cimodule_param(disable_offload_processing, bool, 0644); 2348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(disable_offload_processing, "Disable Offload Processing"); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic bool force_1_bit_data_xfers; 2378c2ecf20Sopenharmony_cimodule_param(force_1_bit_data_xfers, bool, 0644); 2388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_1_bit_data_xfers, 2398c2ecf20Sopenharmony_ci "Force SDIO Data Transfers to 1-bit Mode"); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic bool force_polling_for_irqs; 2428c2ecf20Sopenharmony_cimodule_param(force_polling_for_irqs, bool, 0644); 2438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_polling_for_irqs, "Force Polling for SDIO interrupts"); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int firmware_irqpoll_timeout = 1024; 2468c2ecf20Sopenharmony_cimodule_param(firmware_irqpoll_timeout, int, 0644); 2478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(firmware_irqpoll_timeout, "VUB300 firmware irqpoll timeout"); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int force_max_req_size = 128; 2508c2ecf20Sopenharmony_cimodule_param(force_max_req_size, int, 0644); 2518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_max_req_size, "set max request size in kBytes"); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci#ifdef SMSC_DEVELOPMENT_BOARD 2548c2ecf20Sopenharmony_cistatic int firmware_rom_wait_states = 0x04; 2558c2ecf20Sopenharmony_ci#else 2568c2ecf20Sopenharmony_cistatic int firmware_rom_wait_states = 0x1C; 2578c2ecf20Sopenharmony_ci#endif 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cimodule_param(firmware_rom_wait_states, int, 0644); 2608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(firmware_rom_wait_states, 2618c2ecf20Sopenharmony_ci "ROM wait states byte=RRRIIEEE (Reserved Internal External)"); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define ELAN_VENDOR_ID 0x2201 2648c2ecf20Sopenharmony_ci#define VUB300_VENDOR_ID 0x0424 2658c2ecf20Sopenharmony_ci#define VUB300_PRODUCT_ID 0x012C 2668c2ecf20Sopenharmony_cistatic const struct usb_device_id vub300_table[] = { 2678c2ecf20Sopenharmony_ci {USB_DEVICE(ELAN_VENDOR_ID, VUB300_PRODUCT_ID)}, 2688c2ecf20Sopenharmony_ci {USB_DEVICE(VUB300_VENDOR_ID, VUB300_PRODUCT_ID)}, 2698c2ecf20Sopenharmony_ci {} /* Terminating entry */ 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, vub300_table); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic struct workqueue_struct *cmndworkqueue; 2748c2ecf20Sopenharmony_cistatic struct workqueue_struct *pollworkqueue; 2758c2ecf20Sopenharmony_cistatic struct workqueue_struct *deadworkqueue; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic inline int interface_to_InterfaceNumber(struct usb_interface *interface) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci if (!interface) 2808c2ecf20Sopenharmony_ci return -1; 2818c2ecf20Sopenharmony_ci if (!interface->cur_altsetting) 2828c2ecf20Sopenharmony_ci return -1; 2838c2ecf20Sopenharmony_ci return interface->cur_altsetting->desc.bInterfaceNumber; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistruct sdio_register { 2878c2ecf20Sopenharmony_ci unsigned func_num:3; 2888c2ecf20Sopenharmony_ci unsigned sdio_reg:17; 2898c2ecf20Sopenharmony_ci unsigned activate:1; 2908c2ecf20Sopenharmony_ci unsigned prepared:1; 2918c2ecf20Sopenharmony_ci unsigned regvalue:8; 2928c2ecf20Sopenharmony_ci unsigned response:8; 2938c2ecf20Sopenharmony_ci unsigned sparebit:26; 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistruct vub300_mmc_host { 2978c2ecf20Sopenharmony_ci struct usb_device *udev; 2988c2ecf20Sopenharmony_ci struct usb_interface *interface; 2998c2ecf20Sopenharmony_ci struct kref kref; 3008c2ecf20Sopenharmony_ci struct mutex cmd_mutex; 3018c2ecf20Sopenharmony_ci struct mutex irq_mutex; 3028c2ecf20Sopenharmony_ci char vub_name[3 + (9 * 8) + 4 + 1]; /* max of 7 sdio fn's */ 3038c2ecf20Sopenharmony_ci u8 cmnd_out_ep; /* EndPoint for commands */ 3048c2ecf20Sopenharmony_ci u8 cmnd_res_ep; /* EndPoint for responses */ 3058c2ecf20Sopenharmony_ci u8 data_out_ep; /* EndPoint for out data */ 3068c2ecf20Sopenharmony_ci u8 data_inp_ep; /* EndPoint for inp data */ 3078c2ecf20Sopenharmony_ci bool card_powered; 3088c2ecf20Sopenharmony_ci bool card_present; 3098c2ecf20Sopenharmony_ci bool read_only; 3108c2ecf20Sopenharmony_ci bool large_usb_packets; 3118c2ecf20Sopenharmony_ci bool app_spec; /* ApplicationSpecific */ 3128c2ecf20Sopenharmony_ci bool irq_enabled; /* by the MMC CORE */ 3138c2ecf20Sopenharmony_ci bool irq_disabled; /* in the firmware */ 3148c2ecf20Sopenharmony_ci unsigned bus_width:4; 3158c2ecf20Sopenharmony_ci u8 total_offload_count; 3168c2ecf20Sopenharmony_ci u8 dynamic_register_count; 3178c2ecf20Sopenharmony_ci u8 resp_len; 3188c2ecf20Sopenharmony_ci u32 datasize; 3198c2ecf20Sopenharmony_ci int errors; 3208c2ecf20Sopenharmony_ci int usb_transport_fail; 3218c2ecf20Sopenharmony_ci int usb_timed_out; 3228c2ecf20Sopenharmony_ci int irqs_queued; 3238c2ecf20Sopenharmony_ci struct sdio_register sdio_register[16]; 3248c2ecf20Sopenharmony_ci struct offload_interrupt_function_register { 3258c2ecf20Sopenharmony_ci#define MAXREGBITS 4 3268c2ecf20Sopenharmony_ci#define MAXREGS (1<<MAXREGBITS) 3278c2ecf20Sopenharmony_ci#define MAXREGMASK (MAXREGS-1) 3288c2ecf20Sopenharmony_ci u8 offload_count; 3298c2ecf20Sopenharmony_ci u32 offload_point; 3308c2ecf20Sopenharmony_ci struct offload_registers_access reg[MAXREGS]; 3318c2ecf20Sopenharmony_ci } fn[8]; 3328c2ecf20Sopenharmony_ci u16 fbs[8]; /* Function Block Size */ 3338c2ecf20Sopenharmony_ci struct mmc_command *cmd; 3348c2ecf20Sopenharmony_ci struct mmc_request *req; 3358c2ecf20Sopenharmony_ci struct mmc_data *data; 3368c2ecf20Sopenharmony_ci struct mmc_host *mmc; 3378c2ecf20Sopenharmony_ci struct urb *urb; 3388c2ecf20Sopenharmony_ci struct urb *command_out_urb; 3398c2ecf20Sopenharmony_ci struct urb *command_res_urb; 3408c2ecf20Sopenharmony_ci struct completion command_complete; 3418c2ecf20Sopenharmony_ci struct completion irqpoll_complete; 3428c2ecf20Sopenharmony_ci union sd_command cmnd; 3438c2ecf20Sopenharmony_ci union sd_response resp; 3448c2ecf20Sopenharmony_ci struct timer_list sg_transfer_timer; 3458c2ecf20Sopenharmony_ci struct usb_sg_request sg_request; 3468c2ecf20Sopenharmony_ci struct timer_list inactivity_timer; 3478c2ecf20Sopenharmony_ci struct work_struct deadwork; 3488c2ecf20Sopenharmony_ci struct work_struct cmndwork; 3498c2ecf20Sopenharmony_ci struct delayed_work pollwork; 3508c2ecf20Sopenharmony_ci struct host_controller_info hc_info; 3518c2ecf20Sopenharmony_ci struct sd_status_header system_port_status; 3528c2ecf20Sopenharmony_ci u8 padded_buffer[64]; 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci#define kref_to_vub300_mmc_host(d) container_of(d, struct vub300_mmc_host, kref) 3568c2ecf20Sopenharmony_ci#define SET_TRANSFER_PSEUDOCODE 21 3578c2ecf20Sopenharmony_ci#define SET_INTERRUPT_PSEUDOCODE 20 3588c2ecf20Sopenharmony_ci#define SET_FAILURE_MODE 18 3598c2ecf20Sopenharmony_ci#define SET_ROM_WAIT_STATES 16 3608c2ecf20Sopenharmony_ci#define SET_IRQ_ENABLE 13 3618c2ecf20Sopenharmony_ci#define SET_CLOCK_SPEED 11 3628c2ecf20Sopenharmony_ci#define SET_FUNCTION_BLOCK_SIZE 9 3638c2ecf20Sopenharmony_ci#define SET_SD_DATA_MODE 6 3648c2ecf20Sopenharmony_ci#define SET_SD_POWER 4 3658c2ecf20Sopenharmony_ci#define ENTER_DFU_MODE 3 3668c2ecf20Sopenharmony_ci#define GET_HC_INF0 1 3678c2ecf20Sopenharmony_ci#define GET_SYSTEM_PORT_STATUS 0 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void vub300_delete(struct kref *kref) 3708c2ecf20Sopenharmony_ci{ /* kref callback - softirq */ 3718c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref); 3728c2ecf20Sopenharmony_ci struct mmc_host *mmc = vub300->mmc; 3738c2ecf20Sopenharmony_ci usb_free_urb(vub300->command_out_urb); 3748c2ecf20Sopenharmony_ci vub300->command_out_urb = NULL; 3758c2ecf20Sopenharmony_ci usb_free_urb(vub300->command_res_urb); 3768c2ecf20Sopenharmony_ci vub300->command_res_urb = NULL; 3778c2ecf20Sopenharmony_ci usb_put_dev(vub300->udev); 3788c2ecf20Sopenharmony_ci mmc_free_host(mmc); 3798c2ecf20Sopenharmony_ci /* 3808c2ecf20Sopenharmony_ci * and hence also frees vub300 3818c2ecf20Sopenharmony_ci * which is contained at the end of struct mmc 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void vub300_queue_cmnd_work(struct vub300_mmc_host *vub300) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 3888c2ecf20Sopenharmony_ci if (queue_work(cmndworkqueue, &vub300->cmndwork)) { 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * then the cmndworkqueue was not previously 3918c2ecf20Sopenharmony_ci * running and the above get ref is obvious 3928c2ecf20Sopenharmony_ci * required and will be put when the thread 3938c2ecf20Sopenharmony_ci * terminates by a specific call 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci /* 3978c2ecf20Sopenharmony_ci * the cmndworkqueue was already running from 3988c2ecf20Sopenharmony_ci * a previous invocation and thus to keep the 3998c2ecf20Sopenharmony_ci * kref counts correct we must undo the get 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void vub300_queue_poll_work(struct vub300_mmc_host *vub300, int delay) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 4088c2ecf20Sopenharmony_ci if (queue_delayed_work(pollworkqueue, &vub300->pollwork, delay)) { 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * then the pollworkqueue was not previously 4118c2ecf20Sopenharmony_ci * running and the above get ref is obvious 4128c2ecf20Sopenharmony_ci * required and will be put when the thread 4138c2ecf20Sopenharmony_ci * terminates by a specific call 4148c2ecf20Sopenharmony_ci */ 4158c2ecf20Sopenharmony_ci } else { 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * the pollworkqueue was already running from 4188c2ecf20Sopenharmony_ci * a previous invocation and thus to keep the 4198c2ecf20Sopenharmony_ci * kref counts correct we must undo the get 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void vub300_queue_dead_work(struct vub300_mmc_host *vub300) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 4288c2ecf20Sopenharmony_ci if (queue_work(deadworkqueue, &vub300->deadwork)) { 4298c2ecf20Sopenharmony_ci /* 4308c2ecf20Sopenharmony_ci * then the deadworkqueue was not previously 4318c2ecf20Sopenharmony_ci * running and the above get ref is obvious 4328c2ecf20Sopenharmony_ci * required and will be put when the thread 4338c2ecf20Sopenharmony_ci * terminates by a specific call 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci } else { 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * the deadworkqueue was already running from 4388c2ecf20Sopenharmony_ci * a previous invocation and thus to keep the 4398c2ecf20Sopenharmony_ci * kref counts correct we must undo the get 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic void irqpoll_res_completed(struct urb *urb) 4468c2ecf20Sopenharmony_ci{ /* urb completion handler - hardirq */ 4478c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; 4488c2ecf20Sopenharmony_ci if (urb->status) 4498c2ecf20Sopenharmony_ci vub300->usb_transport_fail = urb->status; 4508c2ecf20Sopenharmony_ci complete(&vub300->irqpoll_complete); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void irqpoll_out_completed(struct urb *urb) 4548c2ecf20Sopenharmony_ci{ /* urb completion handler - hardirq */ 4558c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; 4568c2ecf20Sopenharmony_ci if (urb->status) { 4578c2ecf20Sopenharmony_ci vub300->usb_transport_fail = urb->status; 4588c2ecf20Sopenharmony_ci complete(&vub300->irqpoll_complete); 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci int ret; 4628c2ecf20Sopenharmony_ci unsigned int pipe = 4638c2ecf20Sopenharmony_ci usb_rcvbulkpipe(vub300->udev, vub300->cmnd_res_ep); 4648c2ecf20Sopenharmony_ci usb_fill_bulk_urb(vub300->command_res_urb, vub300->udev, pipe, 4658c2ecf20Sopenharmony_ci &vub300->resp, sizeof(vub300->resp), 4668c2ecf20Sopenharmony_ci irqpoll_res_completed, vub300); 4678c2ecf20Sopenharmony_ci vub300->command_res_urb->actual_length = 0; 4688c2ecf20Sopenharmony_ci ret = usb_submit_urb(vub300->command_res_urb, GFP_ATOMIC); 4698c2ecf20Sopenharmony_ci if (ret) { 4708c2ecf20Sopenharmony_ci vub300->usb_transport_fail = ret; 4718c2ecf20Sopenharmony_ci complete(&vub300->irqpoll_complete); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci return; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void send_irqpoll(struct vub300_mmc_host *vub300) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_pollwork_thread */ 4808c2ecf20Sopenharmony_ci int retval; 4818c2ecf20Sopenharmony_ci int timeout = 0xFFFF & (0x0001FFFF - firmware_irqpoll_timeout); 4828c2ecf20Sopenharmony_ci vub300->cmnd.poll.header_size = 22; 4838c2ecf20Sopenharmony_ci vub300->cmnd.poll.header_type = 1; 4848c2ecf20Sopenharmony_ci vub300->cmnd.poll.port_number = 0; 4858c2ecf20Sopenharmony_ci vub300->cmnd.poll.command_type = 2; 4868c2ecf20Sopenharmony_ci vub300->cmnd.poll.poll_timeout_lsb = 0xFF & (unsigned)timeout; 4878c2ecf20Sopenharmony_ci vub300->cmnd.poll.poll_timeout_msb = 0xFF & (unsigned)(timeout >> 8); 4888c2ecf20Sopenharmony_ci usb_fill_bulk_urb(vub300->command_out_urb, vub300->udev, 4898c2ecf20Sopenharmony_ci usb_sndbulkpipe(vub300->udev, vub300->cmnd_out_ep) 4908c2ecf20Sopenharmony_ci , &vub300->cmnd, sizeof(vub300->cmnd) 4918c2ecf20Sopenharmony_ci , irqpoll_out_completed, vub300); 4928c2ecf20Sopenharmony_ci retval = usb_submit_urb(vub300->command_out_urb, GFP_KERNEL); 4938c2ecf20Sopenharmony_ci if (0 > retval) { 4948c2ecf20Sopenharmony_ci vub300->usb_transport_fail = retval; 4958c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 1); 4968c2ecf20Sopenharmony_ci complete(&vub300->irqpoll_complete); 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci } else { 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic void new_system_port_status(struct vub300_mmc_host *vub300) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci int old_card_present = vub300->card_present; 5068c2ecf20Sopenharmony_ci int new_card_present = 5078c2ecf20Sopenharmony_ci (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; 5088c2ecf20Sopenharmony_ci vub300->read_only = 5098c2ecf20Sopenharmony_ci (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; 5108c2ecf20Sopenharmony_ci if (new_card_present && !old_card_present) { 5118c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, "card just inserted\n"); 5128c2ecf20Sopenharmony_ci vub300->card_present = 1; 5138c2ecf20Sopenharmony_ci vub300->bus_width = 0; 5148c2ecf20Sopenharmony_ci if (disable_offload_processing) 5158c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "EMPTY Processing Disabled", 5168c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 5178c2ecf20Sopenharmony_ci else 5188c2ecf20Sopenharmony_ci vub300->vub_name[0] = 0; 5198c2ecf20Sopenharmony_ci mmc_detect_change(vub300->mmc, 1); 5208c2ecf20Sopenharmony_ci } else if (!new_card_present && old_card_present) { 5218c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, "card just ejected\n"); 5228c2ecf20Sopenharmony_ci vub300->card_present = 0; 5238c2ecf20Sopenharmony_ci mmc_detect_change(vub300->mmc, 0); 5248c2ecf20Sopenharmony_ci } else { 5258c2ecf20Sopenharmony_ci /* no change */ 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void __add_offloaded_reg_to_fifo(struct vub300_mmc_host *vub300, 5308c2ecf20Sopenharmony_ci struct offload_registers_access 5318c2ecf20Sopenharmony_ci *register_access, u8 func) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci u8 r = vub300->fn[func].offload_point + vub300->fn[func].offload_count; 5348c2ecf20Sopenharmony_ci memcpy(&vub300->fn[func].reg[MAXREGMASK & r], register_access, 5358c2ecf20Sopenharmony_ci sizeof(struct offload_registers_access)); 5368c2ecf20Sopenharmony_ci vub300->fn[func].offload_count += 1; 5378c2ecf20Sopenharmony_ci vub300->total_offload_count += 1; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic void add_offloaded_reg(struct vub300_mmc_host *vub300, 5418c2ecf20Sopenharmony_ci struct offload_registers_access *register_access) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci u32 Register = ((0x03 & register_access->command_byte[0]) << 15) 5448c2ecf20Sopenharmony_ci | ((0xFF & register_access->command_byte[1]) << 7) 5458c2ecf20Sopenharmony_ci | ((0xFE & register_access->command_byte[2]) >> 1); 5468c2ecf20Sopenharmony_ci u8 func = ((0x70 & register_access->command_byte[0]) >> 4); 5478c2ecf20Sopenharmony_ci u8 regs = vub300->dynamic_register_count; 5488c2ecf20Sopenharmony_ci u8 i = 0; 5498c2ecf20Sopenharmony_ci while (0 < regs-- && 1 == vub300->sdio_register[i].activate) { 5508c2ecf20Sopenharmony_ci if (vub300->sdio_register[i].func_num == func && 5518c2ecf20Sopenharmony_ci vub300->sdio_register[i].sdio_reg == Register) { 5528c2ecf20Sopenharmony_ci if (vub300->sdio_register[i].prepared == 0) 5538c2ecf20Sopenharmony_ci vub300->sdio_register[i].prepared = 1; 5548c2ecf20Sopenharmony_ci vub300->sdio_register[i].response = 5558c2ecf20Sopenharmony_ci register_access->Respond_Byte[2]; 5568c2ecf20Sopenharmony_ci vub300->sdio_register[i].regvalue = 5578c2ecf20Sopenharmony_ci register_access->Respond_Byte[3]; 5588c2ecf20Sopenharmony_ci return; 5598c2ecf20Sopenharmony_ci } else { 5608c2ecf20Sopenharmony_ci i += 1; 5618c2ecf20Sopenharmony_ci continue; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci __add_offloaded_reg_to_fifo(vub300, register_access, func); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic void check_vub300_port_status(struct vub300_mmc_host *vub300) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * cmd_mutex is held by vub300_pollwork_thread, 5718c2ecf20Sopenharmony_ci * vub300_deadwork_thread or vub300_cmndwork_thread 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci int retval; 5748c2ecf20Sopenharmony_ci retval = 5758c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), 5768c2ecf20Sopenharmony_ci GET_SYSTEM_PORT_STATUS, 5778c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 5788c2ecf20Sopenharmony_ci 0x0000, 0x0000, &vub300->system_port_status, 5798c2ecf20Sopenharmony_ci sizeof(vub300->system_port_status), 1000); 5808c2ecf20Sopenharmony_ci if (sizeof(vub300->system_port_status) == retval) 5818c2ecf20Sopenharmony_ci new_system_port_status(vub300); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void __vub300_irqpoll_response(struct vub300_mmc_host *vub300) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_pollwork_thread */ 5878c2ecf20Sopenharmony_ci if (vub300->command_res_urb->actual_length == 0) 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci switch (vub300->resp.common.header_type) { 5918c2ecf20Sopenharmony_ci case RESPONSE_INTERRUPT: 5928c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 5938c2ecf20Sopenharmony_ci if (vub300->irq_enabled) 5948c2ecf20Sopenharmony_ci mmc_signal_sdio_irq(vub300->mmc); 5958c2ecf20Sopenharmony_ci else 5968c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 5978c2ecf20Sopenharmony_ci vub300->irq_disabled = 1; 5988c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci case RESPONSE_ERROR: 6018c2ecf20Sopenharmony_ci if (vub300->resp.error.error_code == SD_ERROR_NO_DEVICE) 6028c2ecf20Sopenharmony_ci check_vub300_port_status(vub300); 6038c2ecf20Sopenharmony_ci break; 6048c2ecf20Sopenharmony_ci case RESPONSE_STATUS: 6058c2ecf20Sopenharmony_ci vub300->system_port_status = vub300->resp.status; 6068c2ecf20Sopenharmony_ci new_system_port_status(vub300); 6078c2ecf20Sopenharmony_ci if (!vub300->card_present) 6088c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, HZ / 5); 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case RESPONSE_IRQ_DISABLED: 6118c2ecf20Sopenharmony_ci { 6128c2ecf20Sopenharmony_ci int offloaded_data_length = vub300->resp.common.header_size - 3; 6138c2ecf20Sopenharmony_ci int register_count = offloaded_data_length >> 3; 6148c2ecf20Sopenharmony_ci int ri = 0; 6158c2ecf20Sopenharmony_ci while (register_count--) { 6168c2ecf20Sopenharmony_ci add_offloaded_reg(vub300, &vub300->resp.irq.reg[ri]); 6178c2ecf20Sopenharmony_ci ri += 1; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 6208c2ecf20Sopenharmony_ci if (vub300->irq_enabled) 6218c2ecf20Sopenharmony_ci mmc_signal_sdio_irq(vub300->mmc); 6228c2ecf20Sopenharmony_ci else 6238c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 6248c2ecf20Sopenharmony_ci vub300->irq_disabled = 1; 6258c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 6268c2ecf20Sopenharmony_ci break; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci case RESPONSE_IRQ_ENABLED: 6298c2ecf20Sopenharmony_ci { 6308c2ecf20Sopenharmony_ci int offloaded_data_length = vub300->resp.common.header_size - 3; 6318c2ecf20Sopenharmony_ci int register_count = offloaded_data_length >> 3; 6328c2ecf20Sopenharmony_ci int ri = 0; 6338c2ecf20Sopenharmony_ci while (register_count--) { 6348c2ecf20Sopenharmony_ci add_offloaded_reg(vub300, &vub300->resp.irq.reg[ri]); 6358c2ecf20Sopenharmony_ci ri += 1; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 6388c2ecf20Sopenharmony_ci if (vub300->irq_enabled) 6398c2ecf20Sopenharmony_ci mmc_signal_sdio_irq(vub300->mmc); 6408c2ecf20Sopenharmony_ci else 6418c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 6428c2ecf20Sopenharmony_ci vub300->irq_disabled = 0; 6438c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci case RESPONSE_NO_INTERRUPT: 6478c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 1); 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci default: 6508c2ecf20Sopenharmony_ci break; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void __do_poll(struct vub300_mmc_host *vub300) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_pollwork_thread */ 6578c2ecf20Sopenharmony_ci unsigned long commretval; 6588c2ecf20Sopenharmony_ci mod_timer(&vub300->inactivity_timer, jiffies + HZ); 6598c2ecf20Sopenharmony_ci init_completion(&vub300->irqpoll_complete); 6608c2ecf20Sopenharmony_ci send_irqpoll(vub300); 6618c2ecf20Sopenharmony_ci commretval = wait_for_completion_timeout(&vub300->irqpoll_complete, 6628c2ecf20Sopenharmony_ci msecs_to_jiffies(500)); 6638c2ecf20Sopenharmony_ci if (vub300->usb_transport_fail) { 6648c2ecf20Sopenharmony_ci /* no need to do anything */ 6658c2ecf20Sopenharmony_ci } else if (commretval == 0) { 6668c2ecf20Sopenharmony_ci vub300->usb_timed_out = 1; 6678c2ecf20Sopenharmony_ci usb_kill_urb(vub300->command_out_urb); 6688c2ecf20Sopenharmony_ci usb_kill_urb(vub300->command_res_urb); 6698c2ecf20Sopenharmony_ci } else { /* commretval > 0 */ 6708c2ecf20Sopenharmony_ci __vub300_irqpoll_response(vub300); 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* this thread runs only when the driver 6758c2ecf20Sopenharmony_ci * is trying to poll the device for an IRQ 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cistatic void vub300_pollwork_thread(struct work_struct *work) 6788c2ecf20Sopenharmony_ci{ /* NOT irq */ 6798c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = container_of(work, 6808c2ecf20Sopenharmony_ci struct vub300_mmc_host, pollwork.work); 6818c2ecf20Sopenharmony_ci if (!vub300->interface) { 6828c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 6838c2ecf20Sopenharmony_ci return; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci mutex_lock(&vub300->cmd_mutex); 6868c2ecf20Sopenharmony_ci if (vub300->cmd) { 6878c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 1); 6888c2ecf20Sopenharmony_ci } else if (!vub300->card_present) { 6898c2ecf20Sopenharmony_ci /* no need to do anything */ 6908c2ecf20Sopenharmony_ci } else { /* vub300->card_present */ 6918c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 6928c2ecf20Sopenharmony_ci if (!vub300->irq_enabled) { 6938c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 6948c2ecf20Sopenharmony_ci } else if (vub300->irqs_queued) { 6958c2ecf20Sopenharmony_ci vub300->irqs_queued -= 1; 6968c2ecf20Sopenharmony_ci mmc_signal_sdio_irq(vub300->mmc); 6978c2ecf20Sopenharmony_ci mod_timer(&vub300->inactivity_timer, jiffies + HZ); 6988c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 6998c2ecf20Sopenharmony_ci } else { /* NOT vub300->irqs_queued */ 7008c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 7018c2ecf20Sopenharmony_ci __do_poll(vub300); 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 7058c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic void vub300_deadwork_thread(struct work_struct *work) 7098c2ecf20Sopenharmony_ci{ /* NOT irq */ 7108c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = 7118c2ecf20Sopenharmony_ci container_of(work, struct vub300_mmc_host, deadwork); 7128c2ecf20Sopenharmony_ci if (!vub300->interface) { 7138c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 7148c2ecf20Sopenharmony_ci return; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci mutex_lock(&vub300->cmd_mutex); 7178c2ecf20Sopenharmony_ci if (vub300->cmd) { 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * a command got in as the inactivity 7208c2ecf20Sopenharmony_ci * timer expired - so we just let the 7218c2ecf20Sopenharmony_ci * processing of the command show if 7228c2ecf20Sopenharmony_ci * the device is dead 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci } else if (vub300->card_present) { 7258c2ecf20Sopenharmony_ci check_vub300_port_status(vub300); 7268c2ecf20Sopenharmony_ci } else if (vub300->mmc && vub300->mmc->card) { 7278c2ecf20Sopenharmony_ci /* 7288c2ecf20Sopenharmony_ci * the MMC core must not have responded 7298c2ecf20Sopenharmony_ci * to the previous indication - lets 7308c2ecf20Sopenharmony_ci * hope that it eventually does so we 7318c2ecf20Sopenharmony_ci * will just ignore this for now 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ci } else { 7348c2ecf20Sopenharmony_ci check_vub300_port_status(vub300); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci mod_timer(&vub300->inactivity_timer, jiffies + HZ); 7378c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 7388c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic void vub300_inactivity_timer_expired(struct timer_list *t) 7428c2ecf20Sopenharmony_ci{ /* softirq */ 7438c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = from_timer(vub300, t, 7448c2ecf20Sopenharmony_ci inactivity_timer); 7458c2ecf20Sopenharmony_ci if (!vub300->interface) { 7468c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 7478c2ecf20Sopenharmony_ci } else if (vub300->cmd) { 7488c2ecf20Sopenharmony_ci mod_timer(&vub300->inactivity_timer, jiffies + HZ); 7498c2ecf20Sopenharmony_ci } else { 7508c2ecf20Sopenharmony_ci vub300_queue_dead_work(vub300); 7518c2ecf20Sopenharmony_ci mod_timer(&vub300->inactivity_timer, jiffies + HZ); 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic int vub300_response_error(u8 error_code) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci switch (error_code) { 7588c2ecf20Sopenharmony_ci case SD_ERROR_PIO_TIMEOUT: 7598c2ecf20Sopenharmony_ci case SD_ERROR_1BIT_TIMEOUT: 7608c2ecf20Sopenharmony_ci case SD_ERROR_4BIT_TIMEOUT: 7618c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7628c2ecf20Sopenharmony_ci case SD_ERROR_STAT_DATA: 7638c2ecf20Sopenharmony_ci case SD_ERROR_OVERRUN: 7648c2ecf20Sopenharmony_ci case SD_ERROR_STAT_CMD: 7658c2ecf20Sopenharmony_ci case SD_ERROR_STAT_CMD_TIMEOUT: 7668c2ecf20Sopenharmony_ci case SD_ERROR_SDCRDY_STUCK: 7678c2ecf20Sopenharmony_ci case SD_ERROR_UNHANDLED: 7688c2ecf20Sopenharmony_ci case SD_ERROR_1BIT_CRC_WRONG: 7698c2ecf20Sopenharmony_ci case SD_ERROR_4BIT_CRC_WRONG: 7708c2ecf20Sopenharmony_ci case SD_ERROR_1BIT_CRC_ERROR: 7718c2ecf20Sopenharmony_ci case SD_ERROR_4BIT_CRC_ERROR: 7728c2ecf20Sopenharmony_ci case SD_ERROR_NO_CMD_ENDBIT: 7738c2ecf20Sopenharmony_ci case SD_ERROR_NO_1BIT_DATEND: 7748c2ecf20Sopenharmony_ci case SD_ERROR_NO_4BIT_DATEND: 7758c2ecf20Sopenharmony_ci case SD_ERROR_1BIT_DATA_TIMEOUT: 7768c2ecf20Sopenharmony_ci case SD_ERROR_4BIT_DATA_TIMEOUT: 7778c2ecf20Sopenharmony_ci case SD_ERROR_1BIT_UNEXPECTED_TIMEOUT: 7788c2ecf20Sopenharmony_ci case SD_ERROR_4BIT_UNEXPECTED_TIMEOUT: 7798c2ecf20Sopenharmony_ci return -EILSEQ; 7808c2ecf20Sopenharmony_ci case 33: 7818c2ecf20Sopenharmony_ci return -EILSEQ; 7828c2ecf20Sopenharmony_ci case SD_ERROR_ILLEGAL_COMMAND: 7838c2ecf20Sopenharmony_ci return -EINVAL; 7848c2ecf20Sopenharmony_ci case SD_ERROR_NO_DEVICE: 7858c2ecf20Sopenharmony_ci return -ENOMEDIUM; 7868c2ecf20Sopenharmony_ci default: 7878c2ecf20Sopenharmony_ci return -ENODEV; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic void command_res_completed(struct urb *urb) 7928c2ecf20Sopenharmony_ci{ /* urb completion handler - hardirq */ 7938c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; 7948c2ecf20Sopenharmony_ci if (urb->status) { 7958c2ecf20Sopenharmony_ci /* we have to let the initiator handle the error */ 7968c2ecf20Sopenharmony_ci } else if (vub300->command_res_urb->actual_length == 0) { 7978c2ecf20Sopenharmony_ci /* 7988c2ecf20Sopenharmony_ci * we have seen this happen once or twice and 7998c2ecf20Sopenharmony_ci * we suspect a buggy USB host controller 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ci } else if (!vub300->data) { 8028c2ecf20Sopenharmony_ci /* this means that the command (typically CMD52) succeeded */ 8038c2ecf20Sopenharmony_ci } else if (vub300->resp.common.header_type != 0x02) { 8048c2ecf20Sopenharmony_ci /* 8058c2ecf20Sopenharmony_ci * this is an error response from the VUB300 chip 8068c2ecf20Sopenharmony_ci * and we let the initiator handle it 8078c2ecf20Sopenharmony_ci */ 8088c2ecf20Sopenharmony_ci } else if (vub300->urb) { 8098c2ecf20Sopenharmony_ci vub300->cmd->error = 8108c2ecf20Sopenharmony_ci vub300_response_error(vub300->resp.error.error_code); 8118c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->urb); 8128c2ecf20Sopenharmony_ci } else { 8138c2ecf20Sopenharmony_ci vub300->cmd->error = 8148c2ecf20Sopenharmony_ci vub300_response_error(vub300->resp.error.error_code); 8158c2ecf20Sopenharmony_ci usb_sg_cancel(&vub300->sg_request); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci complete(&vub300->command_complete); /* got_response_in */ 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic void command_out_completed(struct urb *urb) 8218c2ecf20Sopenharmony_ci{ /* urb completion handler - hardirq */ 8228c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; 8238c2ecf20Sopenharmony_ci if (urb->status) { 8248c2ecf20Sopenharmony_ci complete(&vub300->command_complete); 8258c2ecf20Sopenharmony_ci } else { 8268c2ecf20Sopenharmony_ci int ret; 8278c2ecf20Sopenharmony_ci unsigned int pipe = 8288c2ecf20Sopenharmony_ci usb_rcvbulkpipe(vub300->udev, vub300->cmnd_res_ep); 8298c2ecf20Sopenharmony_ci usb_fill_bulk_urb(vub300->command_res_urb, vub300->udev, pipe, 8308c2ecf20Sopenharmony_ci &vub300->resp, sizeof(vub300->resp), 8318c2ecf20Sopenharmony_ci command_res_completed, vub300); 8328c2ecf20Sopenharmony_ci vub300->command_res_urb->actual_length = 0; 8338c2ecf20Sopenharmony_ci ret = usb_submit_urb(vub300->command_res_urb, GFP_ATOMIC); 8348c2ecf20Sopenharmony_ci if (ret == 0) { 8358c2ecf20Sopenharmony_ci /* 8368c2ecf20Sopenharmony_ci * the urb completion handler will call 8378c2ecf20Sopenharmony_ci * our completion handler 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_ci } else { 8408c2ecf20Sopenharmony_ci /* 8418c2ecf20Sopenharmony_ci * and thus we only call it directly 8428c2ecf20Sopenharmony_ci * when it will not be called 8438c2ecf20Sopenharmony_ci */ 8448c2ecf20Sopenharmony_ci complete(&vub300->command_complete); 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci/* 8508c2ecf20Sopenharmony_ci * the STUFF bits are masked out for the comparisons 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_cistatic void snoop_block_size_and_bus_width(struct vub300_mmc_host *vub300, 8538c2ecf20Sopenharmony_ci u32 cmd_arg) 8548c2ecf20Sopenharmony_ci{ 8558c2ecf20Sopenharmony_ci if ((0xFBFFFE00 & cmd_arg) == 0x80022200) 8568c2ecf20Sopenharmony_ci vub300->fbs[1] = (cmd_arg << 8) | (0x00FF & vub300->fbs[1]); 8578c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80022000) 8588c2ecf20Sopenharmony_ci vub300->fbs[1] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[1]); 8598c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80042200) 8608c2ecf20Sopenharmony_ci vub300->fbs[2] = (cmd_arg << 8) | (0x00FF & vub300->fbs[2]); 8618c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80042000) 8628c2ecf20Sopenharmony_ci vub300->fbs[2] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[2]); 8638c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80062200) 8648c2ecf20Sopenharmony_ci vub300->fbs[3] = (cmd_arg << 8) | (0x00FF & vub300->fbs[3]); 8658c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80062000) 8668c2ecf20Sopenharmony_ci vub300->fbs[3] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[3]); 8678c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80082200) 8688c2ecf20Sopenharmony_ci vub300->fbs[4] = (cmd_arg << 8) | (0x00FF & vub300->fbs[4]); 8698c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x80082000) 8708c2ecf20Sopenharmony_ci vub300->fbs[4] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[4]); 8718c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x800A2200) 8728c2ecf20Sopenharmony_ci vub300->fbs[5] = (cmd_arg << 8) | (0x00FF & vub300->fbs[5]); 8738c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x800A2000) 8748c2ecf20Sopenharmony_ci vub300->fbs[5] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[5]); 8758c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x800C2200) 8768c2ecf20Sopenharmony_ci vub300->fbs[6] = (cmd_arg << 8) | (0x00FF & vub300->fbs[6]); 8778c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x800C2000) 8788c2ecf20Sopenharmony_ci vub300->fbs[6] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[6]); 8798c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x800E2200) 8808c2ecf20Sopenharmony_ci vub300->fbs[7] = (cmd_arg << 8) | (0x00FF & vub300->fbs[7]); 8818c2ecf20Sopenharmony_ci else if ((0xFBFFFE00 & cmd_arg) == 0x800E2000) 8828c2ecf20Sopenharmony_ci vub300->fbs[7] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[7]); 8838c2ecf20Sopenharmony_ci else if ((0xFBFFFE03 & cmd_arg) == 0x80000E00) 8848c2ecf20Sopenharmony_ci vub300->bus_width = 1; 8858c2ecf20Sopenharmony_ci else if ((0xFBFFFE03 & cmd_arg) == 0x80000E02) 8868c2ecf20Sopenharmony_ci vub300->bus_width = 4; 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic void send_command(struct vub300_mmc_host *vub300) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_cmndwork_thread */ 8928c2ecf20Sopenharmony_ci struct mmc_command *cmd = vub300->cmd; 8938c2ecf20Sopenharmony_ci struct mmc_data *data = vub300->data; 8948c2ecf20Sopenharmony_ci int retval; 8958c2ecf20Sopenharmony_ci int i; 8968c2ecf20Sopenharmony_ci u8 response_type; 8978c2ecf20Sopenharmony_ci if (vub300->app_spec) { 8988c2ecf20Sopenharmony_ci switch (cmd->opcode) { 8998c2ecf20Sopenharmony_ci case 6: 9008c2ecf20Sopenharmony_ci response_type = SDRT_1; 9018c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9028c2ecf20Sopenharmony_ci if (0x00000000 == (0x00000003 & cmd->arg)) 9038c2ecf20Sopenharmony_ci vub300->bus_width = 1; 9048c2ecf20Sopenharmony_ci else if (0x00000002 == (0x00000003 & cmd->arg)) 9058c2ecf20Sopenharmony_ci vub300->bus_width = 4; 9068c2ecf20Sopenharmony_ci else 9078c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 9088c2ecf20Sopenharmony_ci "unexpected ACMD6 bus_width=%d\n", 9098c2ecf20Sopenharmony_ci 0x00000003 & cmd->arg); 9108c2ecf20Sopenharmony_ci break; 9118c2ecf20Sopenharmony_ci case 13: 9128c2ecf20Sopenharmony_ci response_type = SDRT_1; 9138c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci case 22: 9168c2ecf20Sopenharmony_ci response_type = SDRT_1; 9178c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci case 23: 9208c2ecf20Sopenharmony_ci response_type = SDRT_1; 9218c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci case 41: 9248c2ecf20Sopenharmony_ci response_type = SDRT_3; 9258c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9268c2ecf20Sopenharmony_ci break; 9278c2ecf20Sopenharmony_ci case 42: 9288c2ecf20Sopenharmony_ci response_type = SDRT_1; 9298c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci case 51: 9328c2ecf20Sopenharmony_ci response_type = SDRT_1; 9338c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9348c2ecf20Sopenharmony_ci break; 9358c2ecf20Sopenharmony_ci case 55: 9368c2ecf20Sopenharmony_ci response_type = SDRT_1; 9378c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci default: 9408c2ecf20Sopenharmony_ci vub300->resp_len = 0; 9418c2ecf20Sopenharmony_ci cmd->error = -EINVAL; 9428c2ecf20Sopenharmony_ci complete(&vub300->command_complete); 9438c2ecf20Sopenharmony_ci return; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci vub300->app_spec = 0; 9468c2ecf20Sopenharmony_ci } else { 9478c2ecf20Sopenharmony_ci switch (cmd->opcode) { 9488c2ecf20Sopenharmony_ci case 0: 9498c2ecf20Sopenharmony_ci response_type = SDRT_NONE; 9508c2ecf20Sopenharmony_ci vub300->resp_len = 0; 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci case 1: 9538c2ecf20Sopenharmony_ci response_type = SDRT_3; 9548c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci case 2: 9578c2ecf20Sopenharmony_ci response_type = SDRT_2; 9588c2ecf20Sopenharmony_ci vub300->resp_len = 17; 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci case 3: 9618c2ecf20Sopenharmony_ci response_type = SDRT_6; 9628c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci case 4: 9658c2ecf20Sopenharmony_ci response_type = SDRT_NONE; 9668c2ecf20Sopenharmony_ci vub300->resp_len = 0; 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci case 5: 9698c2ecf20Sopenharmony_ci response_type = SDRT_4; 9708c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci case 6: 9738c2ecf20Sopenharmony_ci response_type = SDRT_1; 9748c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9758c2ecf20Sopenharmony_ci break; 9768c2ecf20Sopenharmony_ci case 7: 9778c2ecf20Sopenharmony_ci response_type = SDRT_1B; 9788c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci case 8: 9818c2ecf20Sopenharmony_ci response_type = SDRT_7; 9828c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci case 9: 9858c2ecf20Sopenharmony_ci response_type = SDRT_2; 9868c2ecf20Sopenharmony_ci vub300->resp_len = 17; 9878c2ecf20Sopenharmony_ci break; 9888c2ecf20Sopenharmony_ci case 10: 9898c2ecf20Sopenharmony_ci response_type = SDRT_2; 9908c2ecf20Sopenharmony_ci vub300->resp_len = 17; 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case 12: 9938c2ecf20Sopenharmony_ci response_type = SDRT_1B; 9948c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9958c2ecf20Sopenharmony_ci break; 9968c2ecf20Sopenharmony_ci case 13: 9978c2ecf20Sopenharmony_ci response_type = SDRT_1; 9988c2ecf20Sopenharmony_ci vub300->resp_len = 6; 9998c2ecf20Sopenharmony_ci break; 10008c2ecf20Sopenharmony_ci case 15: 10018c2ecf20Sopenharmony_ci response_type = SDRT_NONE; 10028c2ecf20Sopenharmony_ci vub300->resp_len = 0; 10038c2ecf20Sopenharmony_ci break; 10048c2ecf20Sopenharmony_ci case 16: 10058c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vub300->fbs); i++) 10068c2ecf20Sopenharmony_ci vub300->fbs[i] = 0xFFFF & cmd->arg; 10078c2ecf20Sopenharmony_ci response_type = SDRT_1; 10088c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10098c2ecf20Sopenharmony_ci break; 10108c2ecf20Sopenharmony_ci case 17: 10118c2ecf20Sopenharmony_ci case 18: 10128c2ecf20Sopenharmony_ci case 24: 10138c2ecf20Sopenharmony_ci case 25: 10148c2ecf20Sopenharmony_ci case 27: 10158c2ecf20Sopenharmony_ci response_type = SDRT_1; 10168c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci case 28: 10198c2ecf20Sopenharmony_ci case 29: 10208c2ecf20Sopenharmony_ci response_type = SDRT_1B; 10218c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10228c2ecf20Sopenharmony_ci break; 10238c2ecf20Sopenharmony_ci case 30: 10248c2ecf20Sopenharmony_ci case 32: 10258c2ecf20Sopenharmony_ci case 33: 10268c2ecf20Sopenharmony_ci response_type = SDRT_1; 10278c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10288c2ecf20Sopenharmony_ci break; 10298c2ecf20Sopenharmony_ci case 38: 10308c2ecf20Sopenharmony_ci response_type = SDRT_1B; 10318c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci case 42: 10348c2ecf20Sopenharmony_ci response_type = SDRT_1; 10358c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10368c2ecf20Sopenharmony_ci break; 10378c2ecf20Sopenharmony_ci case 52: 10388c2ecf20Sopenharmony_ci response_type = SDRT_5; 10398c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10408c2ecf20Sopenharmony_ci snoop_block_size_and_bus_width(vub300, cmd->arg); 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci case 53: 10438c2ecf20Sopenharmony_ci response_type = SDRT_5; 10448c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10458c2ecf20Sopenharmony_ci break; 10468c2ecf20Sopenharmony_ci case 55: 10478c2ecf20Sopenharmony_ci response_type = SDRT_1; 10488c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10498c2ecf20Sopenharmony_ci vub300->app_spec = 1; 10508c2ecf20Sopenharmony_ci break; 10518c2ecf20Sopenharmony_ci case 56: 10528c2ecf20Sopenharmony_ci response_type = SDRT_1; 10538c2ecf20Sopenharmony_ci vub300->resp_len = 6; 10548c2ecf20Sopenharmony_ci break; 10558c2ecf20Sopenharmony_ci default: 10568c2ecf20Sopenharmony_ci vub300->resp_len = 0; 10578c2ecf20Sopenharmony_ci cmd->error = -EINVAL; 10588c2ecf20Sopenharmony_ci complete(&vub300->command_complete); 10598c2ecf20Sopenharmony_ci return; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci /* 10638c2ecf20Sopenharmony_ci * it is a shame that we can not use "sizeof(struct sd_command_header)" 10648c2ecf20Sopenharmony_ci * this is because the packet _must_ be padded to 64 bytes 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci vub300->cmnd.head.header_size = 20; 10678c2ecf20Sopenharmony_ci vub300->cmnd.head.header_type = 0x00; 10688c2ecf20Sopenharmony_ci vub300->cmnd.head.port_number = 0; /* "0" means port 1 */ 10698c2ecf20Sopenharmony_ci vub300->cmnd.head.command_type = 0x00; /* standard read command */ 10708c2ecf20Sopenharmony_ci vub300->cmnd.head.response_type = response_type; 10718c2ecf20Sopenharmony_ci vub300->cmnd.head.command_index = cmd->opcode; 10728c2ecf20Sopenharmony_ci vub300->cmnd.head.arguments[0] = cmd->arg >> 24; 10738c2ecf20Sopenharmony_ci vub300->cmnd.head.arguments[1] = cmd->arg >> 16; 10748c2ecf20Sopenharmony_ci vub300->cmnd.head.arguments[2] = cmd->arg >> 8; 10758c2ecf20Sopenharmony_ci vub300->cmnd.head.arguments[3] = cmd->arg >> 0; 10768c2ecf20Sopenharmony_ci if (cmd->opcode == 52) { 10778c2ecf20Sopenharmony_ci int fn = 0x7 & (cmd->arg >> 28); 10788c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = 0; 10798c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = 0; 10808c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[0] = (vub300->fbs[fn] >> 8) & 0xFF; 10818c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[1] = (vub300->fbs[fn] >> 0) & 0xFF; 10828c2ecf20Sopenharmony_ci vub300->cmnd.head.command_type = 0x00; 10838c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[0] = 0; 10848c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[1] = 0; 10858c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[2] = 0; 10868c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[3] = 0; 10878c2ecf20Sopenharmony_ci } else if (!data) { 10888c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = 0; 10898c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = 0; 10908c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[0] = (vub300->fbs[0] >> 8) & 0xFF; 10918c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[1] = (vub300->fbs[0] >> 0) & 0xFF; 10928c2ecf20Sopenharmony_ci vub300->cmnd.head.command_type = 0x00; 10938c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[0] = 0; 10948c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[1] = 0; 10958c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[2] = 0; 10968c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[3] = 0; 10978c2ecf20Sopenharmony_ci } else if (cmd->opcode == 53) { 10988c2ecf20Sopenharmony_ci int fn = 0x7 & (cmd->arg >> 28); 10998c2ecf20Sopenharmony_ci if (0x08 & vub300->cmnd.head.arguments[0]) { /* BLOCK MODE */ 11008c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = 11018c2ecf20Sopenharmony_ci (data->blocks >> 8) & 0xFF; 11028c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = 11038c2ecf20Sopenharmony_ci (data->blocks >> 0) & 0xFF; 11048c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[0] = 11058c2ecf20Sopenharmony_ci (data->blksz >> 8) & 0xFF; 11068c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[1] = 11078c2ecf20Sopenharmony_ci (data->blksz >> 0) & 0xFF; 11088c2ecf20Sopenharmony_ci } else { /* BYTE MODE */ 11098c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = 0; 11108c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = 0; 11118c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[0] = 11128c2ecf20Sopenharmony_ci (vub300->datasize >> 8) & 0xFF; 11138c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[1] = 11148c2ecf20Sopenharmony_ci (vub300->datasize >> 0) & 0xFF; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci vub300->cmnd.head.command_type = 11178c2ecf20Sopenharmony_ci (MMC_DATA_READ & data->flags) ? 0x00 : 0x80; 11188c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[0] = 11198c2ecf20Sopenharmony_ci (vub300->datasize >> 24) & 0xFF; 11208c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[1] = 11218c2ecf20Sopenharmony_ci (vub300->datasize >> 16) & 0xFF; 11228c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[2] = 11238c2ecf20Sopenharmony_ci (vub300->datasize >> 8) & 0xFF; 11248c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[3] = 11258c2ecf20Sopenharmony_ci (vub300->datasize >> 0) & 0xFF; 11268c2ecf20Sopenharmony_ci if (vub300->datasize < vub300->fbs[fn]) { 11278c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = 0; 11288c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = 0; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci } else { 11318c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = (data->blocks >> 8) & 0xFF; 11328c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = (data->blocks >> 0) & 0xFF; 11338c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[0] = (data->blksz >> 8) & 0xFF; 11348c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[1] = (data->blksz >> 0) & 0xFF; 11358c2ecf20Sopenharmony_ci vub300->cmnd.head.command_type = 11368c2ecf20Sopenharmony_ci (MMC_DATA_READ & data->flags) ? 0x00 : 0x80; 11378c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[0] = 11388c2ecf20Sopenharmony_ci (vub300->datasize >> 24) & 0xFF; 11398c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[1] = 11408c2ecf20Sopenharmony_ci (vub300->datasize >> 16) & 0xFF; 11418c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[2] = 11428c2ecf20Sopenharmony_ci (vub300->datasize >> 8) & 0xFF; 11438c2ecf20Sopenharmony_ci vub300->cmnd.head.transfer_size[3] = 11448c2ecf20Sopenharmony_ci (vub300->datasize >> 0) & 0xFF; 11458c2ecf20Sopenharmony_ci if (vub300->datasize < vub300->fbs[0]) { 11468c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[0] = 0; 11478c2ecf20Sopenharmony_ci vub300->cmnd.head.block_count[1] = 0; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci if (vub300->cmnd.head.block_size[0] || vub300->cmnd.head.block_size[1]) { 11518c2ecf20Sopenharmony_ci u16 block_size = vub300->cmnd.head.block_size[1] | 11528c2ecf20Sopenharmony_ci (vub300->cmnd.head.block_size[0] << 8); 11538c2ecf20Sopenharmony_ci u16 block_boundary = FIRMWARE_BLOCK_BOUNDARY - 11548c2ecf20Sopenharmony_ci (FIRMWARE_BLOCK_BOUNDARY % block_size); 11558c2ecf20Sopenharmony_ci vub300->cmnd.head.block_boundary[0] = 11568c2ecf20Sopenharmony_ci (block_boundary >> 8) & 0xFF; 11578c2ecf20Sopenharmony_ci vub300->cmnd.head.block_boundary[1] = 11588c2ecf20Sopenharmony_ci (block_boundary >> 0) & 0xFF; 11598c2ecf20Sopenharmony_ci } else { 11608c2ecf20Sopenharmony_ci vub300->cmnd.head.block_boundary[0] = 0; 11618c2ecf20Sopenharmony_ci vub300->cmnd.head.block_boundary[1] = 0; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci usb_fill_bulk_urb(vub300->command_out_urb, vub300->udev, 11648c2ecf20Sopenharmony_ci usb_sndbulkpipe(vub300->udev, vub300->cmnd_out_ep), 11658c2ecf20Sopenharmony_ci &vub300->cmnd, sizeof(vub300->cmnd), 11668c2ecf20Sopenharmony_ci command_out_completed, vub300); 11678c2ecf20Sopenharmony_ci retval = usb_submit_urb(vub300->command_out_urb, GFP_KERNEL); 11688c2ecf20Sopenharmony_ci if (retval < 0) { 11698c2ecf20Sopenharmony_ci cmd->error = retval; 11708c2ecf20Sopenharmony_ci complete(&vub300->command_complete); 11718c2ecf20Sopenharmony_ci return; 11728c2ecf20Sopenharmony_ci } else { 11738c2ecf20Sopenharmony_ci return; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci/* 11788c2ecf20Sopenharmony_ci * timer callback runs in atomic mode 11798c2ecf20Sopenharmony_ci * so it cannot call usb_kill_urb() 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_cistatic void vub300_sg_timed_out(struct timer_list *t) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = from_timer(vub300, t, 11848c2ecf20Sopenharmony_ci sg_transfer_timer); 11858c2ecf20Sopenharmony_ci vub300->usb_timed_out = 1; 11868c2ecf20Sopenharmony_ci usb_sg_cancel(&vub300->sg_request); 11878c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->command_out_urb); 11888c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->command_res_urb); 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic u16 roundup_to_multiple_of_64(u16 number) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci return 0xFFC0 & (0x3F + number); 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci/* 11978c2ecf20Sopenharmony_ci * this is a separate function to solve the 80 column width restriction 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_cistatic void __download_offload_pseudocode(struct vub300_mmc_host *vub300, 12008c2ecf20Sopenharmony_ci const struct firmware *fw) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci u8 register_count = 0; 12038c2ecf20Sopenharmony_ci u16 ts = 0; 12048c2ecf20Sopenharmony_ci u16 interrupt_size = 0; 12058c2ecf20Sopenharmony_ci const u8 *data = fw->data; 12068c2ecf20Sopenharmony_ci int size = fw->size; 12078c2ecf20Sopenharmony_ci u8 c; 12088c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, "using %s for SDIO offload processing\n", 12098c2ecf20Sopenharmony_ci vub300->vub_name); 12108c2ecf20Sopenharmony_ci do { 12118c2ecf20Sopenharmony_ci c = *data++; 12128c2ecf20Sopenharmony_ci } while (size-- && c); /* skip comment */ 12138c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, "using offload firmware %s %s\n", fw->data, 12148c2ecf20Sopenharmony_ci vub300->vub_name); 12158c2ecf20Sopenharmony_ci if (size < 4) { 12168c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 12178c2ecf20Sopenharmony_ci "corrupt offload pseudocode in firmware %s\n", 12188c2ecf20Sopenharmony_ci vub300->vub_name); 12198c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "corrupt offload pseudocode", 12208c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 12218c2ecf20Sopenharmony_ci return; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci interrupt_size += *data++; 12248c2ecf20Sopenharmony_ci size -= 1; 12258c2ecf20Sopenharmony_ci interrupt_size <<= 8; 12268c2ecf20Sopenharmony_ci interrupt_size += *data++; 12278c2ecf20Sopenharmony_ci size -= 1; 12288c2ecf20Sopenharmony_ci if (interrupt_size < size) { 12298c2ecf20Sopenharmony_ci u16 xfer_length = roundup_to_multiple_of_64(interrupt_size); 12308c2ecf20Sopenharmony_ci u8 *xfer_buffer = kmalloc(xfer_length, GFP_KERNEL); 12318c2ecf20Sopenharmony_ci if (xfer_buffer) { 12328c2ecf20Sopenharmony_ci int retval; 12338c2ecf20Sopenharmony_ci memcpy(xfer_buffer, data, interrupt_size); 12348c2ecf20Sopenharmony_ci memset(xfer_buffer + interrupt_size, 0, 12358c2ecf20Sopenharmony_ci xfer_length - interrupt_size); 12368c2ecf20Sopenharmony_ci size -= interrupt_size; 12378c2ecf20Sopenharmony_ci data += interrupt_size; 12388c2ecf20Sopenharmony_ci retval = 12398c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, 12408c2ecf20Sopenharmony_ci usb_sndctrlpipe(vub300->udev, 0), 12418c2ecf20Sopenharmony_ci SET_INTERRUPT_PSEUDOCODE, 12428c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 12438c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, 0x0000, 0x0000, 12448c2ecf20Sopenharmony_ci xfer_buffer, xfer_length, 1000); 12458c2ecf20Sopenharmony_ci kfree(xfer_buffer); 12468c2ecf20Sopenharmony_ci if (retval < 0) 12478c2ecf20Sopenharmony_ci goto copy_error_message; 12488c2ecf20Sopenharmony_ci } else { 12498c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 12508c2ecf20Sopenharmony_ci "not enough memory for xfer buffer to send" 12518c2ecf20Sopenharmony_ci " INTERRUPT_PSEUDOCODE for %s %s\n", fw->data, 12528c2ecf20Sopenharmony_ci vub300->vub_name); 12538c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, 12548c2ecf20Sopenharmony_ci "SDIO interrupt pseudocode download failed", 12558c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 12568c2ecf20Sopenharmony_ci return; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci } else { 12598c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 12608c2ecf20Sopenharmony_ci "corrupt interrupt pseudocode in firmware %s %s\n", 12618c2ecf20Sopenharmony_ci fw->data, vub300->vub_name); 12628c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "corrupt interrupt pseudocode", 12638c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 12648c2ecf20Sopenharmony_ci return; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci ts += *data++; 12678c2ecf20Sopenharmony_ci size -= 1; 12688c2ecf20Sopenharmony_ci ts <<= 8; 12698c2ecf20Sopenharmony_ci ts += *data++; 12708c2ecf20Sopenharmony_ci size -= 1; 12718c2ecf20Sopenharmony_ci if (ts < size) { 12728c2ecf20Sopenharmony_ci u16 xfer_length = roundup_to_multiple_of_64(ts); 12738c2ecf20Sopenharmony_ci u8 *xfer_buffer = kmalloc(xfer_length, GFP_KERNEL); 12748c2ecf20Sopenharmony_ci if (xfer_buffer) { 12758c2ecf20Sopenharmony_ci int retval; 12768c2ecf20Sopenharmony_ci memcpy(xfer_buffer, data, ts); 12778c2ecf20Sopenharmony_ci memset(xfer_buffer + ts, 0, 12788c2ecf20Sopenharmony_ci xfer_length - ts); 12798c2ecf20Sopenharmony_ci size -= ts; 12808c2ecf20Sopenharmony_ci data += ts; 12818c2ecf20Sopenharmony_ci retval = 12828c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, 12838c2ecf20Sopenharmony_ci usb_sndctrlpipe(vub300->udev, 0), 12848c2ecf20Sopenharmony_ci SET_TRANSFER_PSEUDOCODE, 12858c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | 12868c2ecf20Sopenharmony_ci USB_RECIP_DEVICE, 0x0000, 0x0000, 12878c2ecf20Sopenharmony_ci xfer_buffer, xfer_length, 1000); 12888c2ecf20Sopenharmony_ci kfree(xfer_buffer); 12898c2ecf20Sopenharmony_ci if (retval < 0) 12908c2ecf20Sopenharmony_ci goto copy_error_message; 12918c2ecf20Sopenharmony_ci } else { 12928c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 12938c2ecf20Sopenharmony_ci "not enough memory for xfer buffer to send" 12948c2ecf20Sopenharmony_ci " TRANSFER_PSEUDOCODE for %s %s\n", fw->data, 12958c2ecf20Sopenharmony_ci vub300->vub_name); 12968c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, 12978c2ecf20Sopenharmony_ci "SDIO transfer pseudocode download failed", 12988c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 12998c2ecf20Sopenharmony_ci return; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci } else { 13028c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 13038c2ecf20Sopenharmony_ci "corrupt transfer pseudocode in firmware %s %s\n", 13048c2ecf20Sopenharmony_ci fw->data, vub300->vub_name); 13058c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "corrupt transfer pseudocode", 13068c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 13078c2ecf20Sopenharmony_ci return; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci register_count += *data++; 13108c2ecf20Sopenharmony_ci size -= 1; 13118c2ecf20Sopenharmony_ci if (register_count * 4 == size) { 13128c2ecf20Sopenharmony_ci int I = vub300->dynamic_register_count = register_count; 13138c2ecf20Sopenharmony_ci int i = 0; 13148c2ecf20Sopenharmony_ci while (I--) { 13158c2ecf20Sopenharmony_ci unsigned int func_num = 0; 13168c2ecf20Sopenharmony_ci vub300->sdio_register[i].func_num = *data++; 13178c2ecf20Sopenharmony_ci size -= 1; 13188c2ecf20Sopenharmony_ci func_num += *data++; 13198c2ecf20Sopenharmony_ci size -= 1; 13208c2ecf20Sopenharmony_ci func_num <<= 8; 13218c2ecf20Sopenharmony_ci func_num += *data++; 13228c2ecf20Sopenharmony_ci size -= 1; 13238c2ecf20Sopenharmony_ci func_num <<= 8; 13248c2ecf20Sopenharmony_ci func_num += *data++; 13258c2ecf20Sopenharmony_ci size -= 1; 13268c2ecf20Sopenharmony_ci vub300->sdio_register[i].sdio_reg = func_num; 13278c2ecf20Sopenharmony_ci vub300->sdio_register[i].activate = 1; 13288c2ecf20Sopenharmony_ci vub300->sdio_register[i].prepared = 0; 13298c2ecf20Sopenharmony_ci i += 1; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, 13328c2ecf20Sopenharmony_ci "initialized %d dynamic pseudocode registers\n", 13338c2ecf20Sopenharmony_ci vub300->dynamic_register_count); 13348c2ecf20Sopenharmony_ci return; 13358c2ecf20Sopenharmony_ci } else { 13368c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 13378c2ecf20Sopenharmony_ci "corrupt dynamic registers in firmware %s\n", 13388c2ecf20Sopenharmony_ci vub300->vub_name); 13398c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "corrupt dynamic registers", 13408c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 13418c2ecf20Sopenharmony_ci return; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_cicopy_error_message: 13478c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "SDIO pseudocode download failed", 13488c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* 13528c2ecf20Sopenharmony_ci * if the binary containing the EMPTY PseudoCode can not be found 13538c2ecf20Sopenharmony_ci * vub300->vub_name is set anyway in order to prevent an automatic retry 13548c2ecf20Sopenharmony_ci */ 13558c2ecf20Sopenharmony_cistatic void download_offload_pseudocode(struct vub300_mmc_host *vub300) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci struct mmc_card *card = vub300->mmc->card; 13588c2ecf20Sopenharmony_ci int sdio_funcs = card->sdio_funcs; 13598c2ecf20Sopenharmony_ci const struct firmware *fw = NULL; 13608c2ecf20Sopenharmony_ci int l = snprintf(vub300->vub_name, sizeof(vub300->vub_name), 13618c2ecf20Sopenharmony_ci "vub_%04X%04X", card->cis.vendor, card->cis.device); 13628c2ecf20Sopenharmony_ci int n = 0; 13638c2ecf20Sopenharmony_ci int retval; 13648c2ecf20Sopenharmony_ci for (n = 0; n < sdio_funcs; n++) { 13658c2ecf20Sopenharmony_ci struct sdio_func *sf = card->sdio_func[n]; 13668c2ecf20Sopenharmony_ci l += scnprintf(vub300->vub_name + l, 13678c2ecf20Sopenharmony_ci sizeof(vub300->vub_name) - l, "_%04X%04X", 13688c2ecf20Sopenharmony_ci sf->vendor, sf->device); 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci snprintf(vub300->vub_name + l, sizeof(vub300->vub_name) - l, ".bin"); 13718c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, "requesting offload firmware %s\n", 13728c2ecf20Sopenharmony_ci vub300->vub_name); 13738c2ecf20Sopenharmony_ci retval = request_firmware(&fw, vub300->vub_name, &card->dev); 13748c2ecf20Sopenharmony_ci if (retval < 0) { 13758c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "vub_default.bin", 13768c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 13778c2ecf20Sopenharmony_ci retval = request_firmware(&fw, vub300->vub_name, &card->dev); 13788c2ecf20Sopenharmony_ci if (retval < 0) { 13798c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, 13808c2ecf20Sopenharmony_ci "no SDIO offload firmware found", 13818c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 13828c2ecf20Sopenharmony_ci } else { 13838c2ecf20Sopenharmony_ci __download_offload_pseudocode(vub300, fw); 13848c2ecf20Sopenharmony_ci release_firmware(fw); 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci } else { 13878c2ecf20Sopenharmony_ci __download_offload_pseudocode(vub300, fw); 13888c2ecf20Sopenharmony_ci release_firmware(fw); 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cistatic void vub300_usb_bulk_msg_completion(struct urb *urb) 13938c2ecf20Sopenharmony_ci{ /* urb completion handler - hardirq */ 13948c2ecf20Sopenharmony_ci complete((struct completion *)urb->context); 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_cistatic int vub300_usb_bulk_msg(struct vub300_mmc_host *vub300, 13988c2ecf20Sopenharmony_ci unsigned int pipe, void *data, int len, 13998c2ecf20Sopenharmony_ci int *actual_length, int timeout_msecs) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_cmndwork_thread */ 14028c2ecf20Sopenharmony_ci struct usb_device *usb_dev = vub300->udev; 14038c2ecf20Sopenharmony_ci struct completion done; 14048c2ecf20Sopenharmony_ci int retval; 14058c2ecf20Sopenharmony_ci vub300->urb = usb_alloc_urb(0, GFP_KERNEL); 14068c2ecf20Sopenharmony_ci if (!vub300->urb) 14078c2ecf20Sopenharmony_ci return -ENOMEM; 14088c2ecf20Sopenharmony_ci usb_fill_bulk_urb(vub300->urb, usb_dev, pipe, data, len, 14098c2ecf20Sopenharmony_ci vub300_usb_bulk_msg_completion, NULL); 14108c2ecf20Sopenharmony_ci init_completion(&done); 14118c2ecf20Sopenharmony_ci vub300->urb->context = &done; 14128c2ecf20Sopenharmony_ci vub300->urb->actual_length = 0; 14138c2ecf20Sopenharmony_ci retval = usb_submit_urb(vub300->urb, GFP_KERNEL); 14148c2ecf20Sopenharmony_ci if (unlikely(retval)) 14158c2ecf20Sopenharmony_ci goto out; 14168c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout 14178c2ecf20Sopenharmony_ci (&done, msecs_to_jiffies(timeout_msecs))) { 14188c2ecf20Sopenharmony_ci retval = -ETIMEDOUT; 14198c2ecf20Sopenharmony_ci usb_kill_urb(vub300->urb); 14208c2ecf20Sopenharmony_ci } else { 14218c2ecf20Sopenharmony_ci retval = vub300->urb->status; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ciout: 14248c2ecf20Sopenharmony_ci *actual_length = vub300->urb->actual_length; 14258c2ecf20Sopenharmony_ci usb_free_urb(vub300->urb); 14268c2ecf20Sopenharmony_ci vub300->urb = NULL; 14278c2ecf20Sopenharmony_ci return retval; 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cistatic int __command_read_data(struct vub300_mmc_host *vub300, 14318c2ecf20Sopenharmony_ci struct mmc_command *cmd, struct mmc_data *data) 14328c2ecf20Sopenharmony_ci{ 14338c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_cmndwork_thread */ 14348c2ecf20Sopenharmony_ci int linear_length = vub300->datasize; 14358c2ecf20Sopenharmony_ci int padded_length = vub300->large_usb_packets ? 14368c2ecf20Sopenharmony_ci ((511 + linear_length) >> 9) << 9 : 14378c2ecf20Sopenharmony_ci ((63 + linear_length) >> 6) << 6; 14388c2ecf20Sopenharmony_ci if ((padded_length == linear_length) || !pad_input_to_usb_pkt) { 14398c2ecf20Sopenharmony_ci int result; 14408c2ecf20Sopenharmony_ci unsigned pipe; 14418c2ecf20Sopenharmony_ci pipe = usb_rcvbulkpipe(vub300->udev, vub300->data_inp_ep); 14428c2ecf20Sopenharmony_ci result = usb_sg_init(&vub300->sg_request, vub300->udev, 14438c2ecf20Sopenharmony_ci pipe, 0, data->sg, 14448c2ecf20Sopenharmony_ci data->sg_len, 0, GFP_KERNEL); 14458c2ecf20Sopenharmony_ci if (result < 0) { 14468c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->command_out_urb); 14478c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->command_res_urb); 14488c2ecf20Sopenharmony_ci cmd->error = result; 14498c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci } else { 14528c2ecf20Sopenharmony_ci vub300->sg_transfer_timer.expires = 14538c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(2000 + 14548c2ecf20Sopenharmony_ci (linear_length / 16384)); 14558c2ecf20Sopenharmony_ci add_timer(&vub300->sg_transfer_timer); 14568c2ecf20Sopenharmony_ci usb_sg_wait(&vub300->sg_request); 14578c2ecf20Sopenharmony_ci del_timer(&vub300->sg_transfer_timer); 14588c2ecf20Sopenharmony_ci if (vub300->sg_request.status < 0) { 14598c2ecf20Sopenharmony_ci cmd->error = vub300->sg_request.status; 14608c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 14618c2ecf20Sopenharmony_ci return 0; 14628c2ecf20Sopenharmony_ci } else { 14638c2ecf20Sopenharmony_ci data->bytes_xfered = vub300->datasize; 14648c2ecf20Sopenharmony_ci return linear_length; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci } else { 14688c2ecf20Sopenharmony_ci u8 *buf = kmalloc(padded_length, GFP_KERNEL); 14698c2ecf20Sopenharmony_ci if (buf) { 14708c2ecf20Sopenharmony_ci int result; 14718c2ecf20Sopenharmony_ci unsigned pipe = usb_rcvbulkpipe(vub300->udev, 14728c2ecf20Sopenharmony_ci vub300->data_inp_ep); 14738c2ecf20Sopenharmony_ci int actual_length = 0; 14748c2ecf20Sopenharmony_ci result = vub300_usb_bulk_msg(vub300, pipe, buf, 14758c2ecf20Sopenharmony_ci padded_length, &actual_length, 14768c2ecf20Sopenharmony_ci 2000 + (padded_length / 16384)); 14778c2ecf20Sopenharmony_ci if (result < 0) { 14788c2ecf20Sopenharmony_ci cmd->error = result; 14798c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 14808c2ecf20Sopenharmony_ci kfree(buf); 14818c2ecf20Sopenharmony_ci return 0; 14828c2ecf20Sopenharmony_ci } else if (actual_length < linear_length) { 14838c2ecf20Sopenharmony_ci cmd->error = -EREMOTEIO; 14848c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 14858c2ecf20Sopenharmony_ci kfree(buf); 14868c2ecf20Sopenharmony_ci return 0; 14878c2ecf20Sopenharmony_ci } else { 14888c2ecf20Sopenharmony_ci sg_copy_from_buffer(data->sg, data->sg_len, buf, 14898c2ecf20Sopenharmony_ci linear_length); 14908c2ecf20Sopenharmony_ci kfree(buf); 14918c2ecf20Sopenharmony_ci data->bytes_xfered = vub300->datasize; 14928c2ecf20Sopenharmony_ci return linear_length; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci } else { 14958c2ecf20Sopenharmony_ci cmd->error = -ENOMEM; 14968c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 14978c2ecf20Sopenharmony_ci return 0; 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci} 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_cistatic int __command_write_data(struct vub300_mmc_host *vub300, 15038c2ecf20Sopenharmony_ci struct mmc_command *cmd, struct mmc_data *data) 15048c2ecf20Sopenharmony_ci{ 15058c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_cmndwork_thread */ 15068c2ecf20Sopenharmony_ci unsigned pipe = usb_sndbulkpipe(vub300->udev, vub300->data_out_ep); 15078c2ecf20Sopenharmony_ci int linear_length = vub300->datasize; 15088c2ecf20Sopenharmony_ci int modulo_64_length = linear_length & 0x003F; 15098c2ecf20Sopenharmony_ci int modulo_512_length = linear_length & 0x01FF; 15108c2ecf20Sopenharmony_ci if (linear_length < 64) { 15118c2ecf20Sopenharmony_ci int result; 15128c2ecf20Sopenharmony_ci int actual_length; 15138c2ecf20Sopenharmony_ci sg_copy_to_buffer(data->sg, data->sg_len, 15148c2ecf20Sopenharmony_ci vub300->padded_buffer, 15158c2ecf20Sopenharmony_ci sizeof(vub300->padded_buffer)); 15168c2ecf20Sopenharmony_ci memset(vub300->padded_buffer + linear_length, 0, 15178c2ecf20Sopenharmony_ci sizeof(vub300->padded_buffer) - linear_length); 15188c2ecf20Sopenharmony_ci result = vub300_usb_bulk_msg(vub300, pipe, vub300->padded_buffer, 15198c2ecf20Sopenharmony_ci sizeof(vub300->padded_buffer), 15208c2ecf20Sopenharmony_ci &actual_length, 2000 + 15218c2ecf20Sopenharmony_ci (sizeof(vub300->padded_buffer) / 15228c2ecf20Sopenharmony_ci 16384)); 15238c2ecf20Sopenharmony_ci if (result < 0) { 15248c2ecf20Sopenharmony_ci cmd->error = result; 15258c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 15268c2ecf20Sopenharmony_ci } else { 15278c2ecf20Sopenharmony_ci data->bytes_xfered = vub300->datasize; 15288c2ecf20Sopenharmony_ci } 15298c2ecf20Sopenharmony_ci } else if ((!vub300->large_usb_packets && (0 < modulo_64_length)) || 15308c2ecf20Sopenharmony_ci (vub300->large_usb_packets && (64 > modulo_512_length)) 15318c2ecf20Sopenharmony_ci ) { /* don't you just love these work-rounds */ 15328c2ecf20Sopenharmony_ci int padded_length = ((63 + linear_length) >> 6) << 6; 15338c2ecf20Sopenharmony_ci u8 *buf = kmalloc(padded_length, GFP_KERNEL); 15348c2ecf20Sopenharmony_ci if (buf) { 15358c2ecf20Sopenharmony_ci int result; 15368c2ecf20Sopenharmony_ci int actual_length; 15378c2ecf20Sopenharmony_ci sg_copy_to_buffer(data->sg, data->sg_len, buf, 15388c2ecf20Sopenharmony_ci padded_length); 15398c2ecf20Sopenharmony_ci memset(buf + linear_length, 0, 15408c2ecf20Sopenharmony_ci padded_length - linear_length); 15418c2ecf20Sopenharmony_ci result = 15428c2ecf20Sopenharmony_ci vub300_usb_bulk_msg(vub300, pipe, buf, 15438c2ecf20Sopenharmony_ci padded_length, &actual_length, 15448c2ecf20Sopenharmony_ci 2000 + padded_length / 16384); 15458c2ecf20Sopenharmony_ci kfree(buf); 15468c2ecf20Sopenharmony_ci if (result < 0) { 15478c2ecf20Sopenharmony_ci cmd->error = result; 15488c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 15498c2ecf20Sopenharmony_ci } else { 15508c2ecf20Sopenharmony_ci data->bytes_xfered = vub300->datasize; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci } else { 15538c2ecf20Sopenharmony_ci cmd->error = -ENOMEM; 15548c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci } else { /* no data padding required */ 15578c2ecf20Sopenharmony_ci int result; 15588c2ecf20Sopenharmony_ci unsigned char buf[64 * 4]; 15598c2ecf20Sopenharmony_ci sg_copy_to_buffer(data->sg, data->sg_len, buf, sizeof(buf)); 15608c2ecf20Sopenharmony_ci result = usb_sg_init(&vub300->sg_request, vub300->udev, 15618c2ecf20Sopenharmony_ci pipe, 0, data->sg, 15628c2ecf20Sopenharmony_ci data->sg_len, 0, GFP_KERNEL); 15638c2ecf20Sopenharmony_ci if (result < 0) { 15648c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->command_out_urb); 15658c2ecf20Sopenharmony_ci usb_unlink_urb(vub300->command_res_urb); 15668c2ecf20Sopenharmony_ci cmd->error = result; 15678c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 15688c2ecf20Sopenharmony_ci } else { 15698c2ecf20Sopenharmony_ci vub300->sg_transfer_timer.expires = 15708c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(2000 + 15718c2ecf20Sopenharmony_ci linear_length / 16384); 15728c2ecf20Sopenharmony_ci add_timer(&vub300->sg_transfer_timer); 15738c2ecf20Sopenharmony_ci usb_sg_wait(&vub300->sg_request); 15748c2ecf20Sopenharmony_ci if (cmd->error) { 15758c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 15768c2ecf20Sopenharmony_ci } else { 15778c2ecf20Sopenharmony_ci del_timer(&vub300->sg_transfer_timer); 15788c2ecf20Sopenharmony_ci if (vub300->sg_request.status < 0) { 15798c2ecf20Sopenharmony_ci cmd->error = vub300->sg_request.status; 15808c2ecf20Sopenharmony_ci data->bytes_xfered = 0; 15818c2ecf20Sopenharmony_ci } else { 15828c2ecf20Sopenharmony_ci data->bytes_xfered = vub300->datasize; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci return linear_length; 15888c2ecf20Sopenharmony_ci} 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_cistatic void __vub300_command_response(struct vub300_mmc_host *vub300, 15918c2ecf20Sopenharmony_ci struct mmc_command *cmd, 15928c2ecf20Sopenharmony_ci struct mmc_data *data, int data_length) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_cmndwork_thread */ 15958c2ecf20Sopenharmony_ci long respretval; 15968c2ecf20Sopenharmony_ci int msec_timeout = 1000 + data_length / 4; 15978c2ecf20Sopenharmony_ci respretval = 15988c2ecf20Sopenharmony_ci wait_for_completion_timeout(&vub300->command_complete, 15998c2ecf20Sopenharmony_ci msecs_to_jiffies(msec_timeout)); 16008c2ecf20Sopenharmony_ci if (respretval == 0) { /* TIMED OUT */ 16018c2ecf20Sopenharmony_ci /* we don't know which of "out" and "res" if any failed */ 16028c2ecf20Sopenharmony_ci int result; 16038c2ecf20Sopenharmony_ci vub300->usb_timed_out = 1; 16048c2ecf20Sopenharmony_ci usb_kill_urb(vub300->command_out_urb); 16058c2ecf20Sopenharmony_ci usb_kill_urb(vub300->command_res_urb); 16068c2ecf20Sopenharmony_ci cmd->error = -ETIMEDOUT; 16078c2ecf20Sopenharmony_ci result = usb_lock_device_for_reset(vub300->udev, 16088c2ecf20Sopenharmony_ci vub300->interface); 16098c2ecf20Sopenharmony_ci if (result == 0) { 16108c2ecf20Sopenharmony_ci result = usb_reset_device(vub300->udev); 16118c2ecf20Sopenharmony_ci usb_unlock_device(vub300->udev); 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci } else if (respretval < 0) { 16148c2ecf20Sopenharmony_ci /* we don't know which of "out" and "res" if any failed */ 16158c2ecf20Sopenharmony_ci usb_kill_urb(vub300->command_out_urb); 16168c2ecf20Sopenharmony_ci usb_kill_urb(vub300->command_res_urb); 16178c2ecf20Sopenharmony_ci cmd->error = respretval; 16188c2ecf20Sopenharmony_ci } else if (cmd->error) { 16198c2ecf20Sopenharmony_ci /* 16208c2ecf20Sopenharmony_ci * the error occurred sending the command 16218c2ecf20Sopenharmony_ci * or receiving the response 16228c2ecf20Sopenharmony_ci */ 16238c2ecf20Sopenharmony_ci } else if (vub300->command_out_urb->status) { 16248c2ecf20Sopenharmony_ci vub300->usb_transport_fail = vub300->command_out_urb->status; 16258c2ecf20Sopenharmony_ci cmd->error = -EPROTO == vub300->command_out_urb->status ? 16268c2ecf20Sopenharmony_ci -ESHUTDOWN : vub300->command_out_urb->status; 16278c2ecf20Sopenharmony_ci } else if (vub300->command_res_urb->status) { 16288c2ecf20Sopenharmony_ci vub300->usb_transport_fail = vub300->command_res_urb->status; 16298c2ecf20Sopenharmony_ci cmd->error = -EPROTO == vub300->command_res_urb->status ? 16308c2ecf20Sopenharmony_ci -ESHUTDOWN : vub300->command_res_urb->status; 16318c2ecf20Sopenharmony_ci } else if (vub300->resp.common.header_type == 0x00) { 16328c2ecf20Sopenharmony_ci /* 16338c2ecf20Sopenharmony_ci * the command completed successfully 16348c2ecf20Sopenharmony_ci * and there was no piggybacked data 16358c2ecf20Sopenharmony_ci */ 16368c2ecf20Sopenharmony_ci } else if (vub300->resp.common.header_type == RESPONSE_ERROR) { 16378c2ecf20Sopenharmony_ci cmd->error = 16388c2ecf20Sopenharmony_ci vub300_response_error(vub300->resp.error.error_code); 16398c2ecf20Sopenharmony_ci if (vub300->data) 16408c2ecf20Sopenharmony_ci usb_sg_cancel(&vub300->sg_request); 16418c2ecf20Sopenharmony_ci } else if (vub300->resp.common.header_type == RESPONSE_PIGGYBACKED) { 16428c2ecf20Sopenharmony_ci int offloaded_data_length = 16438c2ecf20Sopenharmony_ci vub300->resp.common.header_size - 16448c2ecf20Sopenharmony_ci sizeof(struct sd_register_header); 16458c2ecf20Sopenharmony_ci int register_count = offloaded_data_length >> 3; 16468c2ecf20Sopenharmony_ci int ri = 0; 16478c2ecf20Sopenharmony_ci while (register_count--) { 16488c2ecf20Sopenharmony_ci add_offloaded_reg(vub300, &vub300->resp.pig.reg[ri]); 16498c2ecf20Sopenharmony_ci ri += 1; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci vub300->resp.common.header_size = 16528c2ecf20Sopenharmony_ci sizeof(struct sd_register_header); 16538c2ecf20Sopenharmony_ci vub300->resp.common.header_type = 0x00; 16548c2ecf20Sopenharmony_ci cmd->error = 0; 16558c2ecf20Sopenharmony_ci } else if (vub300->resp.common.header_type == RESPONSE_PIG_DISABLED) { 16568c2ecf20Sopenharmony_ci int offloaded_data_length = 16578c2ecf20Sopenharmony_ci vub300->resp.common.header_size - 16588c2ecf20Sopenharmony_ci sizeof(struct sd_register_header); 16598c2ecf20Sopenharmony_ci int register_count = offloaded_data_length >> 3; 16608c2ecf20Sopenharmony_ci int ri = 0; 16618c2ecf20Sopenharmony_ci while (register_count--) { 16628c2ecf20Sopenharmony_ci add_offloaded_reg(vub300, &vub300->resp.pig.reg[ri]); 16638c2ecf20Sopenharmony_ci ri += 1; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 16668c2ecf20Sopenharmony_ci if (vub300->irqs_queued) { 16678c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 16688c2ecf20Sopenharmony_ci } else if (vub300->irq_enabled) { 16698c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 16708c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 0); 16718c2ecf20Sopenharmony_ci } else { 16728c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci vub300->irq_disabled = 1; 16758c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 16768c2ecf20Sopenharmony_ci vub300->resp.common.header_size = 16778c2ecf20Sopenharmony_ci sizeof(struct sd_register_header); 16788c2ecf20Sopenharmony_ci vub300->resp.common.header_type = 0x00; 16798c2ecf20Sopenharmony_ci cmd->error = 0; 16808c2ecf20Sopenharmony_ci } else if (vub300->resp.common.header_type == RESPONSE_PIG_ENABLED) { 16818c2ecf20Sopenharmony_ci int offloaded_data_length = 16828c2ecf20Sopenharmony_ci vub300->resp.common.header_size - 16838c2ecf20Sopenharmony_ci sizeof(struct sd_register_header); 16848c2ecf20Sopenharmony_ci int register_count = offloaded_data_length >> 3; 16858c2ecf20Sopenharmony_ci int ri = 0; 16868c2ecf20Sopenharmony_ci while (register_count--) { 16878c2ecf20Sopenharmony_ci add_offloaded_reg(vub300, &vub300->resp.pig.reg[ri]); 16888c2ecf20Sopenharmony_ci ri += 1; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 16918c2ecf20Sopenharmony_ci if (vub300->irqs_queued) { 16928c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 16938c2ecf20Sopenharmony_ci } else if (vub300->irq_enabled) { 16948c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 16958c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 0); 16968c2ecf20Sopenharmony_ci } else { 16978c2ecf20Sopenharmony_ci vub300->irqs_queued += 1; 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci vub300->irq_disabled = 0; 17008c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 17018c2ecf20Sopenharmony_ci vub300->resp.common.header_size = 17028c2ecf20Sopenharmony_ci sizeof(struct sd_register_header); 17038c2ecf20Sopenharmony_ci vub300->resp.common.header_type = 0x00; 17048c2ecf20Sopenharmony_ci cmd->error = 0; 17058c2ecf20Sopenharmony_ci } else { 17068c2ecf20Sopenharmony_ci cmd->error = -EINVAL; 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic void construct_request_response(struct vub300_mmc_host *vub300, 17118c2ecf20Sopenharmony_ci struct mmc_command *cmd) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci int resp_len = vub300->resp_len; 17148c2ecf20Sopenharmony_ci int less_cmd = (17 == resp_len) ? resp_len : resp_len - 1; 17158c2ecf20Sopenharmony_ci int bytes = 3 & less_cmd; 17168c2ecf20Sopenharmony_ci int words = less_cmd >> 2; 17178c2ecf20Sopenharmony_ci u8 *r = vub300->resp.response.command_response; 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (!resp_len) 17208c2ecf20Sopenharmony_ci return; 17218c2ecf20Sopenharmony_ci if (bytes == 3) { 17228c2ecf20Sopenharmony_ci cmd->resp[words] = (r[1 + (words << 2)] << 24) 17238c2ecf20Sopenharmony_ci | (r[2 + (words << 2)] << 16) 17248c2ecf20Sopenharmony_ci | (r[3 + (words << 2)] << 8); 17258c2ecf20Sopenharmony_ci } else if (bytes == 2) { 17268c2ecf20Sopenharmony_ci cmd->resp[words] = (r[1 + (words << 2)] << 24) 17278c2ecf20Sopenharmony_ci | (r[2 + (words << 2)] << 16); 17288c2ecf20Sopenharmony_ci } else if (bytes == 1) { 17298c2ecf20Sopenharmony_ci cmd->resp[words] = (r[1 + (words << 2)] << 24); 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci while (words-- > 0) { 17328c2ecf20Sopenharmony_ci cmd->resp[words] = (r[1 + (words << 2)] << 24) 17338c2ecf20Sopenharmony_ci | (r[2 + (words << 2)] << 16) 17348c2ecf20Sopenharmony_ci | (r[3 + (words << 2)] << 8) 17358c2ecf20Sopenharmony_ci | (r[4 + (words << 2)] << 0); 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci if ((cmd->opcode == 53) && (0x000000FF & cmd->resp[0])) 17388c2ecf20Sopenharmony_ci cmd->resp[0] &= 0xFFFFFF00; 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci/* this thread runs only when there is an upper level command req outstanding */ 17428c2ecf20Sopenharmony_cistatic void vub300_cmndwork_thread(struct work_struct *work) 17438c2ecf20Sopenharmony_ci{ 17448c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = 17458c2ecf20Sopenharmony_ci container_of(work, struct vub300_mmc_host, cmndwork); 17468c2ecf20Sopenharmony_ci if (!vub300->interface) { 17478c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 17488c2ecf20Sopenharmony_ci return; 17498c2ecf20Sopenharmony_ci } else { 17508c2ecf20Sopenharmony_ci struct mmc_request *req = vub300->req; 17518c2ecf20Sopenharmony_ci struct mmc_command *cmd = vub300->cmd; 17528c2ecf20Sopenharmony_ci struct mmc_data *data = vub300->data; 17538c2ecf20Sopenharmony_ci int data_length; 17548c2ecf20Sopenharmony_ci mutex_lock(&vub300->cmd_mutex); 17558c2ecf20Sopenharmony_ci init_completion(&vub300->command_complete); 17568c2ecf20Sopenharmony_ci if (likely(vub300->vub_name[0]) || !vub300->mmc->card) { 17578c2ecf20Sopenharmony_ci /* 17588c2ecf20Sopenharmony_ci * the name of the EMPTY Pseudo firmware file 17598c2ecf20Sopenharmony_ci * is used as a flag to indicate that the file 17608c2ecf20Sopenharmony_ci * has been already downloaded to the VUB300 chip 17618c2ecf20Sopenharmony_ci */ 17628c2ecf20Sopenharmony_ci } else if (0 == vub300->mmc->card->sdio_funcs) { 17638c2ecf20Sopenharmony_ci strncpy(vub300->vub_name, "SD memory device", 17648c2ecf20Sopenharmony_ci sizeof(vub300->vub_name)); 17658c2ecf20Sopenharmony_ci } else { 17668c2ecf20Sopenharmony_ci download_offload_pseudocode(vub300); 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci send_command(vub300); 17698c2ecf20Sopenharmony_ci if (!data) 17708c2ecf20Sopenharmony_ci data_length = 0; 17718c2ecf20Sopenharmony_ci else if (MMC_DATA_READ & data->flags) 17728c2ecf20Sopenharmony_ci data_length = __command_read_data(vub300, cmd, data); 17738c2ecf20Sopenharmony_ci else 17748c2ecf20Sopenharmony_ci data_length = __command_write_data(vub300, cmd, data); 17758c2ecf20Sopenharmony_ci __vub300_command_response(vub300, cmd, data, data_length); 17768c2ecf20Sopenharmony_ci vub300->req = NULL; 17778c2ecf20Sopenharmony_ci vub300->cmd = NULL; 17788c2ecf20Sopenharmony_ci vub300->data = NULL; 17798c2ecf20Sopenharmony_ci if (cmd->error) { 17808c2ecf20Sopenharmony_ci if (cmd->error == -ENOMEDIUM) 17818c2ecf20Sopenharmony_ci check_vub300_port_status(vub300); 17828c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 17838c2ecf20Sopenharmony_ci mmc_request_done(vub300->mmc, req); 17848c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 17858c2ecf20Sopenharmony_ci return; 17868c2ecf20Sopenharmony_ci } else { 17878c2ecf20Sopenharmony_ci construct_request_response(vub300, cmd); 17888c2ecf20Sopenharmony_ci vub300->resp_len = 0; 17898c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 17908c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 17918c2ecf20Sopenharmony_ci mmc_request_done(vub300->mmc, req); 17928c2ecf20Sopenharmony_ci return; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic int examine_cyclic_buffer(struct vub300_mmc_host *vub300, 17988c2ecf20Sopenharmony_ci struct mmc_command *cmd, u8 Function) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_mmc_request */ 18018c2ecf20Sopenharmony_ci u8 cmd0 = 0xFF & (cmd->arg >> 24); 18028c2ecf20Sopenharmony_ci u8 cmd1 = 0xFF & (cmd->arg >> 16); 18038c2ecf20Sopenharmony_ci u8 cmd2 = 0xFF & (cmd->arg >> 8); 18048c2ecf20Sopenharmony_ci u8 cmd3 = 0xFF & (cmd->arg >> 0); 18058c2ecf20Sopenharmony_ci int first = MAXREGMASK & vub300->fn[Function].offload_point; 18068c2ecf20Sopenharmony_ci struct offload_registers_access *rf = &vub300->fn[Function].reg[first]; 18078c2ecf20Sopenharmony_ci if (cmd0 == rf->command_byte[0] && 18088c2ecf20Sopenharmony_ci cmd1 == rf->command_byte[1] && 18098c2ecf20Sopenharmony_ci cmd2 == rf->command_byte[2] && 18108c2ecf20Sopenharmony_ci cmd3 == rf->command_byte[3]) { 18118c2ecf20Sopenharmony_ci u8 checksum = 0x00; 18128c2ecf20Sopenharmony_ci cmd->resp[1] = checksum << 24; 18138c2ecf20Sopenharmony_ci cmd->resp[0] = (rf->Respond_Byte[0] << 24) 18148c2ecf20Sopenharmony_ci | (rf->Respond_Byte[1] << 16) 18158c2ecf20Sopenharmony_ci | (rf->Respond_Byte[2] << 8) 18168c2ecf20Sopenharmony_ci | (rf->Respond_Byte[3] << 0); 18178c2ecf20Sopenharmony_ci vub300->fn[Function].offload_point += 1; 18188c2ecf20Sopenharmony_ci vub300->fn[Function].offload_count -= 1; 18198c2ecf20Sopenharmony_ci vub300->total_offload_count -= 1; 18208c2ecf20Sopenharmony_ci return 1; 18218c2ecf20Sopenharmony_ci } else { 18228c2ecf20Sopenharmony_ci int delta = 1; /* because it does not match the first one */ 18238c2ecf20Sopenharmony_ci u8 register_count = vub300->fn[Function].offload_count - 1; 18248c2ecf20Sopenharmony_ci u32 register_point = vub300->fn[Function].offload_point + 1; 18258c2ecf20Sopenharmony_ci while (0 < register_count) { 18268c2ecf20Sopenharmony_ci int point = MAXREGMASK & register_point; 18278c2ecf20Sopenharmony_ci struct offload_registers_access *r = 18288c2ecf20Sopenharmony_ci &vub300->fn[Function].reg[point]; 18298c2ecf20Sopenharmony_ci if (cmd0 == r->command_byte[0] && 18308c2ecf20Sopenharmony_ci cmd1 == r->command_byte[1] && 18318c2ecf20Sopenharmony_ci cmd2 == r->command_byte[2] && 18328c2ecf20Sopenharmony_ci cmd3 == r->command_byte[3]) { 18338c2ecf20Sopenharmony_ci u8 checksum = 0x00; 18348c2ecf20Sopenharmony_ci cmd->resp[1] = checksum << 24; 18358c2ecf20Sopenharmony_ci cmd->resp[0] = (r->Respond_Byte[0] << 24) 18368c2ecf20Sopenharmony_ci | (r->Respond_Byte[1] << 16) 18378c2ecf20Sopenharmony_ci | (r->Respond_Byte[2] << 8) 18388c2ecf20Sopenharmony_ci | (r->Respond_Byte[3] << 0); 18398c2ecf20Sopenharmony_ci vub300->fn[Function].offload_point += delta; 18408c2ecf20Sopenharmony_ci vub300->fn[Function].offload_count -= delta; 18418c2ecf20Sopenharmony_ci vub300->total_offload_count -= delta; 18428c2ecf20Sopenharmony_ci return 1; 18438c2ecf20Sopenharmony_ci } else { 18448c2ecf20Sopenharmony_ci register_point += 1; 18458c2ecf20Sopenharmony_ci register_count -= 1; 18468c2ecf20Sopenharmony_ci delta += 1; 18478c2ecf20Sopenharmony_ci continue; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci return 0; 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci} 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_cistatic int satisfy_request_from_offloaded_data(struct vub300_mmc_host *vub300, 18558c2ecf20Sopenharmony_ci struct mmc_command *cmd) 18568c2ecf20Sopenharmony_ci{ 18578c2ecf20Sopenharmony_ci /* cmd_mutex is held by vub300_mmc_request */ 18588c2ecf20Sopenharmony_ci u8 regs = vub300->dynamic_register_count; 18598c2ecf20Sopenharmony_ci u8 i = 0; 18608c2ecf20Sopenharmony_ci u8 func = FUN(cmd); 18618c2ecf20Sopenharmony_ci u32 reg = REG(cmd); 18628c2ecf20Sopenharmony_ci while (0 < regs--) { 18638c2ecf20Sopenharmony_ci if ((vub300->sdio_register[i].func_num == func) && 18648c2ecf20Sopenharmony_ci (vub300->sdio_register[i].sdio_reg == reg)) { 18658c2ecf20Sopenharmony_ci if (!vub300->sdio_register[i].prepared) { 18668c2ecf20Sopenharmony_ci return 0; 18678c2ecf20Sopenharmony_ci } else if ((0x80000000 & cmd->arg) == 0x80000000) { 18688c2ecf20Sopenharmony_ci /* 18698c2ecf20Sopenharmony_ci * a write to a dynamic register 18708c2ecf20Sopenharmony_ci * nullifies our offloaded value 18718c2ecf20Sopenharmony_ci */ 18728c2ecf20Sopenharmony_ci vub300->sdio_register[i].prepared = 0; 18738c2ecf20Sopenharmony_ci return 0; 18748c2ecf20Sopenharmony_ci } else { 18758c2ecf20Sopenharmony_ci u8 checksum = 0x00; 18768c2ecf20Sopenharmony_ci u8 rsp0 = 0x00; 18778c2ecf20Sopenharmony_ci u8 rsp1 = 0x00; 18788c2ecf20Sopenharmony_ci u8 rsp2 = vub300->sdio_register[i].response; 18798c2ecf20Sopenharmony_ci u8 rsp3 = vub300->sdio_register[i].regvalue; 18808c2ecf20Sopenharmony_ci vub300->sdio_register[i].prepared = 0; 18818c2ecf20Sopenharmony_ci cmd->resp[1] = checksum << 24; 18828c2ecf20Sopenharmony_ci cmd->resp[0] = (rsp0 << 24) 18838c2ecf20Sopenharmony_ci | (rsp1 << 16) 18848c2ecf20Sopenharmony_ci | (rsp2 << 8) 18858c2ecf20Sopenharmony_ci | (rsp3 << 0); 18868c2ecf20Sopenharmony_ci return 1; 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci } else { 18898c2ecf20Sopenharmony_ci i += 1; 18908c2ecf20Sopenharmony_ci continue; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci } 18938c2ecf20Sopenharmony_ci if (vub300->total_offload_count == 0) 18948c2ecf20Sopenharmony_ci return 0; 18958c2ecf20Sopenharmony_ci else if (vub300->fn[func].offload_count == 0) 18968c2ecf20Sopenharmony_ci return 0; 18978c2ecf20Sopenharmony_ci else 18988c2ecf20Sopenharmony_ci return examine_cyclic_buffer(vub300, cmd, func); 18998c2ecf20Sopenharmony_ci} 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_cistatic void vub300_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 19028c2ecf20Sopenharmony_ci{ /* NOT irq */ 19038c2ecf20Sopenharmony_ci struct mmc_command *cmd = req->cmd; 19048c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = mmc_priv(mmc); 19058c2ecf20Sopenharmony_ci if (!vub300->interface) { 19068c2ecf20Sopenharmony_ci cmd->error = -ESHUTDOWN; 19078c2ecf20Sopenharmony_ci mmc_request_done(mmc, req); 19088c2ecf20Sopenharmony_ci return; 19098c2ecf20Sopenharmony_ci } else { 19108c2ecf20Sopenharmony_ci struct mmc_data *data = req->data; 19118c2ecf20Sopenharmony_ci if (!vub300->card_powered) { 19128c2ecf20Sopenharmony_ci cmd->error = -ENOMEDIUM; 19138c2ecf20Sopenharmony_ci mmc_request_done(mmc, req); 19148c2ecf20Sopenharmony_ci return; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci if (!vub300->card_present) { 19178c2ecf20Sopenharmony_ci cmd->error = -ENOMEDIUM; 19188c2ecf20Sopenharmony_ci mmc_request_done(mmc, req); 19198c2ecf20Sopenharmony_ci return; 19208c2ecf20Sopenharmony_ci } 19218c2ecf20Sopenharmony_ci if (vub300->usb_transport_fail) { 19228c2ecf20Sopenharmony_ci cmd->error = vub300->usb_transport_fail; 19238c2ecf20Sopenharmony_ci mmc_request_done(mmc, req); 19248c2ecf20Sopenharmony_ci return; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci if (!vub300->interface) { 19278c2ecf20Sopenharmony_ci cmd->error = -ENODEV; 19288c2ecf20Sopenharmony_ci mmc_request_done(mmc, req); 19298c2ecf20Sopenharmony_ci return; 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 19328c2ecf20Sopenharmony_ci mutex_lock(&vub300->cmd_mutex); 19338c2ecf20Sopenharmony_ci mod_timer(&vub300->inactivity_timer, jiffies + HZ); 19348c2ecf20Sopenharmony_ci /* 19358c2ecf20Sopenharmony_ci * for performance we have to return immediately 19368c2ecf20Sopenharmony_ci * if the requested data has been offloaded 19378c2ecf20Sopenharmony_ci */ 19388c2ecf20Sopenharmony_ci if (cmd->opcode == 52 && 19398c2ecf20Sopenharmony_ci satisfy_request_from_offloaded_data(vub300, cmd)) { 19408c2ecf20Sopenharmony_ci cmd->error = 0; 19418c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 19428c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 19438c2ecf20Sopenharmony_ci mmc_request_done(mmc, req); 19448c2ecf20Sopenharmony_ci return; 19458c2ecf20Sopenharmony_ci } else { 19468c2ecf20Sopenharmony_ci vub300->cmd = cmd; 19478c2ecf20Sopenharmony_ci vub300->req = req; 19488c2ecf20Sopenharmony_ci vub300->data = data; 19498c2ecf20Sopenharmony_ci if (data) 19508c2ecf20Sopenharmony_ci vub300->datasize = data->blksz * data->blocks; 19518c2ecf20Sopenharmony_ci else 19528c2ecf20Sopenharmony_ci vub300->datasize = 0; 19538c2ecf20Sopenharmony_ci vub300_queue_cmnd_work(vub300); 19548c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 19558c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 19568c2ecf20Sopenharmony_ci /* 19578c2ecf20Sopenharmony_ci * the kernel lock diagnostics complain 19588c2ecf20Sopenharmony_ci * if the cmd_mutex * is "passed on" 19598c2ecf20Sopenharmony_ci * to the cmndwork thread, 19608c2ecf20Sopenharmony_ci * so we must release it now 19618c2ecf20Sopenharmony_ci * and re-acquire it in the cmndwork thread 19628c2ecf20Sopenharmony_ci */ 19638c2ecf20Sopenharmony_ci } 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_cistatic void __set_clock_speed(struct vub300_mmc_host *vub300, u8 buf[8], 19688c2ecf20Sopenharmony_ci struct mmc_ios *ios) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci int buf_array_size = 8; /* ARRAY_SIZE(buf) does not work !!! */ 19718c2ecf20Sopenharmony_ci int retval; 19728c2ecf20Sopenharmony_ci u32 kHzClock; 19738c2ecf20Sopenharmony_ci if (ios->clock >= 48000000) 19748c2ecf20Sopenharmony_ci kHzClock = 48000; 19758c2ecf20Sopenharmony_ci else if (ios->clock >= 24000000) 19768c2ecf20Sopenharmony_ci kHzClock = 24000; 19778c2ecf20Sopenharmony_ci else if (ios->clock >= 20000000) 19788c2ecf20Sopenharmony_ci kHzClock = 20000; 19798c2ecf20Sopenharmony_ci else if (ios->clock >= 15000000) 19808c2ecf20Sopenharmony_ci kHzClock = 15000; 19818c2ecf20Sopenharmony_ci else if (ios->clock >= 200000) 19828c2ecf20Sopenharmony_ci kHzClock = 200; 19838c2ecf20Sopenharmony_ci else 19848c2ecf20Sopenharmony_ci kHzClock = 0; 19858c2ecf20Sopenharmony_ci { 19868c2ecf20Sopenharmony_ci int i; 19878c2ecf20Sopenharmony_ci u64 c = kHzClock; 19888c2ecf20Sopenharmony_ci for (i = 0; i < buf_array_size; i++) { 19898c2ecf20Sopenharmony_ci buf[i] = c; 19908c2ecf20Sopenharmony_ci c >>= 8; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci retval = 19948c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), 19958c2ecf20Sopenharmony_ci SET_CLOCK_SPEED, 19968c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 19978c2ecf20Sopenharmony_ci 0x00, 0x00, buf, buf_array_size, 1000); 19988c2ecf20Sopenharmony_ci if (retval != 8) { 19998c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, "SET_CLOCK_SPEED" 20008c2ecf20Sopenharmony_ci " %dkHz failed with retval=%d\n", kHzClock, retval); 20018c2ecf20Sopenharmony_ci } else { 20028c2ecf20Sopenharmony_ci dev_dbg(&vub300->udev->dev, "SET_CLOCK_SPEED" 20038c2ecf20Sopenharmony_ci " %dkHz\n", kHzClock); 20048c2ecf20Sopenharmony_ci } 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_cistatic void vub300_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 20088c2ecf20Sopenharmony_ci{ /* NOT irq */ 20098c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = mmc_priv(mmc); 20108c2ecf20Sopenharmony_ci if (!vub300->interface) 20118c2ecf20Sopenharmony_ci return; 20128c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 20138c2ecf20Sopenharmony_ci mutex_lock(&vub300->cmd_mutex); 20148c2ecf20Sopenharmony_ci if ((ios->power_mode == MMC_POWER_OFF) && vub300->card_powered) { 20158c2ecf20Sopenharmony_ci vub300->card_powered = 0; 20168c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), 20178c2ecf20Sopenharmony_ci SET_SD_POWER, 20188c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 20198c2ecf20Sopenharmony_ci 0x0000, 0x0000, NULL, 0, 1000); 20208c2ecf20Sopenharmony_ci /* must wait for the VUB300 u-proc to boot up */ 20218c2ecf20Sopenharmony_ci msleep(600); 20228c2ecf20Sopenharmony_ci } else if ((ios->power_mode == MMC_POWER_UP) && !vub300->card_powered) { 20238c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), 20248c2ecf20Sopenharmony_ci SET_SD_POWER, 20258c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 20268c2ecf20Sopenharmony_ci 0x0001, 0x0000, NULL, 0, 1000); 20278c2ecf20Sopenharmony_ci msleep(600); 20288c2ecf20Sopenharmony_ci vub300->card_powered = 1; 20298c2ecf20Sopenharmony_ci } else if (ios->power_mode == MMC_POWER_ON) { 20308c2ecf20Sopenharmony_ci u8 *buf = kmalloc(8, GFP_KERNEL); 20318c2ecf20Sopenharmony_ci if (buf) { 20328c2ecf20Sopenharmony_ci __set_clock_speed(vub300, buf, ios); 20338c2ecf20Sopenharmony_ci kfree(buf); 20348c2ecf20Sopenharmony_ci } 20358c2ecf20Sopenharmony_ci } else { 20368c2ecf20Sopenharmony_ci /* this should mean no change of state */ 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 20398c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_cistatic int vub300_mmc_get_ro(struct mmc_host *mmc) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = mmc_priv(mmc); 20458c2ecf20Sopenharmony_ci return vub300->read_only; 20468c2ecf20Sopenharmony_ci} 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_cistatic void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable) 20498c2ecf20Sopenharmony_ci{ /* NOT irq */ 20508c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = mmc_priv(mmc); 20518c2ecf20Sopenharmony_ci if (!vub300->interface) 20528c2ecf20Sopenharmony_ci return; 20538c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 20548c2ecf20Sopenharmony_ci if (enable) { 20558c2ecf20Sopenharmony_ci set_current_state(TASK_RUNNING); 20568c2ecf20Sopenharmony_ci mutex_lock(&vub300->irq_mutex); 20578c2ecf20Sopenharmony_ci if (vub300->irqs_queued) { 20588c2ecf20Sopenharmony_ci vub300->irqs_queued -= 1; 20598c2ecf20Sopenharmony_ci mmc_signal_sdio_irq(vub300->mmc); 20608c2ecf20Sopenharmony_ci } else if (vub300->irq_disabled) { 20618c2ecf20Sopenharmony_ci vub300->irq_disabled = 0; 20628c2ecf20Sopenharmony_ci vub300->irq_enabled = 1; 20638c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 0); 20648c2ecf20Sopenharmony_ci } else if (vub300->irq_enabled) { 20658c2ecf20Sopenharmony_ci /* this should not happen, so we will just ignore it */ 20668c2ecf20Sopenharmony_ci } else { 20678c2ecf20Sopenharmony_ci vub300->irq_enabled = 1; 20688c2ecf20Sopenharmony_ci vub300_queue_poll_work(vub300, 0); 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci mutex_unlock(&vub300->irq_mutex); 20718c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 20728c2ecf20Sopenharmony_ci } else { 20738c2ecf20Sopenharmony_ci vub300->irq_enabled = 0; 20748c2ecf20Sopenharmony_ci } 20758c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 20768c2ecf20Sopenharmony_ci} 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_cistatic const struct mmc_host_ops vub300_mmc_ops = { 20798c2ecf20Sopenharmony_ci .request = vub300_mmc_request, 20808c2ecf20Sopenharmony_ci .set_ios = vub300_mmc_set_ios, 20818c2ecf20Sopenharmony_ci .get_ro = vub300_mmc_get_ro, 20828c2ecf20Sopenharmony_ci .enable_sdio_irq = vub300_enable_sdio_irq, 20838c2ecf20Sopenharmony_ci}; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cistatic int vub300_probe(struct usb_interface *interface, 20868c2ecf20Sopenharmony_ci const struct usb_device_id *id) 20878c2ecf20Sopenharmony_ci{ /* NOT irq */ 20888c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300; 20898c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 20908c2ecf20Sopenharmony_ci struct usb_device *udev = usb_get_dev(interface_to_usbdev(interface)); 20918c2ecf20Sopenharmony_ci int i; 20928c2ecf20Sopenharmony_ci int retval = -ENOMEM; 20938c2ecf20Sopenharmony_ci struct urb *command_out_urb; 20948c2ecf20Sopenharmony_ci struct urb *command_res_urb; 20958c2ecf20Sopenharmony_ci struct mmc_host *mmc; 20968c2ecf20Sopenharmony_ci char manufacturer[48]; 20978c2ecf20Sopenharmony_ci char product[32]; 20988c2ecf20Sopenharmony_ci char serial_number[32]; 20998c2ecf20Sopenharmony_ci usb_string(udev, udev->descriptor.iManufacturer, manufacturer, 21008c2ecf20Sopenharmony_ci sizeof(manufacturer)); 21018c2ecf20Sopenharmony_ci usb_string(udev, udev->descriptor.iProduct, product, sizeof(product)); 21028c2ecf20Sopenharmony_ci usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 21038c2ecf20Sopenharmony_ci sizeof(serial_number)); 21048c2ecf20Sopenharmony_ci dev_info(&udev->dev, "probing VID:PID(%04X:%04X) %s %s %s\n", 21058c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor), 21068c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct), 21078c2ecf20Sopenharmony_ci manufacturer, product, serial_number); 21088c2ecf20Sopenharmony_ci command_out_urb = usb_alloc_urb(0, GFP_KERNEL); 21098c2ecf20Sopenharmony_ci if (!command_out_urb) { 21108c2ecf20Sopenharmony_ci retval = -ENOMEM; 21118c2ecf20Sopenharmony_ci goto error0; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci command_res_urb = usb_alloc_urb(0, GFP_KERNEL); 21148c2ecf20Sopenharmony_ci if (!command_res_urb) { 21158c2ecf20Sopenharmony_ci retval = -ENOMEM; 21168c2ecf20Sopenharmony_ci goto error1; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci /* this also allocates memory for our VUB300 mmc host device */ 21198c2ecf20Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct vub300_mmc_host), &udev->dev); 21208c2ecf20Sopenharmony_ci if (!mmc) { 21218c2ecf20Sopenharmony_ci retval = -ENOMEM; 21228c2ecf20Sopenharmony_ci dev_err(&udev->dev, "not enough memory for the mmc_host\n"); 21238c2ecf20Sopenharmony_ci goto error4; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci /* MMC core transfer sizes tunable parameters */ 21268c2ecf20Sopenharmony_ci mmc->caps = 0; 21278c2ecf20Sopenharmony_ci if (!force_1_bit_data_xfers) 21288c2ecf20Sopenharmony_ci mmc->caps |= MMC_CAP_4_BIT_DATA; 21298c2ecf20Sopenharmony_ci if (!force_polling_for_irqs) 21308c2ecf20Sopenharmony_ci mmc->caps |= MMC_CAP_SDIO_IRQ; 21318c2ecf20Sopenharmony_ci mmc->caps &= ~MMC_CAP_NEEDS_POLL; 21328c2ecf20Sopenharmony_ci /* 21338c2ecf20Sopenharmony_ci * MMC_CAP_NEEDS_POLL causes core.c:mmc_rescan() to poll 21348c2ecf20Sopenharmony_ci * for devices which results in spurious CMD7's being 21358c2ecf20Sopenharmony_ci * issued which stops some SDIO cards from working 21368c2ecf20Sopenharmony_ci */ 21378c2ecf20Sopenharmony_ci if (limit_speed_to_24_MHz) { 21388c2ecf20Sopenharmony_ci mmc->caps |= MMC_CAP_MMC_HIGHSPEED; 21398c2ecf20Sopenharmony_ci mmc->caps |= MMC_CAP_SD_HIGHSPEED; 21408c2ecf20Sopenharmony_ci mmc->f_max = 24000000; 21418c2ecf20Sopenharmony_ci dev_info(&udev->dev, "limiting SDIO speed to 24_MHz\n"); 21428c2ecf20Sopenharmony_ci } else { 21438c2ecf20Sopenharmony_ci mmc->caps |= MMC_CAP_MMC_HIGHSPEED; 21448c2ecf20Sopenharmony_ci mmc->caps |= MMC_CAP_SD_HIGHSPEED; 21458c2ecf20Sopenharmony_ci mmc->f_max = 48000000; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci mmc->f_min = 200000; 21488c2ecf20Sopenharmony_ci mmc->max_blk_count = 511; 21498c2ecf20Sopenharmony_ci mmc->max_blk_size = 512; 21508c2ecf20Sopenharmony_ci mmc->max_segs = 128; 21518c2ecf20Sopenharmony_ci if (force_max_req_size) 21528c2ecf20Sopenharmony_ci mmc->max_req_size = force_max_req_size * 1024; 21538c2ecf20Sopenharmony_ci else 21548c2ecf20Sopenharmony_ci mmc->max_req_size = 64 * 1024; 21558c2ecf20Sopenharmony_ci mmc->max_seg_size = mmc->max_req_size; 21568c2ecf20Sopenharmony_ci mmc->ocr_avail = 0; 21578c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_165_195; 21588c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_20_21; 21598c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_21_22; 21608c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_22_23; 21618c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_23_24; 21628c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_24_25; 21638c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_25_26; 21648c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_26_27; 21658c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_27_28; 21668c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_28_29; 21678c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_29_30; 21688c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_30_31; 21698c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_31_32; 21708c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_32_33; 21718c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_33_34; 21728c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_34_35; 21738c2ecf20Sopenharmony_ci mmc->ocr_avail |= MMC_VDD_35_36; 21748c2ecf20Sopenharmony_ci mmc->ops = &vub300_mmc_ops; 21758c2ecf20Sopenharmony_ci vub300 = mmc_priv(mmc); 21768c2ecf20Sopenharmony_ci vub300->mmc = mmc; 21778c2ecf20Sopenharmony_ci vub300->card_powered = 0; 21788c2ecf20Sopenharmony_ci vub300->bus_width = 0; 21798c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[0] = 0x00; 21808c2ecf20Sopenharmony_ci vub300->cmnd.head.block_size[1] = 0x00; 21818c2ecf20Sopenharmony_ci vub300->app_spec = 0; 21828c2ecf20Sopenharmony_ci mutex_init(&vub300->cmd_mutex); 21838c2ecf20Sopenharmony_ci mutex_init(&vub300->irq_mutex); 21848c2ecf20Sopenharmony_ci vub300->command_out_urb = command_out_urb; 21858c2ecf20Sopenharmony_ci vub300->command_res_urb = command_res_urb; 21868c2ecf20Sopenharmony_ci vub300->usb_timed_out = 0; 21878c2ecf20Sopenharmony_ci vub300->dynamic_register_count = 0; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vub300->fn); i++) { 21908c2ecf20Sopenharmony_ci vub300->fn[i].offload_point = 0; 21918c2ecf20Sopenharmony_ci vub300->fn[i].offload_count = 0; 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci vub300->total_offload_count = 0; 21958c2ecf20Sopenharmony_ci vub300->irq_enabled = 0; 21968c2ecf20Sopenharmony_ci vub300->irq_disabled = 0; 21978c2ecf20Sopenharmony_ci vub300->irqs_queued = 0; 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vub300->sdio_register); i++) 22008c2ecf20Sopenharmony_ci vub300->sdio_register[i++].activate = 0; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci vub300->udev = udev; 22038c2ecf20Sopenharmony_ci vub300->interface = interface; 22048c2ecf20Sopenharmony_ci vub300->cmnd_res_ep = 0; 22058c2ecf20Sopenharmony_ci vub300->cmnd_out_ep = 0; 22068c2ecf20Sopenharmony_ci vub300->data_inp_ep = 0; 22078c2ecf20Sopenharmony_ci vub300->data_out_ep = 0; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vub300->fbs); i++) 22108c2ecf20Sopenharmony_ci vub300->fbs[i] = 512; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci /* 22138c2ecf20Sopenharmony_ci * set up the endpoint information 22148c2ecf20Sopenharmony_ci * 22158c2ecf20Sopenharmony_ci * use the first pair of bulk-in and bulk-out 22168c2ecf20Sopenharmony_ci * endpoints for Command/Response+Interrupt 22178c2ecf20Sopenharmony_ci * 22188c2ecf20Sopenharmony_ci * use the second pair of bulk-in and bulk-out 22198c2ecf20Sopenharmony_ci * endpoints for Data In/Out 22208c2ecf20Sopenharmony_ci */ 22218c2ecf20Sopenharmony_ci vub300->large_usb_packets = 0; 22228c2ecf20Sopenharmony_ci iface_desc = interface->cur_altsetting; 22238c2ecf20Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 22248c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint = 22258c2ecf20Sopenharmony_ci &iface_desc->endpoint[i].desc; 22268c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, 22278c2ecf20Sopenharmony_ci "vub300 testing %s EndPoint(%d) %02X\n", 22288c2ecf20Sopenharmony_ci usb_endpoint_is_bulk_in(endpoint) ? "BULK IN" : 22298c2ecf20Sopenharmony_ci usb_endpoint_is_bulk_out(endpoint) ? "BULK OUT" : 22308c2ecf20Sopenharmony_ci "UNKNOWN", i, endpoint->bEndpointAddress); 22318c2ecf20Sopenharmony_ci if (endpoint->wMaxPacketSize > 64) 22328c2ecf20Sopenharmony_ci vub300->large_usb_packets = 1; 22338c2ecf20Sopenharmony_ci if (usb_endpoint_is_bulk_in(endpoint)) { 22348c2ecf20Sopenharmony_ci if (!vub300->cmnd_res_ep) { 22358c2ecf20Sopenharmony_ci vub300->cmnd_res_ep = 22368c2ecf20Sopenharmony_ci endpoint->bEndpointAddress; 22378c2ecf20Sopenharmony_ci } else if (!vub300->data_inp_ep) { 22388c2ecf20Sopenharmony_ci vub300->data_inp_ep = 22398c2ecf20Sopenharmony_ci endpoint->bEndpointAddress; 22408c2ecf20Sopenharmony_ci } else { 22418c2ecf20Sopenharmony_ci dev_warn(&vub300->udev->dev, 22428c2ecf20Sopenharmony_ci "ignoring" 22438c2ecf20Sopenharmony_ci " unexpected bulk_in endpoint"); 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci } else if (usb_endpoint_is_bulk_out(endpoint)) { 22468c2ecf20Sopenharmony_ci if (!vub300->cmnd_out_ep) { 22478c2ecf20Sopenharmony_ci vub300->cmnd_out_ep = 22488c2ecf20Sopenharmony_ci endpoint->bEndpointAddress; 22498c2ecf20Sopenharmony_ci } else if (!vub300->data_out_ep) { 22508c2ecf20Sopenharmony_ci vub300->data_out_ep = 22518c2ecf20Sopenharmony_ci endpoint->bEndpointAddress; 22528c2ecf20Sopenharmony_ci } else { 22538c2ecf20Sopenharmony_ci dev_warn(&vub300->udev->dev, 22548c2ecf20Sopenharmony_ci "ignoring" 22558c2ecf20Sopenharmony_ci " unexpected bulk_out endpoint"); 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci } else { 22588c2ecf20Sopenharmony_ci dev_warn(&vub300->udev->dev, 22598c2ecf20Sopenharmony_ci "vub300 ignoring EndPoint(%d) %02X", i, 22608c2ecf20Sopenharmony_ci endpoint->bEndpointAddress); 22618c2ecf20Sopenharmony_ci } 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci if (vub300->cmnd_res_ep && vub300->cmnd_out_ep && 22648c2ecf20Sopenharmony_ci vub300->data_inp_ep && vub300->data_out_ep) { 22658c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, 22668c2ecf20Sopenharmony_ci "vub300 %s packets" 22678c2ecf20Sopenharmony_ci " using EndPoints %02X %02X %02X %02X\n", 22688c2ecf20Sopenharmony_ci vub300->large_usb_packets ? "LARGE" : "SMALL", 22698c2ecf20Sopenharmony_ci vub300->cmnd_out_ep, vub300->cmnd_res_ep, 22708c2ecf20Sopenharmony_ci vub300->data_out_ep, vub300->data_inp_ep); 22718c2ecf20Sopenharmony_ci /* we have the expected EndPoints */ 22728c2ecf20Sopenharmony_ci } else { 22738c2ecf20Sopenharmony_ci dev_err(&vub300->udev->dev, 22748c2ecf20Sopenharmony_ci "Could not find two sets of bulk-in/out endpoint pairs\n"); 22758c2ecf20Sopenharmony_ci retval = -EINVAL; 22768c2ecf20Sopenharmony_ci goto error5; 22778c2ecf20Sopenharmony_ci } 22788c2ecf20Sopenharmony_ci retval = 22798c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), 22808c2ecf20Sopenharmony_ci GET_HC_INF0, 22818c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 22828c2ecf20Sopenharmony_ci 0x0000, 0x0000, &vub300->hc_info, 22838c2ecf20Sopenharmony_ci sizeof(vub300->hc_info), 1000); 22848c2ecf20Sopenharmony_ci if (retval < 0) 22858c2ecf20Sopenharmony_ci goto error5; 22868c2ecf20Sopenharmony_ci retval = 22878c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), 22888c2ecf20Sopenharmony_ci SET_ROM_WAIT_STATES, 22898c2ecf20Sopenharmony_ci USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 22908c2ecf20Sopenharmony_ci firmware_rom_wait_states, 0x0000, NULL, 0, 1000); 22918c2ecf20Sopenharmony_ci if (retval < 0) 22928c2ecf20Sopenharmony_ci goto error5; 22938c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, 22948c2ecf20Sopenharmony_ci "operating_mode = %s %s %d MHz %s %d byte USB packets\n", 22958c2ecf20Sopenharmony_ci (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL", 22968c2ecf20Sopenharmony_ci (mmc->caps & MMC_CAP_4_BIT_DATA) ? "4-bit" : "1-bit", 22978c2ecf20Sopenharmony_ci mmc->f_max / 1000000, 22988c2ecf20Sopenharmony_ci pad_input_to_usb_pkt ? "padding input data to" : "with", 22998c2ecf20Sopenharmony_ci vub300->large_usb_packets ? 512 : 64); 23008c2ecf20Sopenharmony_ci retval = 23018c2ecf20Sopenharmony_ci usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), 23028c2ecf20Sopenharmony_ci GET_SYSTEM_PORT_STATUS, 23038c2ecf20Sopenharmony_ci USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 23048c2ecf20Sopenharmony_ci 0x0000, 0x0000, &vub300->system_port_status, 23058c2ecf20Sopenharmony_ci sizeof(vub300->system_port_status), 1000); 23068c2ecf20Sopenharmony_ci if (retval < 0) { 23078c2ecf20Sopenharmony_ci goto error5; 23088c2ecf20Sopenharmony_ci } else if (sizeof(vub300->system_port_status) == retval) { 23098c2ecf20Sopenharmony_ci vub300->card_present = 23108c2ecf20Sopenharmony_ci (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; 23118c2ecf20Sopenharmony_ci vub300->read_only = 23128c2ecf20Sopenharmony_ci (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; 23138c2ecf20Sopenharmony_ci } else { 23148c2ecf20Sopenharmony_ci retval = -EINVAL; 23158c2ecf20Sopenharmony_ci goto error5; 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci usb_set_intfdata(interface, vub300); 23188c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread); 23198c2ecf20Sopenharmony_ci INIT_WORK(&vub300->cmndwork, vub300_cmndwork_thread); 23208c2ecf20Sopenharmony_ci INIT_WORK(&vub300->deadwork, vub300_deadwork_thread); 23218c2ecf20Sopenharmony_ci kref_init(&vub300->kref); 23228c2ecf20Sopenharmony_ci timer_setup(&vub300->sg_transfer_timer, vub300_sg_timed_out, 0); 23238c2ecf20Sopenharmony_ci kref_get(&vub300->kref); 23248c2ecf20Sopenharmony_ci timer_setup(&vub300->inactivity_timer, 23258c2ecf20Sopenharmony_ci vub300_inactivity_timer_expired, 0); 23268c2ecf20Sopenharmony_ci vub300->inactivity_timer.expires = jiffies + HZ; 23278c2ecf20Sopenharmony_ci add_timer(&vub300->inactivity_timer); 23288c2ecf20Sopenharmony_ci if (vub300->card_present) 23298c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, 23308c2ecf20Sopenharmony_ci "USB vub300 remote SDIO host controller[%d]" 23318c2ecf20Sopenharmony_ci "connected with SD/SDIO card inserted\n", 23328c2ecf20Sopenharmony_ci interface_to_InterfaceNumber(interface)); 23338c2ecf20Sopenharmony_ci else 23348c2ecf20Sopenharmony_ci dev_info(&vub300->udev->dev, 23358c2ecf20Sopenharmony_ci "USB vub300 remote SDIO host controller[%d]" 23368c2ecf20Sopenharmony_ci "connected with no SD/SDIO card inserted\n", 23378c2ecf20Sopenharmony_ci interface_to_InterfaceNumber(interface)); 23388c2ecf20Sopenharmony_ci retval = mmc_add_host(mmc); 23398c2ecf20Sopenharmony_ci if (retval) 23408c2ecf20Sopenharmony_ci goto error6; 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci return 0; 23438c2ecf20Sopenharmony_cierror6: 23448c2ecf20Sopenharmony_ci del_timer_sync(&vub300->inactivity_timer); 23458c2ecf20Sopenharmony_cierror5: 23468c2ecf20Sopenharmony_ci mmc_free_host(mmc); 23478c2ecf20Sopenharmony_ci /* 23488c2ecf20Sopenharmony_ci * and hence also frees vub300 23498c2ecf20Sopenharmony_ci * which is contained at the end of struct mmc 23508c2ecf20Sopenharmony_ci */ 23518c2ecf20Sopenharmony_cierror4: 23528c2ecf20Sopenharmony_ci usb_free_urb(command_res_urb); 23538c2ecf20Sopenharmony_cierror1: 23548c2ecf20Sopenharmony_ci usb_free_urb(command_out_urb); 23558c2ecf20Sopenharmony_cierror0: 23568c2ecf20Sopenharmony_ci usb_put_dev(udev); 23578c2ecf20Sopenharmony_ci return retval; 23588c2ecf20Sopenharmony_ci} 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_cistatic void vub300_disconnect(struct usb_interface *interface) 23618c2ecf20Sopenharmony_ci{ /* NOT irq */ 23628c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = usb_get_intfdata(interface); 23638c2ecf20Sopenharmony_ci if (!vub300 || !vub300->mmc) { 23648c2ecf20Sopenharmony_ci return; 23658c2ecf20Sopenharmony_ci } else { 23668c2ecf20Sopenharmony_ci struct mmc_host *mmc = vub300->mmc; 23678c2ecf20Sopenharmony_ci if (!vub300->mmc) { 23688c2ecf20Sopenharmony_ci return; 23698c2ecf20Sopenharmony_ci } else { 23708c2ecf20Sopenharmony_ci int ifnum = interface_to_InterfaceNumber(interface); 23718c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 23728c2ecf20Sopenharmony_ci /* prevent more I/O from starting */ 23738c2ecf20Sopenharmony_ci vub300->interface = NULL; 23748c2ecf20Sopenharmony_ci kref_put(&vub300->kref, vub300_delete); 23758c2ecf20Sopenharmony_ci mmc_remove_host(mmc); 23768c2ecf20Sopenharmony_ci pr_info("USB vub300 remote SDIO host controller[%d]" 23778c2ecf20Sopenharmony_ci " now disconnected", ifnum); 23788c2ecf20Sopenharmony_ci return; 23798c2ecf20Sopenharmony_ci } 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci} 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 23848c2ecf20Sopenharmony_cistatic int vub300_suspend(struct usb_interface *intf, pm_message_t message) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci return 0; 23878c2ecf20Sopenharmony_ci} 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_cistatic int vub300_resume(struct usb_interface *intf) 23908c2ecf20Sopenharmony_ci{ 23918c2ecf20Sopenharmony_ci return 0; 23928c2ecf20Sopenharmony_ci} 23938c2ecf20Sopenharmony_ci#else 23948c2ecf20Sopenharmony_ci#define vub300_suspend NULL 23958c2ecf20Sopenharmony_ci#define vub300_resume NULL 23968c2ecf20Sopenharmony_ci#endif 23978c2ecf20Sopenharmony_cistatic int vub300_pre_reset(struct usb_interface *intf) 23988c2ecf20Sopenharmony_ci{ /* NOT irq */ 23998c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); 24008c2ecf20Sopenharmony_ci mutex_lock(&vub300->cmd_mutex); 24018c2ecf20Sopenharmony_ci return 0; 24028c2ecf20Sopenharmony_ci} 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_cistatic int vub300_post_reset(struct usb_interface *intf) 24058c2ecf20Sopenharmony_ci{ /* NOT irq */ 24068c2ecf20Sopenharmony_ci struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); 24078c2ecf20Sopenharmony_ci /* we are sure no URBs are active - no locking needed */ 24088c2ecf20Sopenharmony_ci vub300->errors = -EPIPE; 24098c2ecf20Sopenharmony_ci mutex_unlock(&vub300->cmd_mutex); 24108c2ecf20Sopenharmony_ci return 0; 24118c2ecf20Sopenharmony_ci} 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_cistatic struct usb_driver vub300_driver = { 24148c2ecf20Sopenharmony_ci .name = "vub300", 24158c2ecf20Sopenharmony_ci .probe = vub300_probe, 24168c2ecf20Sopenharmony_ci .disconnect = vub300_disconnect, 24178c2ecf20Sopenharmony_ci .suspend = vub300_suspend, 24188c2ecf20Sopenharmony_ci .resume = vub300_resume, 24198c2ecf20Sopenharmony_ci .pre_reset = vub300_pre_reset, 24208c2ecf20Sopenharmony_ci .post_reset = vub300_post_reset, 24218c2ecf20Sopenharmony_ci .id_table = vub300_table, 24228c2ecf20Sopenharmony_ci .supports_autosuspend = 1, 24238c2ecf20Sopenharmony_ci}; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_cistatic int __init vub300_init(void) 24268c2ecf20Sopenharmony_ci{ /* NOT irq */ 24278c2ecf20Sopenharmony_ci int result; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X", 24308c2ecf20Sopenharmony_ci firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout); 24318c2ecf20Sopenharmony_ci cmndworkqueue = create_singlethread_workqueue("kvub300c"); 24328c2ecf20Sopenharmony_ci if (!cmndworkqueue) { 24338c2ecf20Sopenharmony_ci pr_err("not enough memory for the REQUEST workqueue"); 24348c2ecf20Sopenharmony_ci result = -ENOMEM; 24358c2ecf20Sopenharmony_ci goto out1; 24368c2ecf20Sopenharmony_ci } 24378c2ecf20Sopenharmony_ci pollworkqueue = create_singlethread_workqueue("kvub300p"); 24388c2ecf20Sopenharmony_ci if (!pollworkqueue) { 24398c2ecf20Sopenharmony_ci pr_err("not enough memory for the IRQPOLL workqueue"); 24408c2ecf20Sopenharmony_ci result = -ENOMEM; 24418c2ecf20Sopenharmony_ci goto out2; 24428c2ecf20Sopenharmony_ci } 24438c2ecf20Sopenharmony_ci deadworkqueue = create_singlethread_workqueue("kvub300d"); 24448c2ecf20Sopenharmony_ci if (!deadworkqueue) { 24458c2ecf20Sopenharmony_ci pr_err("not enough memory for the EXPIRED workqueue"); 24468c2ecf20Sopenharmony_ci result = -ENOMEM; 24478c2ecf20Sopenharmony_ci goto out3; 24488c2ecf20Sopenharmony_ci } 24498c2ecf20Sopenharmony_ci result = usb_register(&vub300_driver); 24508c2ecf20Sopenharmony_ci if (result) { 24518c2ecf20Sopenharmony_ci pr_err("usb_register failed. Error number %d", result); 24528c2ecf20Sopenharmony_ci goto out4; 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci return 0; 24558c2ecf20Sopenharmony_ciout4: 24568c2ecf20Sopenharmony_ci destroy_workqueue(deadworkqueue); 24578c2ecf20Sopenharmony_ciout3: 24588c2ecf20Sopenharmony_ci destroy_workqueue(pollworkqueue); 24598c2ecf20Sopenharmony_ciout2: 24608c2ecf20Sopenharmony_ci destroy_workqueue(cmndworkqueue); 24618c2ecf20Sopenharmony_ciout1: 24628c2ecf20Sopenharmony_ci return result; 24638c2ecf20Sopenharmony_ci} 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_cistatic void __exit vub300_exit(void) 24668c2ecf20Sopenharmony_ci{ 24678c2ecf20Sopenharmony_ci usb_deregister(&vub300_driver); 24688c2ecf20Sopenharmony_ci flush_workqueue(cmndworkqueue); 24698c2ecf20Sopenharmony_ci flush_workqueue(pollworkqueue); 24708c2ecf20Sopenharmony_ci flush_workqueue(deadworkqueue); 24718c2ecf20Sopenharmony_ci destroy_workqueue(cmndworkqueue); 24728c2ecf20Sopenharmony_ci destroy_workqueue(pollworkqueue); 24738c2ecf20Sopenharmony_ci destroy_workqueue(deadworkqueue); 24748c2ecf20Sopenharmony_ci} 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_cimodule_init(vub300_init); 24778c2ecf20Sopenharmony_cimodule_exit(vub300_exit); 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tony Olech <tony.olech@elandigitalsystems.com>"); 24808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VUB300 USB to SD/MMC/SDIO adapter driver"); 24818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2482