PageRenderTime 57ms CodeModel.GetById 12ms app.highlight 39ms RepoModel.GetById 2ms app.codeStats 0ms

/arch/arm/mach-msm/board-swordfish-mmc.c

https://github.com/AICP/kernel_google_msm
C | 263 lines | 194 code | 45 blank | 24 comment | 16 complexity | d4389de7685ac64d665bab6a099d08da MD5 | raw file
  1/* 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#include <mach/proc_comm.h>
 32
 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