/* $OpenBSD: rkemmcphy.c,v 1.2 2020/01/11 10:32:16 kettenis Exp $ */ /* * Copyright (c) 2019 Patrick Wildt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include /* Registers */ #define GRF_EMMCPHY_CON0 0x00 #define GRF_EMMCPHY_CON0_OTAPDLYSEL_4 (0x4 << 7) #define GRF_EMMCPHY_CON0_OTAPDLYENA_EN (1 << 11) #define GRF_EMMCPHY_CON0_FREQSEL_200M (0x0 << 12) #define GRF_EMMCPHY_CON0_FREQSEL_50M (0x1 << 12) #define GRF_EMMCPHY_CON0_FREQSEL_100M (0x2 << 12) #define GRF_EMMCPHY_CON0_FREQSEL_150M (0x3 << 12) #define GRF_EMMCPHY_CON0_OTAPDLYSEL_CLR (0xf << 23) #define GRF_EMMCPHY_CON0_OTAPDLYENA_CLR (1 << 27) #define GRF_EMMCPHY_CON0_FREQSEL_CLR (0x3 << 28) #define GRF_EMMCPHY_CON1 0x04 #define GRF_EMMCPHY_CON2 0x08 #define GRF_EMMCPHY_CON3 0x0c #define GRF_EMMCPHY_CON4 0x10 #define GRF_EMMCPHY_CON5 0x14 #define GRF_EMMCPHY_CON6 0x18 #define GRF_EMMCPHY_CON6_PDB_OFF (0 << 0) #define GRF_EMMCPHY_CON6_PDB_ON (1 << 0) #define GRF_EMMCPHY_CON6_ENDLL_OFF (0 << 1) #define GRF_EMMCPHY_CON6_ENDLL_ON (1 << 1) #define GRF_EMMCPHY_CON6_DR_50OHM (0x0 << 4) #define GRF_EMMCPHY_CON6_DR_33OHM (0x1 << 4) #define GRF_EMMCPHY_CON6_DR_66OHM (0x2 << 4) #define GRF_EMMCPHY_CON6_DR_100OHM (0x3 << 4) #define GRF_EMMCPHY_CON6_DR_40OHM (0x4 << 4) #define GRF_EMMCPHY_CON6_PDB_CLR (1 << 16) #define GRF_EMMCPHY_CON6_ENDLL_CLR (1 << 17) #define GRF_EMMCPHY_CON6_DR_CLR (0x7 << 20) #define GRF_EMMCPHY_STATUS 0x20 #define GRF_EMMCPHY_STATUS_DLLRDY (1 << 5) #define GRF_EMMCPHY_STATUS_CALDONE (1 << 6) #define HREAD4(sc, reg) \ (regmap_read_4((sc)->sc_rm, (sc)->sc_off + (reg))) #define HWRITE4(sc, reg, val) \ regmap_write_4((sc)->sc_rm, (sc)->sc_off + (reg), (val)) struct rkemmcphy_softc { struct device sc_dev; struct regmap *sc_rm; bus_size_t sc_off; struct phy_device sc_pd; }; int rkemmcphy_match(struct device *, void *, void *); void rkemmcphy_attach(struct device *, struct device *, void *); struct cfattach rkemmcphy_ca = { sizeof (struct rkemmcphy_softc), rkemmcphy_match, rkemmcphy_attach }; struct cfdriver rkemmcphy_cd = { NULL, "rkemmcphy", DV_DULL }; int rkemmcphy_enable(void *, uint32_t *); int rkemmcphy_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; return OF_is_compatible(faa->fa_node, "rockchip,rk3399-emmc-phy"); } void rkemmcphy_attach(struct device *parent, struct device *self, void *aux) { struct rkemmcphy_softc *sc = (struct rkemmcphy_softc *)self; struct fdt_attach_args *faa = aux; if (faa->fa_nreg < 1) { printf(": no registers\n"); return; } sc->sc_off = faa->fa_reg[0].addr; sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node)); if (sc->sc_rm == NULL) { printf(": can't map registers\n"); return; } printf("\n"); sc->sc_pd.pd_node = faa->fa_node; sc->sc_pd.pd_cookie = sc; sc->sc_pd.pd_enable = rkemmcphy_enable; phy_register(&sc->sc_pd); } int rkemmcphy_enable(void *cookie, uint32_t *cells) { struct rkemmcphy_softc *sc = cookie; uint32_t impedance, freqsel, freq, reg; int node = sc->sc_pd.pd_node; int i; impedance = OF_getpropint(node, "drive-impedance-ohm", 0); freq = clock_get_frequency(node, "emmcclk"); switch (impedance) { case 100: impedance = GRF_EMMCPHY_CON6_DR_100OHM; break; case 66: impedance = GRF_EMMCPHY_CON6_DR_66OHM; break; case 50: impedance = GRF_EMMCPHY_CON6_DR_50OHM; break; case 40: impedance = GRF_EMMCPHY_CON6_DR_40OHM; break; case 33: impedance = GRF_EMMCPHY_CON6_DR_33OHM; break; default: impedance = GRF_EMMCPHY_CON6_DR_50OHM; break; } if (freq == 0) { freqsel = GRF_EMMCPHY_CON0_FREQSEL_200M; } else if (freq < 75000000) { freqsel = GRF_EMMCPHY_CON0_FREQSEL_50M; } else if (freq < 125000000) { freqsel = GRF_EMMCPHY_CON0_FREQSEL_100M; } else if (freq < 175000000) { freqsel = GRF_EMMCPHY_CON0_FREQSEL_150M; } else { freqsel = GRF_EMMCPHY_CON0_FREQSEL_200M; } HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_DR_CLR | impedance); HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_PDB_CLR | GRF_EMMCPHY_CON6_PDB_OFF | GRF_EMMCPHY_CON6_ENDLL_CLR | GRF_EMMCPHY_CON6_ENDLL_OFF); delay(3); HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_PDB_CLR | GRF_EMMCPHY_CON6_PDB_ON); for (i = 5; i > 0; i--) { reg = HREAD4(sc, GRF_EMMCPHY_STATUS); if (reg & GRF_EMMCPHY_STATUS_CALDONE) break; delay(10); } if (i == 0) printf("%s: timeout\n", sc->sc_dev.dv_xname); HWRITE4(sc, GRF_EMMCPHY_CON0, GRF_EMMCPHY_CON0_FREQSEL_CLR | freqsel); HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_ENDLL_CLR | GRF_EMMCPHY_CON6_ENDLL_ON); if (freq != 0) { for (i = 5; i > 0; i--) { reg = HREAD4(sc, GRF_EMMCPHY_STATUS); if (reg & GRF_EMMCPHY_STATUS_DLLRDY) break; delay(10 * 1000); } if (i == 0) printf("%s: timeout\n", sc->sc_dev.dv_xname); } return 0; }