Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | // SPDX-License-Identifier: GPL-2.0-or-later /* * (C) Copyright 2022 - Analog Devices, Inc. * * Written and/or maintained by Timesys Corporation * * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> * Contact: Greg Malysa <greg.malysa@timesys.com> * * Based on Rockchip's sdhci.c file */ #include <clk.h> #include <dm.h> #include <malloc.h> #include <sdhci.h> #include <asm/cache.h> /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 400000 /* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */ #define ADMA_BOUNDARY_ALGN SZ_128M #define BOUNDARY_OK(addr, len) \ (((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \ (ADMA_BOUNDARY_ALGN - 1))) /* We split a descriptor for every crossing of the ADMA alignment boundary, * so we need an additional descriptor for every expected crossing. * As I understand it, the max expected transaction size is: * CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN * * With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a * clean power of two, we'd only ever need +1 descriptor as the first * descriptor that got split would then bring the remaining DMA * destination addresses into alignment. Unfortunately, it's currently * hardcoded to a non-power-of-two value. * * If that ever becomes parameterized, ADMA max length can be set to * 0x10000, and set this to 1. */ #define ADMA_POTENTIAL_CROSSINGS \ DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \ ADMA_BOUNDARY_ALGN) /* +1 descriptor for each crossing. */ #define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN) struct adi_sdhc_plat { struct mmc_config cfg; struct mmc mmc; }; void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, dma_addr_t addr, int len, bool end) { int tmplen, offset; if (likely(!len || BOUNDARY_OK(addr, len))) { sdhci_adma_write_desc(host, desc, addr, len, end); return; } offset = addr & (ADMA_BOUNDARY_ALGN - 1); tmplen = ADMA_BOUNDARY_ALGN - offset; sdhci_adma_write_desc(host, desc, addr, tmplen, false); addr += tmplen; len -= tmplen; sdhci_adma_write_desc(host, desc, addr, len, end); } struct sdhci_ops adi_dwcmshc_sdhci_ops = { .adma_write_desc = adi_dwcmshc_adma_write_desc, }; static int adi_dwcmshc_sdhci_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct adi_sdhc_plat *plat = dev_get_plat(dev); struct sdhci_host *host = dev_get_priv(dev); int max_frequency, ret; struct clk clk; max_frequency = dev_read_u32_default(dev, "max-frequency", 0); ret = clk_get_by_index(dev, 0, &clk); host->quirks = 0; host->max_clk = max_frequency; /* * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg * doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't * check for other bus-width values. */ if (host->bus_width == 8) host->host_caps |= MMC_MODE_8BIT; host->mmc = &plat->mmc; host->mmc->priv = host; host->mmc->dev = dev; upriv->mmc = host->mmc; host->ops = &adi_dwcmshc_sdhci_ops; host->adma_desc_table = memalign(ARCH_DMA_MINALIGN, ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ); host->adma_addr = virt_to_phys(host->adma_desc_table); ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ); if (ret) return ret; return sdhci_probe(dev); } static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev) { struct sdhci_host *host = dev_get_priv(dev); host->name = dev->name; host->ioaddr = dev_read_addr_ptr(dev); host->bus_width = dev_read_u32_default(dev, "bus-width", 4); return 0; } static int adi_sdhci_bind(struct udevice *dev) { struct adi_sdhc_plat *plat = dev_get_plat(dev); return sdhci_bind(dev, &plat->mmc, &plat->cfg); } static const struct udevice_id adi_dwcmshc_sdhci_ids[] = { { .compatible = "adi,dwc-sdhci" }, { } }; U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = { .name = "adi_sdhci", .id = UCLASS_MMC, .of_match = adi_dwcmshc_sdhci_ids, .of_to_plat = adi_dwcmshc_sdhci_of_to_plat, .ops = &sdhci_ops, .bind = adi_sdhci_bind, .probe = adi_dwcmshc_sdhci_probe, .priv_auto = sizeof(struct sdhci_host), .plat_auto = sizeof(struct adi_sdhc_plat), }; |