1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Copyright (C) 2019 ASPEED Technology Inc. */
3 /* Copyright (C) 2019 IBM Corp. */
6 #include <linux/delay.h>
7 #include <linux/device.h>
9 #include <linux/mmc/host.h>
10 #include <linux/module.h>
11 #include <linux/of_address.h>
13 #include <linux/of_platform.h>
14 #include <linux/platform_device.h>
15 #include <linux/spinlock.h>
17 #include "sdhci-pltfm.h"
19 #define ASPEED_SDC_INFO 0x00
20 #define ASPEED_SDC_S1MMC8 BIT(25)
21 #define ASPEED_SDC_S0MMC8 BIT(24)
32 struct aspeed_sdc
*parent
;
36 static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc
*sdc
,
37 struct aspeed_sdhci
*sdhci
,
42 /* Set/clear 8 bit mode */
43 spin_lock(&sdc
->lock
);
44 info
= readl(sdc
->regs
+ ASPEED_SDC_INFO
);
46 info
|= sdhci
->width_mask
;
48 info
&= ~sdhci
->width_mask
;
49 writel(info
, sdc
->regs
+ ASPEED_SDC_INFO
);
50 spin_unlock(&sdc
->lock
);
53 static void aspeed_sdhci_set_clock(struct sdhci_host
*host
, unsigned int clock
)
55 struct sdhci_pltfm_host
*pltfm_host
;
60 pltfm_host
= sdhci_priv(host
);
61 parent
= clk_get_rate(pltfm_host
->clk
);
63 sdhci_writew(host
, 0, SDHCI_CLOCK_CONTROL
);
68 if (WARN_ON(clock
> host
->max_clk
))
69 clock
= host
->max_clk
;
71 for (div
= 1; div
< 256; div
*= 2) {
72 if ((parent
/ div
) <= clock
)
77 clk
= div
<< SDHCI_DIVIDER_SHIFT
;
79 sdhci_enable_clk(host
, clk
);
82 static unsigned int aspeed_sdhci_get_max_clock(struct sdhci_host
*host
)
85 return host
->mmc
->f_max
;
87 return sdhci_pltfm_clk_get_max_clock(host
);
90 static void aspeed_sdhci_set_bus_width(struct sdhci_host
*host
, int width
)
92 struct sdhci_pltfm_host
*pltfm_priv
;
93 struct aspeed_sdhci
*aspeed_sdhci
;
94 struct aspeed_sdc
*aspeed_sdc
;
97 pltfm_priv
= sdhci_priv(host
);
98 aspeed_sdhci
= sdhci_pltfm_priv(pltfm_priv
);
99 aspeed_sdc
= aspeed_sdhci
->parent
;
101 /* Set/clear 8-bit mode */
102 aspeed_sdc_configure_8bit_mode(aspeed_sdc
, aspeed_sdhci
,
103 width
== MMC_BUS_WIDTH_8
);
105 /* Set/clear 1 or 4 bit mode */
106 ctrl
= sdhci_readb(host
, SDHCI_HOST_CONTROL
);
107 if (width
== MMC_BUS_WIDTH_4
)
108 ctrl
|= SDHCI_CTRL_4BITBUS
;
110 ctrl
&= ~SDHCI_CTRL_4BITBUS
;
111 sdhci_writeb(host
, ctrl
, SDHCI_HOST_CONTROL
);
114 static u32
aspeed_sdhci_readl(struct sdhci_host
*host
, int reg
)
116 u32 val
= readl(host
->ioaddr
+ reg
);
118 if (unlikely(reg
== SDHCI_PRESENT_STATE
) &&
119 (host
->mmc
->caps2
& MMC_CAP2_CD_ACTIVE_HIGH
))
120 val
^= SDHCI_CARD_PRESENT
;
125 static const struct sdhci_ops aspeed_sdhci_ops
= {
126 .read_l
= aspeed_sdhci_readl
,
127 .set_clock
= aspeed_sdhci_set_clock
,
128 .get_max_clock
= aspeed_sdhci_get_max_clock
,
129 .set_bus_width
= aspeed_sdhci_set_bus_width
,
130 .get_timeout_clock
= sdhci_pltfm_clk_get_max_clock
,
131 .reset
= sdhci_reset
,
132 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
135 static const struct sdhci_pltfm_data aspeed_sdhci_pdata
= {
136 .ops
= &aspeed_sdhci_ops
,
137 .quirks
= SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN
,
140 static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci
*dev
,
141 struct resource
*res
)
143 resource_size_t delta
;
145 if (!res
|| resource_type(res
) != IORESOURCE_MEM
)
148 if (res
->start
< dev
->parent
->res
->start
)
151 delta
= res
->start
- dev
->parent
->res
->start
;
152 if (delta
& (0x100 - 1))
155 return (delta
/ 0x100) - 1;
158 static int aspeed_sdhci_probe(struct platform_device
*pdev
)
160 struct sdhci_pltfm_host
*pltfm_host
;
161 struct aspeed_sdhci
*dev
;
162 struct sdhci_host
*host
;
163 struct resource
*res
;
167 host
= sdhci_pltfm_init(pdev
, &aspeed_sdhci_pdata
, sizeof(*dev
));
169 return PTR_ERR(host
);
171 pltfm_host
= sdhci_priv(host
);
172 dev
= sdhci_pltfm_priv(pltfm_host
);
173 dev
->parent
= dev_get_drvdata(pdev
->dev
.parent
);
175 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
176 slot
= aspeed_sdhci_calculate_slot(dev
, res
);
183 dev_info(&pdev
->dev
, "Configuring for slot %d\n", slot
);
184 dev
->width_mask
= !slot
? ASPEED_SDC_S0MMC8
: ASPEED_SDC_S1MMC8
;
186 sdhci_get_of_property(pdev
);
188 pltfm_host
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
189 if (IS_ERR(pltfm_host
->clk
))
190 return PTR_ERR(pltfm_host
->clk
);
192 ret
= clk_prepare_enable(pltfm_host
->clk
);
194 dev_err(&pdev
->dev
, "Unable to enable SDIO clock\n");
198 ret
= mmc_of_parse(host
->mmc
);
202 ret
= sdhci_add_host(host
);
209 clk_disable_unprepare(pltfm_host
->clk
);
211 sdhci_pltfm_free(pdev
);
215 static int aspeed_sdhci_remove(struct platform_device
*pdev
)
217 struct sdhci_pltfm_host
*pltfm_host
;
218 struct sdhci_host
*host
;
221 host
= platform_get_drvdata(pdev
);
222 pltfm_host
= sdhci_priv(host
);
224 sdhci_remove_host(host
, dead
);
226 clk_disable_unprepare(pltfm_host
->clk
);
228 sdhci_pltfm_free(pdev
);
233 static const struct of_device_id aspeed_sdhci_of_match
[] = {
234 { .compatible
= "aspeed,ast2400-sdhci", },
235 { .compatible
= "aspeed,ast2500-sdhci", },
236 { .compatible
= "aspeed,ast2600-sdhci", },
240 static struct platform_driver aspeed_sdhci_driver
= {
242 .name
= "sdhci-aspeed",
243 .of_match_table
= aspeed_sdhci_of_match
,
245 .probe
= aspeed_sdhci_probe
,
246 .remove
= aspeed_sdhci_remove
,
249 static int aspeed_sdc_probe(struct platform_device
*pdev
)
252 struct device_node
*parent
, *child
;
253 struct aspeed_sdc
*sdc
;
256 sdc
= devm_kzalloc(&pdev
->dev
, sizeof(*sdc
), GFP_KERNEL
);
260 spin_lock_init(&sdc
->lock
);
262 sdc
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
263 if (IS_ERR(sdc
->clk
))
264 return PTR_ERR(sdc
->clk
);
266 ret
= clk_prepare_enable(sdc
->clk
);
268 dev_err(&pdev
->dev
, "Unable to enable SDCLK\n");
272 sdc
->res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
273 sdc
->regs
= devm_ioremap_resource(&pdev
->dev
, sdc
->res
);
274 if (IS_ERR(sdc
->regs
)) {
275 ret
= PTR_ERR(sdc
->regs
);
279 dev_set_drvdata(&pdev
->dev
, sdc
);
281 parent
= pdev
->dev
.of_node
;
282 for_each_available_child_of_node(parent
, child
) {
283 struct platform_device
*cpdev
;
285 cpdev
= of_platform_device_create(child
, NULL
, &pdev
->dev
);
296 clk_disable_unprepare(sdc
->clk
);
300 static int aspeed_sdc_remove(struct platform_device
*pdev
)
302 struct aspeed_sdc
*sdc
= dev_get_drvdata(&pdev
->dev
);
304 clk_disable_unprepare(sdc
->clk
);
309 static const struct of_device_id aspeed_sdc_of_match
[] = {
310 { .compatible
= "aspeed,ast2400-sd-controller", },
311 { .compatible
= "aspeed,ast2500-sd-controller", },
312 { .compatible
= "aspeed,ast2600-sd-controller", },
316 MODULE_DEVICE_TABLE(of
, aspeed_sdc_of_match
);
318 static struct platform_driver aspeed_sdc_driver
= {
320 .name
= "sd-controller-aspeed",
321 .pm
= &sdhci_pltfm_pmops
,
322 .of_match_table
= aspeed_sdc_of_match
,
324 .probe
= aspeed_sdc_probe
,
325 .remove
= aspeed_sdc_remove
,
328 static int __init
aspeed_sdc_init(void)
332 rc
= platform_driver_register(&aspeed_sdhci_driver
);
336 rc
= platform_driver_register(&aspeed_sdc_driver
);
338 platform_driver_unregister(&aspeed_sdhci_driver
);
342 module_init(aspeed_sdc_init
);
344 static void __exit
aspeed_sdc_exit(void)
346 platform_driver_unregister(&aspeed_sdc_driver
);
347 platform_driver_unregister(&aspeed_sdhci_driver
);
349 module_exit(aspeed_sdc_exit
);
351 MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers");
352 MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>");
353 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
354 MODULE_LICENSE("GPL");