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