Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's
  4 *
  5 * Copyright (C) 2015 Broadcom Corporation
  6 */
  7
  8#include <linux/io.h>
  9#include <linux/mmc/host.h>
 10#include <linux/module.h>
 11#include <linux/of.h>
 12#include <linux/bitops.h>
 13#include <linux/delay.h>
 14
 15#include "sdhci-pltfm.h"
 16#include "cqhci.h"
 17
 18#define SDHCI_VENDOR 0x78
 19#define  SDHCI_VENDOR_ENHANCED_STRB 0x1
 20
 21#define BRCMSTB_PRIV_FLAGS_NO_64BIT		BIT(0)
 22#define BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT	BIT(1)
 23
 24#define SDHCI_ARASAN_CQE_BASE_ADDR		0x200
 25
 26struct sdhci_brcmstb_priv {
 27	void __iomem *cfg_regs;
 28	bool has_cqe;
 29};
 30
 31struct brcmstb_match_priv {
 32	void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
 33	struct sdhci_ops *ops;
 34	unsigned int flags;
 35};
 36
 37static void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios)
 38{
 39	struct sdhci_host *host = mmc_priv(mmc);
 40
 41	u32 reg;
 42
 43	dev_dbg(mmc_dev(mmc), "%s(): Setting HS400-Enhanced-Strobe mode\n",
 44		__func__);
 45	reg = readl(host->ioaddr + SDHCI_VENDOR);
 46	if (ios->enhanced_strobe)
 47		reg |= SDHCI_VENDOR_ENHANCED_STRB;
 48	else
 49		reg &= ~SDHCI_VENDOR_ENHANCED_STRB;
 50	writel(reg, host->ioaddr + SDHCI_VENDOR);
 51}
 52
 53static void sdhci_brcmstb_set_clock(struct sdhci_host *host, unsigned int clock)
 54{
 55	u16 clk;
 56
 57	host->mmc->actual_clock = 0;
 58
 59	clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
 60	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 61
 62	if (clock == 0)
 63		return;
 64
 65	sdhci_enable_clk(host, clk);
 66}
 67
 68static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
 69					    unsigned int timing)
 70{
 71	u16 ctrl_2;
 72
 73	dev_dbg(mmc_dev(host->mmc), "%s: Setting UHS signaling for %d timing\n",
 74		__func__, timing);
 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);
 95}
 96
 97static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
 98{
 99	sdhci_dumpregs(mmc_priv(mmc));
100}
101
102static void sdhci_brcmstb_cqe_enable(struct mmc_host *mmc)
103{
104	struct sdhci_host *host = mmc_priv(mmc);
105	u32 reg;
106
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);
111	}
112
113	sdhci_cqe_enable(mmc);
114}
115
116static 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,
120};
121
122static 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,
127};
128
129static 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,
134};
135
136static 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,
140};
141
142static struct brcmstb_match_priv match_priv_7445 = {
143	.flags = BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT,
144	.ops = &sdhci_brcmstb_ops,
145};
146
147static const struct brcmstb_match_priv match_priv_7216 = {
148	.hs400es = sdhci_brcmstb_hs400es,
149	.ops = &sdhci_brcmstb_ops_7216,
150};
151
152static 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 },
156	{},
157};
158
159static u32 sdhci_brcmstb_cqhci_irq(struct sdhci_host *host, u32 intmask)
160{
161	int cmd_error = 0;
162	int data_error = 0;
163
164	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
165		return intmask;
166
167	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
168
169	return 0;
170}
171
172static int sdhci_brcmstb_add_host(struct sdhci_host *host,
173				  struct sdhci_brcmstb_priv *priv)
174{
175	struct cqhci_host *cq_host;
176	bool dma64;
177	int ret;
178
179	if (!priv->has_cqe)
180		return sdhci_add_host(host);
181
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);
185	if (ret)
186		return ret;
187
188	cq_host = devm_kzalloc(mmc_dev(host->mmc),
189			       sizeof(*cq_host), GFP_KERNEL);
190	if (!cq_host) {
191		ret = -ENOMEM;
192		goto cleanup;
193	}
194
195	cq_host->mmio = host->ioaddr + SDHCI_ARASAN_CQE_BASE_ADDR;
196	cq_host->ops = &sdhci_brcmstb_cqhci_ops;
197
198	dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
199	if (dma64) {
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;
203	}
204
205	ret = cqhci_init(cq_host, host->mmc, dma64);
206	if (ret)
207		goto cleanup;
208
209	ret = __sdhci_add_host(host);
210	if (ret)
211		goto cleanup;
212
213	return 0;
214
215cleanup:
216	sdhci_cleanup_host(host);
217	return ret;
218}
219
220static int sdhci_brcmstb_probe(struct platform_device *pdev)
221{
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;
230	struct clk *clk;
231	int res;
232
233	match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node);
234	match_priv = match->data;
235
236	dev_dbg(&pdev->dev, "Probe found match for %s\n",  match->compatible);
237
238	clk = devm_clk_get(&pdev->dev, NULL);
239	if (IS_ERR(clk)) {
240		if (PTR_ERR(clk) == -EPROBE_DEFER)
241			return -EPROBE_DEFER;
242		dev_err(&pdev->dev, "Clock not found in Device Tree\n");
243		clk = NULL;
244	}
245	res = clk_prepare_enable(clk);
246	if (res)
247		return res;
248
249	memset(&brcmstb_pdata, 0, sizeof(brcmstb_pdata));
250	if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
251		has_cqe = true;
252		match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
253	}
254	brcmstb_pdata.ops = match_priv->ops;
255	host = sdhci_pltfm_init(pdev, &brcmstb_pdata,
256				sizeof(struct sdhci_brcmstb_priv));
257	if (IS_ERR(host)) {
258		res = PTR_ERR(host);
259		goto err_clk;
260	}
261
262	pltfm_host = sdhci_priv(host);
263	priv = sdhci_pltfm_priv(pltfm_host);
264	priv->has_cqe = has_cqe;
265
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);
271		goto err;
272	}
273
274	sdhci_get_of_property(pdev);
275	res = mmc_of_parse(host->mmc);
276	if (res)
277		goto err;
278
279	/*
280	 * If the chip has enhanced strobe and it's enabled, add
281	 * callback
282	 */
283	if (match_priv->hs400es &&
284	    (host->mmc->caps2 & MMC_CAP2_HS400_ES))
285		host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
286
287	/*
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().
291	 */
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;
299
300	if (match_priv->flags & BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT)
301		host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
302
303	res = sdhci_brcmstb_add_host(host, priv);
304	if (res)
305		goto err;
306
307	pltfm_host->clk = clk;
308	return res;
309
310err:
311	sdhci_pltfm_free(pdev);
312err_clk:
313	clk_disable_unprepare(clk);
314	return res;
315}
316
317static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
318{
319	int ret;
320
321	ret = sdhci_pltfm_unregister(pdev);
322	if (ret)
323		dev_err(&pdev->dev, "failed to shutdown\n");
324}
325
326MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
327
328static struct platform_driver sdhci_brcmstb_driver = {
329	.driver		= {
330		.name	= "sdhci-brcmstb",
331		.pm	= &sdhci_pltfm_pmops,
332		.of_match_table = of_match_ptr(sdhci_brcm_of_match),
333	},
334	.probe		= sdhci_brcmstb_probe,
335	.remove		= sdhci_pltfm_unregister,
336	.shutdown	= sdhci_brcmstb_shutdown,
337};
338
339module_platform_driver(sdhci_brcmstb_driver);
340
341MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs");
342MODULE_AUTHOR("Broadcom");
343MODULE_LICENSE("GPL v2");