blob: bb0b1734b60724ac6de582854bda351646e57fed [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* linux/arch/arm/mach-msm/board-swordfish-mmc.c
2 *
3 * Copyright (C) 2008 Google, Inc.
4 *
5 * This software is licensed under the terms of the GNU General Public
6 * License version 2, as published by the Free Software Foundation, and
7 * may be copied, distributed, and modified under those terms.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 *
15 */
16
17#include <linux/delay.h>
18#include <linux/device.h>
19#include <linux/err.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/mmc/host.h>
23#include <linux/mmc/sdio_ids.h>
24#include <linux/platform_device.h>
25
26#include <asm/gpio.h>
27#include <asm/io.h>
28#include <asm/mach/mmc.h>
29
30#include <mach/vreg.h>
31
32#include "proc_comm.h"
33#include "devices.h"
34
35#define FPGA_BASE 0x70000000
36#define FPGA_SDIO_STATUS 0x280
37
38static void __iomem *fpga_base;
39
40#define DEBUG_SWORDFISH_MMC 1
41
42extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat,
43 unsigned int stat_irq, unsigned long stat_irq_flags);
44
45static int config_gpio_table(unsigned *table, int len, int enable)
46{
47 int n;
48 int rc = 0;
49
50 for (n = 0; n < len; n++) {
51 unsigned dis = !enable;
52 unsigned id = table[n];
53
54 if (msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, &dis)) {
55 pr_err("%s: id=0x%08x dis=%d\n", __func__, table[n],
56 dis);
57 rc = -1;
58 }
59 }
60
61 return rc;
62}
63
64static unsigned sdc1_gpio_table[] = {
65 PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
66 PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
67 PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
68 PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
69 PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
70 PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
71};
72
73static unsigned sdc2_gpio_table[] = {
74 PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
75 PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
76 PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
77 PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
78 PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
79 PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA),
80};
81
82static unsigned sdc3_gpio_table[] = {
83 PCOM_GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
84 PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
85 PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
86 PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
87 PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
88 PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
89};
90
91static unsigned sdc4_gpio_table[] = {
92 PCOM_GPIO_CFG(142, 3, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA),
93 PCOM_GPIO_CFG(143, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
94 PCOM_GPIO_CFG(144, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
95 PCOM_GPIO_CFG(145, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
96 PCOM_GPIO_CFG(146, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
97 PCOM_GPIO_CFG(147, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA),
98};
99
100struct sdc_info {
101 unsigned *table;
102 unsigned len;
103};
104
105static struct sdc_info sdcc_gpio_tables[] = {
106 [0] = {
107 .table = sdc1_gpio_table,
108 .len = ARRAY_SIZE(sdc1_gpio_table),
109 },
110 [1] = {
111 .table = sdc2_gpio_table,
112 .len = ARRAY_SIZE(sdc2_gpio_table),
113 },
114 [2] = {
115 .table = sdc3_gpio_table,
116 .len = ARRAY_SIZE(sdc3_gpio_table),
117 },
118 [3] = {
119 .table = sdc4_gpio_table,
120 .len = ARRAY_SIZE(sdc4_gpio_table),
121 },
122};
123
124static int swordfish_sdcc_setup_gpio(int dev_id, unsigned enable)
125{
126 struct sdc_info *info;
127
128 if (dev_id < 1 || dev_id > 4)
129 return -1;
130
131 info = &sdcc_gpio_tables[dev_id - 1];
132 return config_gpio_table(info->table, info->len, enable);
133}
134
135struct mmc_vdd_xlat {
136 int mask;
137 int level;
138};
139
140static struct mmc_vdd_xlat mmc_vdd_table[] = {
141 { MMC_VDD_165_195, 1800 },
142 { MMC_VDD_20_21, 2050 },
143 { MMC_VDD_21_22, 2150 },
144 { MMC_VDD_22_23, 2250 },
145 { MMC_VDD_23_24, 2350 },
146 { MMC_VDD_24_25, 2450 },
147 { MMC_VDD_25_26, 2550 },
148 { MMC_VDD_26_27, 2650 },
149 { MMC_VDD_27_28, 2750 },
150 { MMC_VDD_28_29, 2850 },
151 { MMC_VDD_29_30, 2950 },
152};
153
154static struct vreg *vreg_sdcc;
155static unsigned int vreg_sdcc_enabled;
156static unsigned int sdcc_vdd = 0xffffffff;
157
158static uint32_t sdcc_translate_vdd(struct device *dev, unsigned int vdd)
159{
160 int i;
161 int rc = 0;
162 struct platform_device *pdev;
163
164 pdev = container_of(dev, struct platform_device, dev);
165 BUG_ON(!vreg_sdcc);
166
167 if (vdd == sdcc_vdd)
168 return 0;
169
170 sdcc_vdd = vdd;
171
172 /* enable/disable the signals to the slot */
173 swordfish_sdcc_setup_gpio(pdev->id, !!vdd);
174
175 /* power down */
176 if (vdd == 0) {
177#if DEBUG_SWORDFISH_MMC
178 pr_info("%s: disable sdcc power\n", __func__);
179#endif
180 vreg_disable(vreg_sdcc);
181 vreg_sdcc_enabled = 0;
182 return 0;
183 }
184
185 if (!vreg_sdcc_enabled) {
186 rc = vreg_enable(vreg_sdcc);
187 if (rc)
188 pr_err("%s: Error enabling vreg (%d)\n", __func__, rc);
189 vreg_sdcc_enabled = 1;
190 }
191
192 for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) {
193 if (mmc_vdd_table[i].mask != (1 << vdd))
194 continue;
195#if DEBUG_SWORDFISH_MMC
196 pr_info("%s: Setting level to %u\n", __func__,
197 mmc_vdd_table[i].level);
198#endif
199 rc = vreg_set_level(vreg_sdcc, mmc_vdd_table[i].level);
200 if (rc)
201 pr_err("%s: Error setting vreg level (%d)\n", __func__, rc);
202 return 0;
203 }
204
205 pr_err("%s: Invalid VDD %d specified\n", __func__, vdd);
206 return 0;
207}
208
209static unsigned int swordfish_sdcc_slot_status (struct device *dev)
210{
211 struct platform_device *pdev;
212 uint32_t sdcc_stat;
213
214 pdev = container_of(dev, struct platform_device, dev);
215
216 sdcc_stat = readl(fpga_base + FPGA_SDIO_STATUS);
217
218 /* bit 0 - sdcc1 crd_det
219 * bit 1 - sdcc1 wr_prt
220 * bit 2 - sdcc2 crd_det
221 * bit 3 - sdcc2 wr_prt
222 * etc...
223 */
224
225 /* crd_det is active low */
226 return !(sdcc_stat & (1 << ((pdev->id - 1) << 1)));
227}
228
229#define SWORDFISH_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \
230 | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \
231 | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \
232 | MMC_VDD_28_29 | MMC_VDD_29_30)
233
234static struct mmc_platform_data swordfish_sdcc_data = {
235 .ocr_mask = SWORDFISH_MMC_VDD/*MMC_VDD_27_28 | MMC_VDD_28_29*/,
236 .status = swordfish_sdcc_slot_status,
237 .translate_vdd = sdcc_translate_vdd,
238};
239
240int __init swordfish_init_mmc(void)
241{
242 vreg_sdcc_enabled = 0;
243 vreg_sdcc = vreg_get(NULL, "gp5");
244 if (IS_ERR(vreg_sdcc)) {
245 pr_err("%s: vreg get failed (%ld)\n",
246 __func__, PTR_ERR(vreg_sdcc));
247 return PTR_ERR(vreg_sdcc);
248 }
249
250 fpga_base = ioremap(FPGA_BASE, SZ_4K);
251 if (!fpga_base) {
252 pr_err("%s: Can't ioremap FPGA base address (0x%08x)\n",
253 __func__, FPGA_BASE);
254 vreg_put(vreg_sdcc);
255 return -EIO;
256 }
257
258 msm_add_sdcc(1, &swordfish_sdcc_data, 0, 0);
259 msm_add_sdcc(4, &swordfish_sdcc_data, 0, 0);
260
261 return 0;
262}
263