diff options
| -rw-r--r-- | drivers/mmc/host/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/mmc/host/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mmc/host/ushc.c | 566 |
3 files changed, 581 insertions, 0 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 68d12794cfd..8de7b9e8fc5 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig | |||
| @@ -451,3 +451,17 @@ config MMC_JZ4740 | |||
| 451 | SoCs. | 451 | SoCs. |
| 452 | If you have a board based on such a SoC and with a SD/MMC slot, | 452 | If you have a board based on such a SoC and with a SD/MMC slot, |
| 453 | say Y or M here. | 453 | say Y or M here. |
| 454 | |||
| 455 | config MMC_USHC | ||
| 456 | tristate "USB SD Host Controller (USHC) support" | ||
| 457 | depends on USB | ||
| 458 | help | ||
| 459 | This selects support for USB SD Host Controllers based on | ||
| 460 | the Cypress Astoria chip with firmware compliant with CSR's | ||
| 461 | USB SD Host Controller specification (CS-118793-SP). | ||
| 462 | |||
| 463 | CSR boards with this device include: USB<>SDIO (M1985v2), | ||
| 464 | and Ultrasira. | ||
| 465 | |||
| 466 | Note: These controllers only support SDIO cards and do not | ||
| 467 | support MMC or SD memory cards. | ||
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 840bcb52d82..f7fb7eb234b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile | |||
| @@ -36,6 +36,7 @@ obj-$(CONFIG_MMC_VIA_SDMMC) += via-sdmmc.o | |||
| 36 | obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o | 36 | obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o |
| 37 | obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o | 37 | obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o |
| 38 | obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o | 38 | obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o |
| 39 | obj-$(CONFIG_MMC_USHC) += ushc.o | ||
| 39 | 40 | ||
| 40 | obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o | 41 | obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o |
| 41 | sdhci-platform-y := sdhci-pltfm.o | 42 | sdhci-platform-y := sdhci-pltfm.o |
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c new file mode 100644 index 00000000000..b4ead4a13c9 --- /dev/null +++ b/drivers/mmc/host/ushc.c | |||
| @@ -0,0 +1,566 @@ | |||
| 1 | /* | ||
| 2 | * USB SD Host Controller (USHC) controller driver. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Cambridge Silicon Radio Ltd. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or (at | ||
| 9 | * your option) any later version. | ||
| 10 | * | ||
| 11 | * Notes: | ||
| 12 | * - Only version 2 devices are supported. | ||
| 13 | * - Version 2 devices only support SDIO cards/devices (R2 response is | ||
| 14 | * unsupported). | ||
| 15 | * | ||
| 16 | * References: | ||
| 17 | * [USHC] USB SD Host Controller specification (CS-118793-SP) | ||
| 18 | */ | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/usb.h> | ||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/usb.h> | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/dma-mapping.h> | ||
| 25 | #include <linux/mmc/host.h> | ||
| 26 | |||
| 27 | enum ushc_request { | ||
| 28 | USHC_GET_CAPS = 0x00, | ||
| 29 | USHC_HOST_CTRL = 0x01, | ||
| 30 | USHC_PWR_CTRL = 0x02, | ||
| 31 | USHC_CLK_FREQ = 0x03, | ||
| 32 | USHC_EXEC_CMD = 0x04, | ||
| 33 | USHC_READ_RESP = 0x05, | ||
| 34 | USHC_RESET = 0x06, | ||
| 35 | }; | ||
| 36 | |||
| 37 | enum ushc_request_type { | ||
| 38 | USHC_GET_CAPS_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 39 | USHC_HOST_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 40 | USHC_PWR_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 41 | USHC_CLK_FREQ_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 42 | USHC_EXEC_CMD_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 43 | USHC_READ_RESP_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 44 | USHC_RESET_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
| 45 | }; | ||
| 46 | |||
| 47 | #define USHC_GET_CAPS_VERSION_MASK 0xff | ||
| 48 | #define USHC_GET_CAPS_3V3 (1 << 8) | ||
| 49 | #define USHC_GET_CAPS_3V0 (1 << 9) | ||
| 50 | #define USHC_GET_CAPS_1V8 (1 << 10) | ||
| 51 | #define USHC_GET_CAPS_HIGH_SPD (1 << 16) | ||
| 52 | |||
| 53 | #define USHC_HOST_CTRL_4BIT (1 << 1) | ||
| 54 | #define USHC_HOST_CTRL_HIGH_SPD (1 << 0) | ||
| 55 | |||
| 56 | #define USHC_PWR_CTRL_OFF 0x00 | ||
| 57 | #define USHC_PWR_CTRL_3V3 0x01 | ||
| 58 | #define USHC_PWR_CTRL_3V0 0x02 | ||
| 59 | #define USHC_PWR_CTRL_1V8 0x03 | ||
| 60 | |||
| 61 | #define USHC_READ_RESP_BUSY (1 << 4) | ||
| 62 | #define USHC_READ_RESP_ERR_TIMEOUT (1 << 3) | ||
| 63 | #define USHC_READ_RESP_ERR_CRC (1 << 2) | ||
| 64 | #define USHC_READ_RESP_ERR_DAT (1 << 1) | ||
| 65 | #define USHC_READ_RESP_ERR_CMD (1 << 0) | ||
| 66 | #define USHC_READ_RESP_ERR_MASK 0x0f | ||
| 67 | |||
| 68 | struct ushc_cbw { | ||
| 69 | __u8 signature; | ||
| 70 | __u8 cmd_idx; | ||
| 71 | __le16 block_size; | ||
| 72 | __le32 arg; | ||
| 73 | } __attribute__((packed)); | ||
| 74 | |||
| 75 | #define USHC_CBW_SIGNATURE 'C' | ||
| 76 | |||
| 77 | struct ushc_csw { | ||
| 78 | __u8 signature; | ||
| 79 | __u8 status; | ||
| 80 | __le32 response; | ||
| 81 | } __attribute__((packed)); | ||
| 82 | |||
| 83 | #define USHC_CSW_SIGNATURE 'S' | ||
| 84 | |||
| 85 | struct ushc_int_data { | ||
| 86 | u8 status; | ||
| 87 | u8 reserved[3]; | ||
| 88 | }; | ||
| 89 | |||
| 90 | #define USHC_INT_STATUS_SDIO_INT (1 << 1) | ||
| 91 | #define USHC_INT_STATUS_CARD_PRESENT (1 << 0) | ||
| 92 | |||
| 93 | |||
| 94 | struct ushc_data { | ||
| 95 | struct usb_device *usb_dev; | ||
| 96 | struct mmc_host *mmc; | ||
| 97 | |||
| 98 | struct urb *int_urb; | ||
| 99 | struct ushc_int_data *int_data; | ||
| 100 | |||
| 101 | struct urb *cbw_urb; | ||
| 102 | struct ushc_cbw *cbw; | ||
| 103 | |||
| 104 | struct urb *data_urb; | ||
| 105 | |||
| 106 | struct urb *csw_urb; | ||
| 107 | struct ushc_csw *csw; | ||
| 108 | |||
| 109 | spinlock_t lock; | ||
| 110 | struct mmc_request *current_req; | ||
| 111 | u32 caps; | ||
| 112 | u16 host_ctrl; | ||
| 113 | unsigned long flags; | ||
| 114 | u8 last_status; | ||
| 115 | int clock_freq; | ||
| 116 | }; | ||
| 117 | |||
| 118 | #define DISCONNECTED 0 | ||
| 119 | #define INT_EN 1 | ||
| 120 | #define IGNORE_NEXT_INT 2 | ||
| 121 | |||
| 122 | static void data_callback(struct urb *urb); | ||
| 123 | |||
| 124 | static int ushc_hw_reset(struct ushc_data *ushc) | ||
| 125 | { | ||
| 126 | return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), | ||
| 127 | USHC_RESET, USHC_RESET_TYPE, | ||
| 128 | 0, 0, NULL, 0, 100); | ||
| 129 | } | ||
| 130 | |||
| 131 | static int ushc_hw_get_caps(struct ushc_data *ushc) | ||
| 132 | { | ||
| 133 | int ret; | ||
| 134 | int version; | ||
| 135 | |||
| 136 | ret = usb_control_msg(ushc->usb_dev, usb_rcvctrlpipe(ushc->usb_dev, 0), | ||
| 137 | USHC_GET_CAPS, USHC_GET_CAPS_TYPE, | ||
| 138 | 0, 0, &ushc->caps, sizeof(ushc->caps), 100); | ||
| 139 | if (ret < 0) | ||
| 140 | return ret; | ||
| 141 | |||
| 142 | ushc->caps = le32_to_cpu(ushc->caps); | ||
| 143 | |||
| 144 | version = ushc->caps & USHC_GET_CAPS_VERSION_MASK; | ||
| 145 | if (version != 0x02) { | ||
| 146 | dev_err(&ushc->usb_dev->dev, "controller version %d is not supported\n", version); | ||
| 147 | return -EINVAL; | ||
| 148 | } | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int ushc_hw_set_host_ctrl(struct ushc_data *ushc, u16 mask, u16 val) | ||
| 154 | { | ||
| 155 | u16 host_ctrl; | ||
| 156 | int ret; | ||
| 157 | |||
| 158 | host_ctrl = (ushc->host_ctrl & ~mask) | val; | ||
| 159 | ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), | ||
| 160 | USHC_HOST_CTRL, USHC_HOST_CTRL_TYPE, | ||
| 161 | host_ctrl, 0, NULL, 0, 100); | ||
| 162 | if (ret < 0) | ||
| 163 | return ret; | ||
| 164 | ushc->host_ctrl = host_ctrl; | ||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static void int_callback(struct urb *urb) | ||
| 169 | { | ||
| 170 | struct ushc_data *ushc = urb->context; | ||
| 171 | u8 status, last_status; | ||
| 172 | |||
| 173 | if (urb->status < 0) | ||
| 174 | return; | ||
| 175 | |||
| 176 | status = ushc->int_data->status; | ||
| 177 | last_status = ushc->last_status; | ||
| 178 | ushc->last_status = status; | ||
| 179 | |||
| 180 | /* | ||
| 181 | * Ignore the card interrupt status on interrupt transfers that | ||
| 182 | * were submitted while card interrupts where disabled. | ||
| 183 | * | ||
