1 // SPDX-License-Identifier: GPL-2.0-only
3 * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's
5 * Copyright (C) 2015 Broadcom Corporation
9 #include <linux/mmc/host.h>
10 #include <linux/module.h>
12 #include <linux/bitops.h>
13 #include <linux/delay.h>
15 #include "sdhci-pltfm.h"
18 #define SDHCI_VENDOR 0x78
19 #define SDHCI_VENDOR_ENHANCED_STRB 0x1
21 #define BRCMSTB_PRIV_FLAGS_NO_64BIT BIT(0)
22 #define BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT BIT(1)
24 #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
26 struct sdhci_brcmstb_priv
{
27 void __iomem
*cfg_regs
;
31 struct brcmstb_match_priv
{
32 void (*hs400es
)(struct mmc_host
*mmc
, struct mmc_ios
*ios
);
33 struct sdhci_ops
*ops
;
37 static void sdhci_brcmstb_hs400es(struct mmc_host
*mmc
, struct mmc_ios
*ios
)
39 struct sdhci_host
*host
= mmc_priv(mmc
);
43 dev_dbg(mmc_dev(mmc
), "%s(): Setting HS400-Enhanced-Strobe mode\n",
45 reg
= readl(host
->ioaddr
+ SDHCI_VENDOR
);
46 if (ios
->enhanced_strobe
)
47 reg
|= SDHCI_VENDOR_ENHANCED_STRB
;
49 reg
&= ~SDHCI_VENDOR_ENHANCED_STRB
;
50 writel(reg
, host
->ioaddr
+ SDHCI_VENDOR
);
53 static void sdhci_brcmstb_set_clock(struct sdhci_host
*host
, unsigned int clock
)
57 host
->mmc
->actual_clock
= 0;
59 clk
= sdhci_calc_clk(host
, clock
, &host
->mmc
->actual_clock
);
60 sdhci_writew(host
, clk
, SDHCI_CLOCK_CONTROL
);
65 sdhci_enable_clk(host
, clk
);
68 static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host
*host
,
73 dev_dbg(mmc_dev(host
->mmc
), "%s: Setting UHS signaling for %d timing\n",
75 ctrl_2
= sdhci_readw(host
, SDHCI_HOST_CONTROL2
);
76 /* Select Bus Speed Mode for host */
77 ctrl_2
&= ~SDHCI_CTRL_UHS_MASK
;
78 if ((timing
== MMC_TIMING_MMC_HS200
) ||
79 (timing
== MMC_TIMING_UHS_SDR104
))
80 ctrl_2
|= SDHCI_CTRL_UHS_SDR104
;
81 else if (timing
== MMC_TIMING_UHS_SDR12
)
82 ctrl_2
|= SDHCI_CTRL_UHS_SDR12
;
83 else if (timing
== MMC_TIMING_SD_HS
||
84 timing
== MMC_TIMING_MMC_HS
||
85 timing
== MMC_TIMING_UHS_SDR25
)
86 ctrl_2
|= SDHCI_CTRL_UHS_SDR25
;
87 else if (timing
== MMC_TIMING_UHS_SDR50
)
88 ctrl_2
|= SDHCI_CTRL_UHS_SDR50
;
89 else if ((timing
== MMC_TIMING_UHS_DDR50
) ||
90 (timing
== MMC_TIMING_MMC_DDR52
))
91 ctrl_2
|= SDHCI_CTRL_UHS_DDR50
;
92 else if (timing
== MMC_TIMING_MMC_HS400
)
93 ctrl_2
|= SDHCI_CTRL_HS400
; /* Non-standard */
94 sdhci_writew(host
, ctrl_2
, SDHCI_HOST_CONTROL2
);
97 static void sdhci_brcmstb_dumpregs(struct mmc_host
*mmc
)
99 sdhci_dumpregs(mmc_priv(mmc
));
102 static void sdhci_brcmstb_cqe_enable(struct mmc_host
*mmc
)
104 struct sdhci_host
*host
= mmc_priv(mmc
);
107 reg
= sdhci_readl(host
, SDHCI_PRESENT_STATE
);
108 while (reg
& SDHCI_DATA_AVAILABLE
) {
109 sdhci_readl(host
, SDHCI_BUFFER
);
110 reg
= sdhci_readl(host
, SDHCI_PRESENT_STATE
);
113 sdhci_cqe_enable(mmc
);
116 static const struct cqhci_host_ops sdhci_brcmstb_cqhci_ops
= {
117 .enable
= sdhci_brcmstb_cqe_enable
,
118 .disable
= sdhci_cqe_disable
,
119 .dumpregs
= sdhci_brcmstb_dumpregs
,
122 static struct sdhci_ops sdhci_brcmstb_ops
= {
123 .set_clock
= sdhci_set_clock
,
124 .set_bus_width
= sdhci_set_bus_width
,
125 .reset
= sdhci_reset
,
126 .set_uhs_signaling
= sdhci_set_uhs_signaling
,
129 static struct sdhci_ops sdhci_brcmstb_ops_7216
= {
130 .set_clock
= sdhci_brcmstb_set_clock
,
131 .set_bus_width
= sdhci_set_bus_width
,
132 .reset
= sdhci_reset
,
133 .set_uhs_signaling
= sdhci_brcmstb_set_uhs_signaling
,
136 static struct brcmstb_match_priv match_priv_7425
= {
137 .flags
= BRCMSTB_PRIV_FLAGS_NO_64BIT
|
138 BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT
,
139 .ops
= &sdhci_brcmstb_ops
,
142 static struct brcmstb_match_priv match_priv_7445
= {
143 .flags
= BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT
,
144 .ops
= &sdhci_brcmstb_ops
,
147 static const struct brcmstb_match_priv match_priv_7216
= {
148 .hs400es
= sdhci_brcmstb_hs400es
,
149 .ops
= &sdhci_brcmstb_ops_7216
,
152 static const struct of_device_id sdhci_brcm_of_match
[] = {
153 { .compatible
= "brcm,bcm7425-sdhci", .data
= &match_priv_7425
},
154 { .compatible
= "brcm,bcm7445-sdhci", .data
= &match_priv_7445
},
155 { .compatible
= "brcm,bcm7216-sdhci", .data
= &match_priv_7216
},
159 static u32
sdhci_brcmstb_cqhci_irq(struct sdhci_host
*host
, u32 intmask
)
164 if (!sdhci_cqe_irq(host
, intmask
, &cmd_error
, &data_error
))
167 cqhci_irq(host
->mmc
, intmask
, cmd_error
, data_error
);
172 static int sdhci_brcmstb_add_host(struct sdhci_host
*host
,
173 struct sdhci_brcmstb_priv
*priv
)
175 struct cqhci_host
*cq_host
;
180 return sdhci_add_host(host
);
182 dev_dbg(mmc_dev(host
->mmc
), "CQE is enabled\n");
183 host
->mmc
->caps2
|= MMC_CAP2_CQE
| MMC_CAP2_CQE_DCMD
;
184 ret
= sdhci_setup_host(host
);
188 cq_host
= devm_kzalloc(mmc_dev(host
->mmc
),
189 sizeof(*cq_host
), GFP_KERNEL
);
195 cq_host
->mmio
= host
->ioaddr
+ SDHCI_ARASAN_CQE_BASE_ADDR
;
196 cq_host
->ops
= &sdhci_brcmstb_cqhci_ops
;
198 dma64
= host
->flags
& SDHCI_USE_64_BIT_DMA
;
200 dev_dbg(mmc_dev(host
->mmc
), "Using 64 bit DMA\n");
201 cq_host
->caps
|= CQHCI_TASK_DESC_SZ_128
;
202 cq_host
->quirks
|= CQHCI_QUIRK_SHORT_TXFR_DESC_SZ
;
205 ret
= cqhci_init(cq_host
, host
->mmc
, dma64
);
209 ret
= __sdhci_add_host(host
);
216 sdhci_cleanup_host(host
);
220 static int sdhci_brcmstb_probe(struct platform_device
*pdev
)
222 const struct brcmstb_match_priv
*match_priv
;
223 struct sdhci_pltfm_data brcmstb_pdata
;
224 struct sdhci_pltfm_host
*pltfm_host
;
225 const struct of_device_id
*match
;
226 struct sdhci_brcmstb_priv
*priv
;
227 struct sdhci_host
*host
;
228 struct resource
*iomem
;
229 bool has_cqe
= false;
233 match
= of_match_node(sdhci_brcm_of_match
, pdev
->dev
.of_node
);
234 match_priv
= match
->data
;
236 dev_dbg(&pdev
->dev
, "Probe found match for %s\n", match
->compatible
);
238 clk
= devm_clk_get(&pdev
->dev
, NULL
);
240 if (PTR_ERR(clk
) == -EPROBE_DEFER
)
241 return -EPROBE_DEFER
;
242 dev_err(&pdev
->dev
, "Clock not found in Device Tree\n");
245 res
= clk_prepare_enable(clk
);
249 memset(&brcmstb_pdata
, 0, sizeof(brcmstb_pdata
));
250 if (device_property_read_bool(&pdev
->dev
, "supports-cqe")) {
252 match_priv
->ops
->irq
= sdhci_brcmstb_cqhci_irq
;
254 brcmstb_pdata
.ops
= match_priv
->ops
;
255 host
= sdhci_pltfm_init(pdev
, &brcmstb_pdata
,
256 sizeof(struct sdhci_brcmstb_priv
));
262 pltfm_host
= sdhci_priv(host
);
263 priv
= sdhci_pltfm_priv(pltfm_host
);
264 priv
->has_cqe
= has_cqe
;
266 /* Map in the non-standard CFG registers */
267 iomem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
268 priv
->cfg_regs
= devm_ioremap_resource(&pdev
->dev
, iomem
);
269 if (IS_ERR(priv
->cfg_regs
)) {
270 res
= PTR_ERR(priv
->cfg_regs
);
274 sdhci_get_of_property(pdev
);
275 res
= mmc_of_parse(host
->mmc
);
280 * If the chip has enhanced strobe and it's enabled, add
283 if (match_priv
->hs400es
&&
284 (host
->mmc
->caps2
& MMC_CAP2_HS400_ES
))
285 host
->mmc_host_ops
.hs400_enhanced_strobe
= match_priv
->hs400es
;
288 * Supply the existing CAPS, but clear the UHS modes. This
289 * will allow these modes to be specified by device tree
290 * properties through mmc_of_parse().
292 host
->caps
= sdhci_readl(host
, SDHCI_CAPABILITIES
);
293 if (match_priv
->flags
& BRCMSTB_PRIV_FLAGS_NO_64BIT
)
294 host
->caps
&= ~SDHCI_CAN_64BIT
;
295 host
->caps1
= sdhci_readl(host
, SDHCI_CAPABILITIES_1
);
296 host
->caps1
&= ~(SDHCI_SUPPORT_SDR50
| SDHCI_SUPPORT_SDR104
|
297 SDHCI_SUPPORT_DDR50
);
298 host
->quirks
|= SDHCI_QUIRK_MISSING_CAPS
;
300 if (match_priv
->flags
& BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT
)
301 host
->quirks
|= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
;
303 res
= sdhci_brcmstb_add_host(host
, priv
);
307 pltfm_host
->clk
= clk
;
311 sdhci_pltfm_free(pdev
);
313 clk_disable_unprepare(clk
);
317 static void sdhci_brcmstb_shutdown(struct platform_device
*pdev
)
321 ret
= sdhci_pltfm_unregister(pdev
);
323 dev_err(&pdev
->dev
, "failed to shutdown\n");
326 MODULE_DEVICE_TABLE(of
, sdhci_brcm_of_match
);
328 static struct platform_driver sdhci_brcmstb_driver
= {
330 .name
= "sdhci-brcmstb",
331 .pm
= &sdhci_pltfm_pmops
,
332 .of_match_table
= of_match_ptr(sdhci_brcm_of_match
),
334 .probe
= sdhci_brcmstb_probe
,
335 .remove
= sdhci_pltfm_unregister
,
336 .shutdown
= sdhci_brcmstb_shutdown
,
339 module_platform_driver(sdhci_brcmstb_driver
);
341 MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs");
342 MODULE_AUTHOR("Broadcom");
343 MODULE_LICENSE("GPL v2");