aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/Kconfig14
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/ushc.c566
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
455config 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
36obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o 36obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
37obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o 37obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
38obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o 38obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
39obj-$(CONFIG_MMC_USHC) += ushc.o
39 40
40obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o 41obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
41sdhci-platform-y := sdhci-pltfm.o 42sdhci-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
27enum 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
37enum 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
68struct 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
77struct ushc_csw {
78 __u8 signature;
79 __u8 status;
80 __le32 response;
81} __attribute__((packed));
82
83#define USHC_CSW_SIGNATURE 'S'
84
85struct 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
94struct 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
122static void data_callback(struct urb *urb);
123
124static 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
131static 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
153static 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
168static 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 *