153f3a9e2SDavid Vrabel /* 253f3a9e2SDavid Vrabel * USB SD Host Controller (USHC) controller driver. 353f3a9e2SDavid Vrabel * 453f3a9e2SDavid Vrabel * Copyright (C) 2010 Cambridge Silicon Radio Ltd. 553f3a9e2SDavid Vrabel * 653f3a9e2SDavid Vrabel * This program is free software; you can redistribute it and/or modify 753f3a9e2SDavid Vrabel * it under the terms of the GNU General Public License as published by 853f3a9e2SDavid Vrabel * the Free Software Foundation; either version 2 of the License, or (at 953f3a9e2SDavid Vrabel * your option) any later version. 1053f3a9e2SDavid Vrabel * 1153f3a9e2SDavid Vrabel * Notes: 1253f3a9e2SDavid Vrabel * - Only version 2 devices are supported. 1353f3a9e2SDavid Vrabel * - Version 2 devices only support SDIO cards/devices (R2 response is 1453f3a9e2SDavid Vrabel * unsupported). 1553f3a9e2SDavid Vrabel * 1653f3a9e2SDavid Vrabel * References: 1753f3a9e2SDavid Vrabel * [USHC] USB SD Host Controller specification (CS-118793-SP) 1853f3a9e2SDavid Vrabel */ 1953f3a9e2SDavid Vrabel #include <linux/module.h> 2053f3a9e2SDavid Vrabel #include <linux/usb.h> 2153f3a9e2SDavid Vrabel #include <linux/kernel.h> 2253f3a9e2SDavid Vrabel #include <linux/usb.h> 2353f3a9e2SDavid Vrabel #include <linux/slab.h> 2453f3a9e2SDavid Vrabel #include <linux/dma-mapping.h> 2553f3a9e2SDavid Vrabel #include <linux/mmc/host.h> 2653f3a9e2SDavid Vrabel 2753f3a9e2SDavid Vrabel enum ushc_request { 2853f3a9e2SDavid Vrabel USHC_GET_CAPS = 0x00, 2953f3a9e2SDavid Vrabel USHC_HOST_CTRL = 0x01, 3053f3a9e2SDavid Vrabel USHC_PWR_CTRL = 0x02, 3153f3a9e2SDavid Vrabel USHC_CLK_FREQ = 0x03, 3253f3a9e2SDavid Vrabel USHC_EXEC_CMD = 0x04, 3353f3a9e2SDavid Vrabel USHC_READ_RESP = 0x05, 3453f3a9e2SDavid Vrabel USHC_RESET = 0x06, 3553f3a9e2SDavid Vrabel }; 3653f3a9e2SDavid Vrabel 3753f3a9e2SDavid Vrabel enum ushc_request_type { 3853f3a9e2SDavid Vrabel USHC_GET_CAPS_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 3953f3a9e2SDavid Vrabel USHC_HOST_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4053f3a9e2SDavid Vrabel USHC_PWR_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4153f3a9e2SDavid Vrabel USHC_CLK_FREQ_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4253f3a9e2SDavid Vrabel USHC_EXEC_CMD_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4353f3a9e2SDavid Vrabel USHC_READ_RESP_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4453f3a9e2SDavid Vrabel USHC_RESET_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 4553f3a9e2SDavid Vrabel }; 4653f3a9e2SDavid Vrabel 4753f3a9e2SDavid Vrabel #define USHC_GET_CAPS_VERSION_MASK 0xff 4853f3a9e2SDavid Vrabel #define USHC_GET_CAPS_3V3 (1 << 8) 4953f3a9e2SDavid Vrabel #define USHC_GET_CAPS_3V0 (1 << 9) 5053f3a9e2SDavid Vrabel #define USHC_GET_CAPS_1V8 (1 << 10) 5153f3a9e2SDavid Vrabel #define USHC_GET_CAPS_HIGH_SPD (1 << 16) 5253f3a9e2SDavid Vrabel 5353f3a9e2SDavid Vrabel #define USHC_HOST_CTRL_4BIT (1 << 1) 5453f3a9e2SDavid Vrabel #define USHC_HOST_CTRL_HIGH_SPD (1 << 0) 5553f3a9e2SDavid Vrabel 5653f3a9e2SDavid Vrabel #define USHC_PWR_CTRL_OFF 0x00 5753f3a9e2SDavid Vrabel #define USHC_PWR_CTRL_3V3 0x01 5853f3a9e2SDavid Vrabel #define USHC_PWR_CTRL_3V0 0x02 5953f3a9e2SDavid Vrabel #define USHC_PWR_CTRL_1V8 0x03 6053f3a9e2SDavid Vrabel 6153f3a9e2SDavid Vrabel #define USHC_READ_RESP_BUSY (1 << 4) 6253f3a9e2SDavid Vrabel #define USHC_READ_RESP_ERR_TIMEOUT (1 << 3) 6353f3a9e2SDavid Vrabel #define USHC_READ_RESP_ERR_CRC (1 << 2) 6453f3a9e2SDavid Vrabel #define USHC_READ_RESP_ERR_DAT (1 << 1) 6553f3a9e2SDavid Vrabel #define USHC_READ_RESP_ERR_CMD (1 << 0) 6653f3a9e2SDavid Vrabel #define USHC_READ_RESP_ERR_MASK 0x0f 6753f3a9e2SDavid Vrabel 6853f3a9e2SDavid Vrabel struct ushc_cbw { 6953f3a9e2SDavid Vrabel __u8 signature; 7053f3a9e2SDavid Vrabel __u8 cmd_idx; 7153f3a9e2SDavid Vrabel __le16 block_size; 7253f3a9e2SDavid Vrabel __le32 arg; 7353f3a9e2SDavid Vrabel } __attribute__((packed)); 7453f3a9e2SDavid Vrabel 7553f3a9e2SDavid Vrabel #define USHC_CBW_SIGNATURE 'C' 7653f3a9e2SDavid Vrabel 7753f3a9e2SDavid Vrabel struct ushc_csw { 7853f3a9e2SDavid Vrabel __u8 signature; 7953f3a9e2SDavid Vrabel __u8 status; 8053f3a9e2SDavid Vrabel __le32 response; 8153f3a9e2SDavid Vrabel } __attribute__((packed)); 8253f3a9e2SDavid Vrabel 8353f3a9e2SDavid Vrabel #define USHC_CSW_SIGNATURE 'S' 8453f3a9e2SDavid Vrabel 8553f3a9e2SDavid Vrabel struct ushc_int_data { 8653f3a9e2SDavid Vrabel u8 status; 8753f3a9e2SDavid Vrabel u8 reserved[3]; 8853f3a9e2SDavid Vrabel }; 8953f3a9e2SDavid Vrabel 9053f3a9e2SDavid Vrabel #define USHC_INT_STATUS_SDIO_INT (1 << 1) 9153f3a9e2SDavid Vrabel #define USHC_INT_STATUS_CARD_PRESENT (1 << 0) 9253f3a9e2SDavid Vrabel 9353f3a9e2SDavid Vrabel 9453f3a9e2SDavid Vrabel struct ushc_data { 9553f3a9e2SDavid Vrabel struct usb_device *usb_dev; 9653f3a9e2SDavid Vrabel struct mmc_host *mmc; 9753f3a9e2SDavid Vrabel 9853f3a9e2SDavid Vrabel struct urb *int_urb; 9953f3a9e2SDavid Vrabel struct ushc_int_data *int_data; 10053f3a9e2SDavid Vrabel 10153f3a9e2SDavid Vrabel struct urb *cbw_urb; 10253f3a9e2SDavid Vrabel struct ushc_cbw *cbw; 10353f3a9e2SDavid Vrabel 10453f3a9e2SDavid Vrabel struct urb *data_urb; 10553f3a9e2SDavid Vrabel 10653f3a9e2SDavid Vrabel struct urb *csw_urb; 10753f3a9e2SDavid Vrabel struct ushc_csw *csw; 10853f3a9e2SDavid Vrabel 10953f3a9e2SDavid Vrabel spinlock_t lock; 11053f3a9e2SDavid Vrabel struct mmc_request *current_req; 11153f3a9e2SDavid Vrabel u32 caps; 11253f3a9e2SDavid Vrabel u16 host_ctrl; 11353f3a9e2SDavid Vrabel unsigned long flags; 11453f3a9e2SDavid Vrabel u8 last_status; 11553f3a9e2SDavid Vrabel int clock_freq; 11653f3a9e2SDavid Vrabel }; 11753f3a9e2SDavid Vrabel 11853f3a9e2SDavid Vrabel #define DISCONNECTED 0 11953f3a9e2SDavid Vrabel #define INT_EN 1 12053f3a9e2SDavid Vrabel #define IGNORE_NEXT_INT 2 12153f3a9e2SDavid Vrabel 12253f3a9e2SDavid Vrabel static void data_callback(struct urb *urb); 12353f3a9e2SDavid Vrabel 12453f3a9e2SDavid Vrabel static int ushc_hw_reset(struct ushc_data *ushc) 12553f3a9e2SDavid Vrabel { 12653f3a9e2SDavid Vrabel return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), 12753f3a9e2SDavid Vrabel USHC_RESET, USHC_RESET_TYPE, 12853f3a9e2SDavid Vrabel 0, 0, NULL, 0, 100); 12953f3a9e2SDavid Vrabel } 13053f3a9e2SDavid Vrabel 13153f3a9e2SDavid Vrabel static int ushc_hw_get_caps(struct ushc_data *ushc) 13253f3a9e2SDavid Vrabel { 13353f3a9e2SDavid Vrabel int ret; 13453f3a9e2SDavid Vrabel int version; 13553f3a9e2SDavid Vrabel 13653f3a9e2SDavid Vrabel ret = usb_control_msg(ushc->usb_dev, usb_rcvctrlpipe(ushc->usb_dev, 0), 13753f3a9e2SDavid Vrabel USHC_GET_CAPS, USHC_GET_CAPS_TYPE, 13853f3a9e2SDavid Vrabel 0, 0, &ushc->caps, sizeof(ushc->caps), 100); 13953f3a9e2SDavid Vrabel if (ret < 0) 14053f3a9e2SDavid Vrabel return ret; 14153f3a9e2SDavid Vrabel 14253f3a9e2SDavid Vrabel ushc->caps = le32_to_cpu(ushc->caps); 14353f3a9e2SDavid Vrabel 14453f3a9e2SDavid Vrabel version = ushc->caps & USHC_GET_CAPS_VERSION_MASK; 14553f3a9e2SDavid Vrabel if (version != 0x02) { 14653f3a9e2SDavid Vrabel dev_err(&ushc->usb_dev->dev, "controller version %d is not supported\n", version); 14753f3a9e2SDavid Vrabel return -EINVAL; 14853f3a9e2SDavid Vrabel } 14953f3a9e2SDavid Vrabel 15053f3a9e2SDavid Vrabel return 0; 15153f3a9e2SDavid Vrabel } 15253f3a9e2SDavid Vrabel 15353f3a9e2SDavid Vrabel static int ushc_hw_set_host_ctrl(struct ushc_data *ushc, u16 mask, u16 val) 15453f3a9e2SDavid Vrabel { 15553f3a9e2SDavid Vrabel u16 host_ctrl; 15653f3a9e2SDavid Vrabel int ret; 15753f3a9e2SDavid Vrabel 15853f3a9e2SDavid Vrabel host_ctrl = (ushc->host_ctrl & ~mask) | val; 15953f3a9e2SDavid Vrabel ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), 16053f3a9e2SDavid Vrabel USHC_HOST_CTRL, USHC_HOST_CTRL_TYPE, 16153f3a9e2SDavid Vrabel host_ctrl, 0, NULL, 0, 100); 16253f3a9e2SDavid Vrabel if (ret < 0) 16353f3a9e2SDavid Vrabel return ret; 16453f3a9e2SDavid Vrabel ushc->host_ctrl = host_ctrl; 16553f3a9e2SDavid Vrabel return 0; 16653f3a9e2SDavid Vrabel } 16753f3a9e2SDavid Vrabel 16853f3a9e2SDavid Vrabel static void int_callback(struct urb *urb) 16953f3a9e2SDavid Vrabel { 17053f3a9e2SDavid Vrabel struct ushc_data *ushc = urb->context; 17153f3a9e2SDavid Vrabel u8 status, last_status; 17253f3a9e2SDavid Vrabel 17353f3a9e2SDavid Vrabel if (urb->status < 0) 17453f3a9e2SDavid Vrabel return; 17553f3a9e2SDavid Vrabel 17653f3a9e2SDavid Vrabel status = ushc->int_data->status; 17753f3a9e2SDavid Vrabel last_status = ushc->last_status; 17853f3a9e2SDavid Vrabel ushc->last_status = status; 17953f3a9e2SDavid Vrabel 18053f3a9e2SDavid Vrabel /* 18153f3a9e2SDavid Vrabel * Ignore the card interrupt status on interrupt transfers that 18253f3a9e2SDavid Vrabel * were submitted while card interrupts where disabled. 18353f3a9e2SDavid Vrabel * 18453f3a9e2SDavid Vrabel * This avoid occasional spurious interrupts when enabling 18553f3a9e2SDavid Vrabel * interrupts immediately after clearing the source on the card. 18653f3a9e2SDavid Vrabel */ 18753f3a9e2SDavid Vrabel 18853f3a9e2SDavid Vrabel if (!test_and_clear_bit(IGNORE_NEXT_INT, &ushc->flags) 18953f3a9e2SDavid Vrabel && test_bit(INT_EN, &ushc->flags) 19053f3a9e2SDavid Vrabel && status & USHC_INT_STATUS_SDIO_INT) { 19153f3a9e2SDavid Vrabel mmc_signal_sdio_irq(ushc->mmc); 19253f3a9e2SDavid Vrabel } 19353f3a9e2SDavid Vrabel 19453f3a9e2SDavid Vrabel if ((status ^ last_status) & USHC_INT_STATUS_CARD_PRESENT) 19553f3a9e2SDavid Vrabel mmc_detect_change(ushc->mmc, msecs_to_jiffies(100)); 19653f3a9e2SDavid Vrabel 19753f3a9e2SDavid Vrabel if (!test_bit(INT_EN, &ushc->flags)) 19853f3a9e2SDavid Vrabel set_bit(IGNORE_NEXT_INT, &ushc->flags); 19953f3a9e2SDavid Vrabel usb_submit_urb(ushc->int_urb, GFP_ATOMIC); 20053f3a9e2SDavid Vrabel } 20153f3a9e2SDavid Vrabel 20253f3a9e2SDavid Vrabel static void cbw_callback(struct urb *urb) 20353f3a9e2SDavid Vrabel { 20453f3a9e2SDavid Vrabel struct ushc_data *ushc = urb->context; 20553f3a9e2SDavid Vrabel 20653f3a9e2SDavid Vrabel if (urb->status != 0) { 20753f3a9e2SDavid Vrabel usb_unlink_urb(ushc->data_urb); 20853f3a9e2SDavid Vrabel usb_unlink_urb(ushc->csw_urb); 20953f3a9e2SDavid Vrabel } 21053f3a9e2SDavid Vrabel } 21153f3a9e2SDavid Vrabel 21253f3a9e2SDavid Vrabel static void data_callback(struct urb *urb) 21353f3a9e2SDavid Vrabel { 21453f3a9e2SDavid Vrabel struct ushc_data *ushc = urb->context; 21553f3a9e2SDavid Vrabel 21653f3a9e2SDavid Vrabel if (urb->status != 0) 21753f3a9e2SDavid Vrabel usb_unlink_urb(ushc->csw_urb); 21853f3a9e2SDavid Vrabel } 21953f3a9e2SDavid Vrabel 22053f3a9e2SDavid Vrabel static void csw_callback(struct urb *urb) 22153f3a9e2SDavid Vrabel { 22253f3a9e2SDavid Vrabel struct ushc_data *ushc = urb->context; 22353f3a9e2SDavid Vrabel struct mmc_request *req = ushc->current_req; 22453f3a9e2SDavid Vrabel int status; 22553f3a9e2SDavid Vrabel 22653f3a9e2SDavid Vrabel status = ushc->csw->status; 22753f3a9e2SDavid Vrabel 22853f3a9e2SDavid Vrabel if (urb->status != 0) { 22953f3a9e2SDavid Vrabel req->cmd->error = urb->status; 23053f3a9e2SDavid Vrabel } else if (status & USHC_READ_RESP_ERR_CMD) { 23153f3a9e2SDavid Vrabel if (status & USHC_READ_RESP_ERR_CRC) 23253f3a9e2SDavid Vrabel req->cmd->error = -EIO; 23353f3a9e2SDavid Vrabel else 23453f3a9e2SDavid Vrabel req->cmd->error = -ETIMEDOUT; 23553f3a9e2SDavid Vrabel } 23653f3a9e2SDavid Vrabel if (req->data) { 23753f3a9e2SDavid Vrabel if (status & USHC_READ_RESP_ERR_DAT) { 23853f3a9e2SDavid Vrabel if (status & USHC_READ_RESP_ERR_CRC) 23953f3a9e2SDavid Vrabel req->data->error = -EIO; 24053f3a9e2SDavid Vrabel else 24153f3a9e2SDavid Vrabel req->data->error = -ETIMEDOUT; 24253f3a9e2SDavid Vrabel req->data->bytes_xfered = 0; 24353f3a9e2SDavid Vrabel } else { 24453f3a9e2SDavid Vrabel req->data->bytes_xfered = req->data->blksz * req->data->blocks; 24553f3a9e2SDavid Vrabel } 24653f3a9e2SDavid Vrabel } 24753f3a9e2SDavid Vrabel 24853f3a9e2SDavid Vrabel req->cmd->resp[0] = le32_to_cpu(ushc->csw->response); 24953f3a9e2SDavid Vrabel 25053f3a9e2SDavid Vrabel mmc_request_done(ushc->mmc, req); 25153f3a9e2SDavid Vrabel } 25253f3a9e2SDavid Vrabel 25353f3a9e2SDavid Vrabel static void ushc_request(struct mmc_host *mmc, struct mmc_request *req) 25453f3a9e2SDavid Vrabel { 25553f3a9e2SDavid Vrabel struct ushc_data *ushc = mmc_priv(mmc); 25653f3a9e2SDavid Vrabel int ret; 25753f3a9e2SDavid Vrabel unsigned long flags; 25853f3a9e2SDavid Vrabel 25953f3a9e2SDavid Vrabel spin_lock_irqsave(&ushc->lock, flags); 26053f3a9e2SDavid Vrabel 26153f3a9e2SDavid Vrabel if (test_bit(DISCONNECTED, &ushc->flags)) { 26253f3a9e2SDavid Vrabel ret = -ENODEV; 26353f3a9e2SDavid Vrabel goto out; 26453f3a9e2SDavid Vrabel } 26553f3a9e2SDavid Vrabel 26653f3a9e2SDavid Vrabel /* Version 2 firmware doesn't support the R2 response format. */ 26753f3a9e2SDavid Vrabel if (req->cmd->flags & MMC_RSP_136) { 26853f3a9e2SDavid Vrabel ret = -EINVAL; 26953f3a9e2SDavid Vrabel goto out; 27053f3a9e2SDavid Vrabel } 27153f3a9e2SDavid Vrabel 27253f3a9e2SDavid Vrabel /* The Astoria's data FIFOs don't work with clock speeds < 5MHz so 27353f3a9e2SDavid Vrabel limit commands with data to 6MHz or more. */ 27453f3a9e2SDavid Vrabel if (req->data && ushc->clock_freq < 6000000) { 27553f3a9e2SDavid Vrabel ret = -EINVAL; 27653f3a9e2SDavid Vrabel goto out; 27753f3a9e2SDavid Vrabel } 27853f3a9e2SDavid Vrabel 27953f3a9e2SDavid Vrabel ushc->current_req = req; 28053f3a9e2SDavid Vrabel 28153f3a9e2SDavid Vrabel /* Start cmd with CBW. */ 28253f3a9e2SDavid Vrabel ushc->cbw->cmd_idx = cpu_to_le16(req->cmd->opcode); 28353f3a9e2SDavid Vrabel if (req->data) 28453f3a9e2SDavid Vrabel ushc->cbw->block_size = cpu_to_le16(req->data->blksz); 28553f3a9e2SDavid Vrabel else 28653f3a9e2SDavid Vrabel ushc->cbw->block_size = 0; 28753f3a9e2SDavid Vrabel ushc->cbw->arg = cpu_to_le32(req->cmd->arg); 28853f3a9e2SDavid Vrabel 28953f3a9e2SDavid Vrabel ret = usb_submit_urb(ushc->cbw_urb, GFP_ATOMIC); 29053f3a9e2SDavid Vrabel if (ret < 0) 29153f3a9e2SDavid Vrabel goto out; 29253f3a9e2SDavid Vrabel 29353f3a9e2SDavid Vrabel /* Submit data (if any). */ 29453f3a9e2SDavid Vrabel if (req->data) { 29553f3a9e2SDavid Vrabel struct mmc_data *data = req->data; 29653f3a9e2SDavid Vrabel int pipe; 29753f3a9e2SDavid Vrabel 29853f3a9e2SDavid Vrabel if (data->flags & MMC_DATA_READ) 29953f3a9e2SDavid Vrabel pipe = usb_rcvbulkpipe(ushc->usb_dev, 6); 30053f3a9e2SDavid Vrabel else 30153f3a9e2SDavid Vrabel pipe = usb_sndbulkpipe(ushc->usb_dev, 2); 30253f3a9e2SDavid Vrabel 30353f3a9e2SDavid Vrabel usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe, 30453f3a9e2SDavid Vrabel sg_virt(data->sg), data->sg->length, 30553f3a9e2SDavid Vrabel data_callback, ushc); 30653f3a9e2SDavid Vrabel ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC); 30753f3a9e2SDavid Vrabel if (ret < 0) 30853f3a9e2SDavid Vrabel goto out; 30953f3a9e2SDavid Vrabel } 31053f3a9e2SDavid Vrabel 31153f3a9e2SDavid Vrabel /* Submit CSW. */ 31253f3a9e2SDavid Vrabel ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC); 31353f3a9e2SDavid Vrabel if (ret < 0) 31453f3a9e2SDavid Vrabel goto out; 31553f3a9e2SDavid Vrabel 31653f3a9e2SDavid Vrabel out: 31753f3a9e2SDavid Vrabel spin_unlock_irqrestore(&ushc->lock, flags); 31853f3a9e2SDavid Vrabel if (ret < 0) { 31953f3a9e2SDavid Vrabel usb_unlink_urb(ushc->cbw_urb); 32053f3a9e2SDavid Vrabel usb_unlink_urb(ushc->data_urb); 32153f3a9e2SDavid Vrabel req->cmd->error = ret; 32253f3a9e2SDavid Vrabel mmc_request_done(mmc, req); 32353f3a9e2SDavid Vrabel } 32453f3a9e2SDavid Vrabel } 32553f3a9e2SDavid Vrabel 32653f3a9e2SDavid Vrabel static int ushc_set_power(struct ushc_data *ushc, unsigned char power_mode) 32753f3a9e2SDavid Vrabel { 32853f3a9e2SDavid Vrabel u16 voltage; 32953f3a9e2SDavid Vrabel 33053f3a9e2SDavid Vrabel switch (power_mode) { 33153f3a9e2SDavid Vrabel case MMC_POWER_OFF: 33253f3a9e2SDavid Vrabel voltage = USHC_PWR_CTRL_OFF; 33353f3a9e2SDavid Vrabel break; 33453f3a9e2SDavid Vrabel case MMC_POWER_UP: 33553f3a9e2SDavid Vrabel case MMC_POWER_ON: 33653f3a9e2SDavid Vrabel voltage = USHC_PWR_CTRL_3V3; 33753f3a9e2SDavid Vrabel break; 33853f3a9e2SDavid Vrabel default: 33953f3a9e2SDavid Vrabel return -EINVAL; 34053f3a9e2SDavid Vrabel } 34153f3a9e2SDavid Vrabel 34253f3a9e2SDavid Vrabel return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), 34353f3a9e2SDavid Vrabel USHC_PWR_CTRL, USHC_PWR_CTRL_TYPE, 34453f3a9e2SDavid Vrabel voltage, 0, NULL, 0, 100); 34553f3a9e2SDavid Vrabel } 34653f3a9e2SDavid Vrabel 34753f3a9e2SDavid Vrabel static int ushc_set_bus_width(struct ushc_data *ushc, int bus_width) 34853f3a9e2SDavid Vrabel { 34953f3a9e2SDavid Vrabel return ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_4BIT, 35053f3a9e2SDavid Vrabel bus_width == 4 ? USHC_HOST_CTRL_4BIT : 0); 35153f3a9e2SDavid Vrabel } 35253f3a9e2SDavid Vrabel 35353f3a9e2SDavid Vrabel static int ushc_set_bus_freq(struct ushc_data *ushc, int clk, bool enable_hs) 35453f3a9e2SDavid Vrabel { 35553f3a9e2SDavid Vrabel int ret; 35653f3a9e2SDavid Vrabel 35753f3a9e2SDavid Vrabel /* Hardware can't detect interrupts while the clock is off. */ 35853f3a9e2SDavid Vrabel if (clk == 0) 35953f3a9e2SDavid Vrabel clk = 400000; 36053f3a9e2SDavid Vrabel 36153f3a9e2SDavid Vrabel ret = ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_HIGH_SPD, 36253f3a9e2SDavid Vrabel enable_hs ? USHC_HOST_CTRL_HIGH_SPD : 0); 36353f3a9e2SDavid Vrabel if (ret < 0) 36453f3a9e2SDavid Vrabel return ret; 36553f3a9e2SDavid Vrabel 36653f3a9e2SDavid Vrabel ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), 36753f3a9e2SDavid Vrabel USHC_CLK_FREQ, USHC_CLK_FREQ_TYPE, 36853f3a9e2SDavid Vrabel clk & 0xffff, (clk >> 16) & 0xffff, NULL, 0, 100); 36953f3a9e2SDavid Vrabel if (ret < 0) 37053f3a9e2SDavid Vrabel return ret; 37153f3a9e2SDavid Vrabel 37253f3a9e2SDavid Vrabel ushc->clock_freq = clk; 37353f3a9e2SDavid Vrabel return 0; 37453f3a9e2SDavid Vrabel } 37553f3a9e2SDavid Vrabel 37653f3a9e2SDavid Vrabel static void ushc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 37753f3a9e2SDavid Vrabel { 37853f3a9e2SDavid Vrabel struct ushc_data *ushc = mmc_priv(mmc); 37953f3a9e2SDavid Vrabel 38053f3a9e2SDavid Vrabel ushc_set_power(ushc, ios->power_mode); 38153f3a9e2SDavid Vrabel ushc_set_bus_width(ushc, 1 << ios->bus_width); 38253f3a9e2SDavid Vrabel ushc_set_bus_freq(ushc, ios->clock, ios->timing == MMC_TIMING_SD_HS); 38353f3a9e2SDavid Vrabel } 38453f3a9e2SDavid Vrabel 38553f3a9e2SDavid Vrabel static int ushc_get_cd(struct mmc_host *mmc) 38653f3a9e2SDavid Vrabel { 38753f3a9e2SDavid Vrabel struct ushc_data *ushc = mmc_priv(mmc); 38853f3a9e2SDavid Vrabel 38953f3a9e2SDavid Vrabel return !!(ushc->last_status & USHC_INT_STATUS_CARD_PRESENT); 39053f3a9e2SDavid Vrabel } 39153f3a9e2SDavid Vrabel 39253f3a9e2SDavid Vrabel static void ushc_enable_sdio_irq(struct mmc_host *mmc, int enable) 39353f3a9e2SDavid Vrabel { 39453f3a9e2SDavid Vrabel struct ushc_data *ushc = mmc_priv(mmc); 39553f3a9e2SDavid Vrabel 39653f3a9e2SDavid Vrabel if (enable) 39753f3a9e2SDavid Vrabel set_bit(INT_EN, &ushc->flags); 39853f3a9e2SDavid Vrabel else 39953f3a9e2SDavid Vrabel clear_bit(INT_EN, &ushc->flags); 40053f3a9e2SDavid Vrabel } 40153f3a9e2SDavid Vrabel 40253f3a9e2SDavid Vrabel static void ushc_clean_up(struct ushc_data *ushc) 40353f3a9e2SDavid Vrabel { 40453f3a9e2SDavid Vrabel usb_free_urb(ushc->int_urb); 40553f3a9e2SDavid Vrabel usb_free_urb(ushc->csw_urb); 40653f3a9e2SDavid Vrabel usb_free_urb(ushc->data_urb); 40753f3a9e2SDavid Vrabel usb_free_urb(ushc->cbw_urb); 40853f3a9e2SDavid Vrabel 40953f3a9e2SDavid Vrabel kfree(ushc->int_data); 41053f3a9e2SDavid Vrabel kfree(ushc->cbw); 41153f3a9e2SDavid Vrabel kfree(ushc->csw); 41253f3a9e2SDavid Vrabel 41353f3a9e2SDavid Vrabel mmc_free_host(ushc->mmc); 41453f3a9e2SDavid Vrabel } 41553f3a9e2SDavid Vrabel 41653f3a9e2SDavid Vrabel static const struct mmc_host_ops ushc_ops = { 41753f3a9e2SDavid Vrabel .request = ushc_request, 41853f3a9e2SDavid Vrabel .set_ios = ushc_set_ios, 41953f3a9e2SDavid Vrabel .get_cd = ushc_get_cd, 42053f3a9e2SDavid Vrabel .enable_sdio_irq = ushc_enable_sdio_irq, 42153f3a9e2SDavid Vrabel }; 42253f3a9e2SDavid Vrabel 42353f3a9e2SDavid Vrabel static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id) 42453f3a9e2SDavid Vrabel { 42553f3a9e2SDavid Vrabel struct usb_device *usb_dev = interface_to_usbdev(intf); 42653f3a9e2SDavid Vrabel struct mmc_host *mmc; 42753f3a9e2SDavid Vrabel struct ushc_data *ushc; 42853f3a9e2SDavid Vrabel int ret = -ENOMEM; 42953f3a9e2SDavid Vrabel 43053f3a9e2SDavid Vrabel mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); 43153f3a9e2SDavid Vrabel if (mmc == NULL) 43253f3a9e2SDavid Vrabel return -ENOMEM; 43353f3a9e2SDavid Vrabel ushc = mmc_priv(mmc); 43453f3a9e2SDavid Vrabel usb_set_intfdata(intf, ushc); 43553f3a9e2SDavid Vrabel 43653f3a9e2SDavid Vrabel ushc->usb_dev = usb_dev; 43753f3a9e2SDavid Vrabel ushc->mmc = mmc; 43853f3a9e2SDavid Vrabel 43953f3a9e2SDavid Vrabel spin_lock_init(&ushc->lock); 44053f3a9e2SDavid Vrabel 44153f3a9e2SDavid Vrabel ret = ushc_hw_reset(ushc); 44253f3a9e2SDavid Vrabel if (ret < 0) 44353f3a9e2SDavid Vrabel goto err; 44453f3a9e2SDavid Vrabel 44553f3a9e2SDavid Vrabel /* Read capabilities. */ 44653f3a9e2SDavid Vrabel ret = ushc_hw_get_caps(ushc); 44753f3a9e2SDavid Vrabel if (ret < 0) 44853f3a9e2SDavid Vrabel goto err; 44953f3a9e2SDavid Vrabel 45053f3a9e2SDavid Vrabel mmc->ops = &ushc_ops; 45153f3a9e2SDavid Vrabel 45253f3a9e2SDavid Vrabel mmc->f_min = 400000; 45353f3a9e2SDavid Vrabel mmc->f_max = 50000000; 45453f3a9e2SDavid Vrabel mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 45553f3a9e2SDavid Vrabel mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; 45653f3a9e2SDavid Vrabel mmc->caps |= (ushc->caps & USHC_GET_CAPS_HIGH_SPD) ? MMC_CAP_SD_HIGHSPEED : 0; 45753f3a9e2SDavid Vrabel 45853f3a9e2SDavid Vrabel mmc->max_seg_size = 512*511; 45953f3a9e2SDavid Vrabel mmc->max_segs = 1; 46053f3a9e2SDavid Vrabel mmc->max_req_size = 512*511; 46153f3a9e2SDavid Vrabel mmc->max_blk_size = 512; 46253f3a9e2SDavid Vrabel mmc->max_blk_count = 511; 46353f3a9e2SDavid Vrabel 46453f3a9e2SDavid Vrabel ushc->int_urb = usb_alloc_urb(0, GFP_KERNEL); 46553f3a9e2SDavid Vrabel if (ushc->int_urb == NULL) 46653f3a9e2SDavid Vrabel goto err; 46753f3a9e2SDavid Vrabel ushc->int_data = kzalloc(sizeof(struct ushc_int_data), GFP_KERNEL); 46853f3a9e2SDavid Vrabel if (ushc->int_data == NULL) 46953f3a9e2SDavid Vrabel goto err; 47053f3a9e2SDavid Vrabel usb_fill_int_urb(ushc->int_urb, ushc->usb_dev, 47153f3a9e2SDavid Vrabel usb_rcvintpipe(usb_dev, 47253f3a9e2SDavid Vrabel intf->cur_altsetting->endpoint[0].desc.bEndpointAddress), 47353f3a9e2SDavid Vrabel ushc->int_data, sizeof(struct ushc_int_data), 47453f3a9e2SDavid Vrabel int_callback, ushc, 47553f3a9e2SDavid Vrabel intf->cur_altsetting->endpoint[0].desc.bInterval); 47653f3a9e2SDavid Vrabel 47753f3a9e2SDavid Vrabel ushc->cbw_urb = usb_alloc_urb(0, GFP_KERNEL); 47853f3a9e2SDavid Vrabel if (ushc->cbw_urb == NULL) 47953f3a9e2SDavid Vrabel goto err; 48053f3a9e2SDavid Vrabel ushc->cbw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); 48153f3a9e2SDavid Vrabel if (ushc->cbw == NULL) 48253f3a9e2SDavid Vrabel goto err; 48353f3a9e2SDavid Vrabel ushc->cbw->signature = USHC_CBW_SIGNATURE; 48453f3a9e2SDavid Vrabel 48553f3a9e2SDavid Vrabel usb_fill_bulk_urb(ushc->cbw_urb, ushc->usb_dev, usb_sndbulkpipe(usb_dev, 2), 48653f3a9e2SDavid Vrabel ushc->cbw, sizeof(struct ushc_cbw), 48753f3a9e2SDavid Vrabel cbw_callback, ushc); 48853f3a9e2SDavid Vrabel 48953f3a9e2SDavid Vrabel ushc->data_urb = usb_alloc_urb(0, GFP_KERNEL); 49053f3a9e2SDavid Vrabel if (ushc->data_urb == NULL) 49153f3a9e2SDavid Vrabel goto err; 49253f3a9e2SDavid Vrabel 49353f3a9e2SDavid Vrabel ushc->csw_urb = usb_alloc_urb(0, GFP_KERNEL); 49453f3a9e2SDavid Vrabel if (ushc->csw_urb == NULL) 49553f3a9e2SDavid Vrabel goto err; 49653f3a9e2SDavid Vrabel ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); 49753f3a9e2SDavid Vrabel if (ushc->csw == NULL) 49853f3a9e2SDavid Vrabel goto err; 49953f3a9e2SDavid Vrabel usb_fill_bulk_urb(ushc->csw_urb, ushc->usb_dev, usb_rcvbulkpipe(usb_dev, 6), 50053f3a9e2SDavid Vrabel ushc->csw, sizeof(struct ushc_csw), 50153f3a9e2SDavid Vrabel csw_callback, ushc); 50253f3a9e2SDavid Vrabel 50353f3a9e2SDavid Vrabel ret = mmc_add_host(ushc->mmc); 50453f3a9e2SDavid Vrabel if (ret) 50553f3a9e2SDavid Vrabel goto err; 50653f3a9e2SDavid Vrabel 50753f3a9e2SDavid Vrabel ret = usb_submit_urb(ushc->int_urb, GFP_KERNEL); 50853f3a9e2SDavid Vrabel if (ret < 0) { 50953f3a9e2SDavid Vrabel mmc_remove_host(ushc->mmc); 51053f3a9e2SDavid Vrabel goto err; 51153f3a9e2SDavid Vrabel } 51253f3a9e2SDavid Vrabel 51353f3a9e2SDavid Vrabel return 0; 51453f3a9e2SDavid Vrabel 51553f3a9e2SDavid Vrabel err: 51653f3a9e2SDavid Vrabel ushc_clean_up(ushc); 51753f3a9e2SDavid Vrabel return ret; 51853f3a9e2SDavid Vrabel } 51953f3a9e2SDavid Vrabel 52053f3a9e2SDavid Vrabel static void ushc_disconnect(struct usb_interface *intf) 52153f3a9e2SDavid Vrabel { 52253f3a9e2SDavid Vrabel struct ushc_data *ushc = usb_get_intfdata(intf); 52353f3a9e2SDavid Vrabel 52453f3a9e2SDavid Vrabel spin_lock_irq(&ushc->lock); 52553f3a9e2SDavid Vrabel set_bit(DISCONNECTED, &ushc->flags); 52653f3a9e2SDavid Vrabel spin_unlock_irq(&ushc->lock); 52753f3a9e2SDavid Vrabel 52853f3a9e2SDavid Vrabel usb_kill_urb(ushc->int_urb); 52953f3a9e2SDavid Vrabel usb_kill_urb(ushc->cbw_urb); 53053f3a9e2SDavid Vrabel usb_kill_urb(ushc->data_urb); 53153f3a9e2SDavid Vrabel usb_kill_urb(ushc->csw_urb); 53253f3a9e2SDavid Vrabel 53353f3a9e2SDavid Vrabel mmc_remove_host(ushc->mmc); 53453f3a9e2SDavid Vrabel 53553f3a9e2SDavid Vrabel ushc_clean_up(ushc); 53653f3a9e2SDavid Vrabel } 53753f3a9e2SDavid Vrabel 53853f3a9e2SDavid Vrabel static struct usb_device_id ushc_id_table[] = { 53953f3a9e2SDavid Vrabel /* CSR USB SD Host Controller */ 54053f3a9e2SDavid Vrabel { USB_DEVICE(0x0a12, 0x5d10) }, 54153f3a9e2SDavid Vrabel { }, 54253f3a9e2SDavid Vrabel }; 54353f3a9e2SDavid Vrabel MODULE_DEVICE_TABLE(usb, ushc_id_table); 54453f3a9e2SDavid Vrabel 54553f3a9e2SDavid Vrabel static struct usb_driver ushc_driver = { 54653f3a9e2SDavid Vrabel .name = "ushc", 54753f3a9e2SDavid Vrabel .id_table = ushc_id_table, 54853f3a9e2SDavid Vrabel .probe = ushc_probe, 54953f3a9e2SDavid Vrabel .disconnect = ushc_disconnect, 55053f3a9e2SDavid Vrabel }; 55153f3a9e2SDavid Vrabel 55253f3a9e2SDavid Vrabel static int __init ushc_init(void) 55353f3a9e2SDavid Vrabel { 55453f3a9e2SDavid Vrabel return usb_register(&ushc_driver); 55553f3a9e2SDavid Vrabel } 55653f3a9e2SDavid Vrabel module_init(ushc_init); 55753f3a9e2SDavid Vrabel 55853f3a9e2SDavid Vrabel static void __exit ushc_exit(void) 55953f3a9e2SDavid Vrabel { 56053f3a9e2SDavid Vrabel usb_deregister(&ushc_driver); 56153f3a9e2SDavid Vrabel } 56253f3a9e2SDavid Vrabel module_exit(ushc_exit); 56353f3a9e2SDavid Vrabel 56453f3a9e2SDavid Vrabel MODULE_DESCRIPTION("USB SD Host Controller driver"); 56553f3a9e2SDavid Vrabel MODULE_AUTHOR("David Vrabel <david.vrabel@csr.com>"); 56653f3a9e2SDavid Vrabel MODULE_LICENSE("GPL"); 567