1 /* $OpenBSD: cdsdhc.c,v 1.2 2022/01/18 11:36:21 patrick Exp $ */ 2 3 /* 4 * Copyright (c) 2022 Visa Hankala 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Driver glue for Cadence SD/SDIO/eMMC host controller. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/conf.h> 26 #include <sys/device.h> 27 28 #include <machine/bus.h> 29 #include <machine/fdt.h> 30 31 #include <dev/ofw/fdt.h> 32 #include <dev/ofw/openfirm.h> 33 #include <dev/ofw/ofw_clock.h> 34 35 #include <dev/sdmmc/sdhcvar.h> 36 #include <dev/sdmmc/sdmmcvar.h> 37 38 /* Host Register Set */ 39 #define HRS06 0x0018 40 #define HRS06_ETR (0x1 << 15) 41 #define HRS06_ETV_MASK (0x3f << 8) 42 #define HRS06_ETV_SHIFT 8 43 #define HRS06_EMM_MASK (0x7 << 0) 44 #define HRS06_EMM_SD (0x0 << 0) 45 #define HRS06_EMM_MMC_SDR (0x2 << 0) 46 #define HRS06_EMM_MMC_DDR (0x3 << 0) 47 #define HRS06_EMM_MMC_HS200 (0x4 << 0) 48 #define HRS06_EMM_MMC_HS400 (0x5 << 0) 49 #define HRS06_EMM_MMC_HS400_ENH (0x6 << 0) 50 #define HRS31 0x007c 51 #define HRS31_HOSTCTLVER(x) (((x) >> 16) & 0xfff) 52 #define HRS31_HOSTFIXVER(x) ((x) & 0xff) 53 54 /* Slot Register Set */ 55 #define SRS_OFFSET 0x200 56 #define SRS_SIZE 0x100 57 58 struct cdsdhc_softc { 59 struct sdhc_softc sc_sdhc; 60 bus_space_tag_t sc_iot; 61 bus_space_handle_t sc_ioh; 62 bus_space_handle_t sc_srs_ioh; 63 void *sc_ih; 64 65 struct sdhc_host *sc_host; 66 }; 67 68 #define HREAD4(sc, reg) \ 69 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 70 #define HWRITE4(sc, reg, val) \ 71 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 72 73 int cdsdhc_match(struct device *, void *, void*); 74 void cdsdhc_attach(struct device *, struct device *, void *); 75 void cdsdhc_bus_clock_pre(struct sdhc_softc *, int, int); 76 77 const struct cfattach cdsdhc_ca = { 78 sizeof(struct cdsdhc_softc), cdsdhc_match, cdsdhc_attach 79 }; 80 81 struct cfdriver cdsdhc_cd = { 82 NULL, "cdsdhc", DV_DULL 83 }; 84 85 int 86 cdsdhc_match(struct device *parent, void *match, void *aux) 87 { 88 struct fdt_attach_args *faa = aux; 89 90 if (faa->fa_nreg < 1) 91 return 0; 92 return OF_is_compatible(faa->fa_node, "cdns,sd4hc"); 93 } 94 95 void 96 cdsdhc_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct fdt_attach_args *faa = aux; 99 struct cdsdhc_softc *sc = (struct cdsdhc_softc *)self; 100 uint64_t capmask = 0, capset = 0; 101 uint32_t ver; 102 103 sc->sc_iot = faa->fa_iot; 104 105 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 106 0, &sc->sc_ioh) != 0) { 107 printf(": can't map registers\n"); 108 return; 109 } 110 111 if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, SRS_OFFSET, SRS_SIZE, 112 &sc->sc_srs_ioh) != 0) { 113 printf(": can't map SRS subregion\n"); 114 goto unmap; 115 } 116 117 clock_enable_all(faa->fa_node); 118 119 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, 120 sdhc_intr, sc, sc->sc_sdhc.sc_dev.dv_xname); 121 if (sc->sc_ih == NULL) { 122 printf(": can't establish interrupt\n"); 123 goto disable; 124 } 125 126 ver = HREAD4(sc, HRS31); 127 printf(": rev 0x%x/0x%x\n", HRS31_HOSTCTLVER(ver), 128 HRS31_HOSTFIXVER(ver)); 129 130 sc->sc_sdhc.sc_host = &sc->sc_host; 131 sc->sc_sdhc.sc_dmat = faa->fa_dmat; 132 sc->sc_sdhc.sc_bus_clock_pre = cdsdhc_bus_clock_pre; 133 sdhc_host_found(&sc->sc_sdhc, sc->sc_iot, sc->sc_srs_ioh, SRS_SIZE, 134 1, capmask, capset); 135 return; 136 137 disable: 138 clock_disable_all(faa->fa_node); 139 unmap: 140 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 141 } 142 143 void 144 cdsdhc_bus_clock_pre(struct sdhc_softc *sc_sdhc, int freq, int timing) 145 { 146 struct cdsdhc_softc *sc = (struct cdsdhc_softc *)sc_sdhc; 147 uint32_t mode, val; 148 149 switch (timing) { 150 case SDMMC_TIMING_HIGHSPEED: 151 mode = HRS06_EMM_MMC_SDR; 152 break; 153 case SDMMC_TIMING_MMC_DDR52: 154 mode = HRS06_EMM_MMC_DDR; 155 break; 156 case SDMMC_TIMING_MMC_HS200: 157 mode = HRS06_EMM_MMC_HS200; 158 break; 159 default: 160 mode = HRS06_EMM_SD; 161 break; 162 } 163 164 val = HREAD4(sc, HRS06); 165 val &= ~HRS06_EMM_MASK; 166 val |= mode; 167 HWRITE4(sc, HRS06, val); 168 } 169