2 * Support of SDHCI platform devices for Microchip PIC32.
4 * Copyright (C) 2015 Microchip
5 * Andrei Pistirica, Paul Thacker
7 * Inspired by sdhci-pltfm.c
9 * This file is licensed under the terms of the GNU General Public
10 * License version 2. This program is licensed "as is" without any
11 * warranty of any kind, whether express or implied.
14 #include <linux/clk.h>
15 #include <linux/delay.h>
16 #include <linux/highmem.h>
17 #include <linux/module.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
21 #include <linux/platform_device.h>
23 #include <linux/slab.h>
24 #include <linux/mmc/host.h>
27 #include "sdhci-pltfm.h"
28 #include <linux/platform_data/sdhci-pic32.h>
30 #define SDH_SHARED_BUS_CTRL 0x000000E0
31 #define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x7
32 #define SDH_SHARED_BUS_NR_IRQ_PINS_MASK 0x30
33 #define SDH_SHARED_BUS_CLK_PINS 0x10
34 #define SDH_SHARED_BUS_IRQ_PINS 0x14
35 #define SDH_CAPS_SDH_SLOT_TYPE_MASK 0xC0000000
36 #define SDH_SLOT_TYPE_REMOVABLE 0x0
37 #define SDH_SLOT_TYPE_EMBEDDED 0x1
38 #define SDH_SLOT_TYPE_SHARED_BUS 0x2
39 #define SDHCI_CTRL_CDSSEL 0x80
40 #define SDHCI_CTRL_CDTLVL 0x40
42 #define ADMA_FIFO_RD_THSHLD 512
43 #define ADMA_FIFO_WR_THSHLD 512
45 struct pic32_sdhci_priv
{
46 struct platform_device
*pdev
;
51 static unsigned int pic32_sdhci_get_max_clock(struct sdhci_host
*host
)
53 struct pic32_sdhci_priv
*sdhci_pdata
= sdhci_priv(host
);
55 return clk_get_rate(sdhci_pdata
->base_clk
);
58 static void pic32_sdhci_set_bus_width(struct sdhci_host
*host
, int width
)
62 ctrl
= sdhci_readb(host
, SDHCI_HOST_CONTROL
);
63 if (width
== MMC_BUS_WIDTH_8
) {
64 ctrl
&= ~SDHCI_CTRL_4BITBUS
;
65 if (host
->version
>= SDHCI_SPEC_300
)
66 ctrl
|= SDHCI_CTRL_8BITBUS
;
68 if (host
->version
>= SDHCI_SPEC_300
)
69 ctrl
&= ~SDHCI_CTRL_8BITBUS
;
70 if (width
== MMC_BUS_WIDTH_4
)
71 ctrl
|= SDHCI_CTRL_4BITBUS
;
73 ctrl
&= ~SDHCI_CTRL_4BITBUS
;
76 /* CD select and test bits must be set for errata workaround. */
77 ctrl
&= ~SDHCI_CTRL_CDTLVL
;
78 ctrl
|= SDHCI_CTRL_CDSSEL
;
79 sdhci_writeb(host
, ctrl
, SDHCI_HOST_CONTROL
);
82 static unsigned int pic32_sdhci_get_ro(struct sdhci_host
*host
)
85 * The SDHCI_WRITE_PROTECT bit is unstable on current hardware so we
86 * can't depend on its value in any way.
91 static const struct sdhci_ops pic32_sdhci_ops
= {
92 .get_max_clock
= pic32_sdhci_get_max_clock
,
93 .set_clock
= sdhci_set_clock
,
94 .set_bus_width
= pic32_sdhci_set_bus_width
,
96 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
97 .get_ro
= pic32_sdhci_get_ro
,
100 static struct sdhci_pltfm_data sdhci_pic32_pdata
= {
101 .ops
= &pic32_sdhci_ops
,
102 .quirks
= SDHCI_QUIRK_NO_HISPD_BIT
,
103 .quirks2
= SDHCI_QUIRK2_NO_1_8_V
,
106 static void pic32_sdhci_shared_bus(struct platform_device
*pdev
)
108 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
109 u32 bus
= readl(host
->ioaddr
+ SDH_SHARED_BUS_CTRL
);
110 u32 clk_pins
= (bus
& SDH_SHARED_BUS_NR_CLK_PINS_MASK
) >> 0;
111 u32 irq_pins
= (bus
& SDH_SHARED_BUS_NR_IRQ_PINS_MASK
) >> 4;
113 /* select first clock */
115 bus
|= (1 << SDH_SHARED_BUS_CLK_PINS
);
117 /* select first interrupt */
119 bus
|= (1 << SDH_SHARED_BUS_IRQ_PINS
);
121 writel(bus
, host
->ioaddr
+ SDH_SHARED_BUS_CTRL
);
124 static int pic32_sdhci_probe_platform(struct platform_device
*pdev
,
125 struct pic32_sdhci_priv
*pdata
)
129 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
131 /* Check card slot connected on shared bus. */
132 host
->caps
= readl(host
->ioaddr
+ SDHCI_CAPABILITIES
);
133 caps_slot_type
= (host
->caps
& SDH_CAPS_SDH_SLOT_TYPE_MASK
) >> 30;
134 if (caps_slot_type
== SDH_SLOT_TYPE_SHARED_BUS
)
135 pic32_sdhci_shared_bus(pdev
);
140 static int pic32_sdhci_probe(struct platform_device
*pdev
)
142 struct sdhci_host
*host
;
143 struct sdhci_pltfm_host
*pltfm_host
;
144 struct pic32_sdhci_priv
*sdhci_pdata
;
145 struct pic32_sdhci_platform_data
*plat_data
;
148 host
= sdhci_pltfm_init(pdev
, &sdhci_pic32_pdata
,
149 sizeof(struct pic32_sdhci_priv
));
155 pltfm_host
= sdhci_priv(host
);
156 sdhci_pdata
= sdhci_pltfm_priv(pltfm_host
);
158 plat_data
= pdev
->dev
.platform_data
;
159 if (plat_data
&& plat_data
->setup_dma
) {
160 ret
= plat_data
->setup_dma(ADMA_FIFO_RD_THSHLD
,
161 ADMA_FIFO_WR_THSHLD
);
166 sdhci_pdata
->sys_clk
= devm_clk_get(&pdev
->dev
, "sys_clk");
167 if (IS_ERR(sdhci_pdata
->sys_clk
)) {
168 ret
= PTR_ERR(sdhci_pdata
->sys_clk
);
169 dev_err(&pdev
->dev
, "Error getting clock\n");
173 ret
= clk_prepare_enable(sdhci_pdata
->sys_clk
);
175 dev_err(&pdev
->dev
, "Error enabling clock\n");
179 sdhci_pdata
->base_clk
= devm_clk_get(&pdev
->dev
, "base_clk");
180 if (IS_ERR(sdhci_pdata
->base_clk
)) {
181 ret
= PTR_ERR(sdhci_pdata
->base_clk
);
182 dev_err(&pdev
->dev
, "Error getting clock\n");
186 ret
= clk_prepare_enable(sdhci_pdata
->base_clk
);
188 dev_err(&pdev
->dev
, "Error enabling clock\n");
192 ret
= mmc_of_parse(host
->mmc
);
196 ret
= pic32_sdhci_probe_platform(pdev
, sdhci_pdata
);
198 dev_err(&pdev
->dev
, "failed to probe platform!\n");
202 ret
= sdhci_add_host(host
);
204 dev_err(&pdev
->dev
, "error adding host\n");
208 dev_info(&pdev
->dev
, "Successfully added sdhci host\n");
212 clk_disable_unprepare(sdhci_pdata
->base_clk
);
214 clk_disable_unprepare(sdhci_pdata
->sys_clk
);
216 sdhci_pltfm_free(pdev
);
218 dev_err(&pdev
->dev
, "pic32-sdhci probe failed: %d\n", ret
);
222 static int pic32_sdhci_remove(struct platform_device
*pdev
)
224 struct sdhci_host
*host
= platform_get_drvdata(pdev
);
225 struct pic32_sdhci_priv
*sdhci_pdata
= sdhci_priv(host
);
228 scratch
= readl(host
->ioaddr
+ SDHCI_INT_STATUS
);
229 sdhci_remove_host(host
, scratch
== (u32
)~0);
230 clk_disable_unprepare(sdhci_pdata
->base_clk
);
231 clk_disable_unprepare(sdhci_pdata
->sys_clk
);
232 sdhci_pltfm_free(pdev
);
237 static const struct of_device_id pic32_sdhci_id_table
[] = {
238 { .compatible
= "microchip,pic32mzda-sdhci" },
241 MODULE_DEVICE_TABLE(of
, pic32_sdhci_id_table
);
243 static struct platform_driver pic32_sdhci_driver
= {
245 .name
= "pic32-sdhci",
246 .of_match_table
= of_match_ptr(pic32_sdhci_id_table
),
248 .probe
= pic32_sdhci_probe
,
249 .remove
= pic32_sdhci_remove
,
252 module_platform_driver(pic32_sdhci_driver
);
254 MODULE_DESCRIPTION("Microchip PIC32 SDHCI driver");
255 MODULE_AUTHOR("Pistirica Sorin Andrei & Sandeep Sheriker");
256 MODULE_LICENSE("GPL v2");