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 58struct 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 73int cdsdhc_match(struct device *, void *, void*); 74void cdsdhc_attach(struct device *, struct device *, void *); 75void cdsdhc_bus_clock_pre(struct sdhc_softc *, int, int); 76 77const struct cfattach cdsdhc_ca = { 78 sizeof(struct cdsdhc_softc), cdsdhc_match, cdsdhc_attach 79}; 80 81struct cfdriver cdsdhc_cd = { 82 NULL, "cdsdhc", DV_DULL 83}; 84 85int 86cdsdhc_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 95void 96cdsdhc_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 137disable: 138 clock_disable_all(faa->fa_node); 139unmap: 140 bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size); 141} 142 143void 144cdsdhc_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