Date: Sat, 20 Jul 2019 02:53:06 +0000 (UTC) From: Ganbold Tsagaankhuu <ganbold@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r350162 - head/sys/dev/sdhci Message-ID: <201907200253.x6K2r64J070385@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ganbold Date: Sat Jul 20 02:53:06 2019 New Revision: 350162 URL: https://svnweb.freebsd.org/changeset/base/350162 Log: Add emmc support for Rockchip RK3399 SoC. Tested on NanoPC-T4 board. Reviewed by: manu Differential Revision: https://reviews.freebsd.org/D20156 Modified: head/sys/dev/sdhci/sdhci_fdt.c Modified: head/sys/dev/sdhci/sdhci_fdt.c ============================================================================== --- head/sys/dev/sdhci/sdhci_fdt.c Sat Jul 20 02:03:31 2019 (r350161) +++ head/sys/dev/sdhci/sdhci_fdt.c Sat Jul 20 02:53:06 2019 (r350162) @@ -52,6 +52,14 @@ __FBSDID("$FreeBSD$"); #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#ifdef EXT_RESOURCES +#include <dev/ofw/ofw_subr.h> +#include <dev/extres/clk/clk.h> +#include <dev/extres/clk/clk_fixed.h> +#include <dev/extres/syscon/syscon.h> +#include <dev/extres/phy/phy.h> +#endif + #include <dev/mmc/bridge.h> #include <dev/sdhci/sdhci.h> @@ -61,16 +69,37 @@ __FBSDID("$FreeBSD$"); #include "opt_mmccam.h" +#ifdef EXT_RESOURCES +#include "clkdev_if.h" +#include "syscon_if.h" +#endif + #define MAX_SLOTS 6 #define SDHCI_FDT_ARMADA38X 1 #define SDHCI_FDT_GENERIC 2 #define SDHCI_FDT_XLNX_ZY7 3 #define SDHCI_FDT_QUALCOMM 4 +#define SDHCI_FDT_RK3399 5 +#ifdef EXT_RESOURCES +#define RK3399_GRF_EMMCCORE_CON0 0xf000 +#define RK3399_CORECFG_BASECLKFREQ 0xff00 +#define RK3399_CORECFG_TIMEOUTCLKUNIT (1 << 7) +#define RK3399_CORECFG_TUNINGCOUNT 0x3f +#define RK3399_GRF_EMMCCORE_CON11 0xf02c +#define RK3399_CORECFG_CLOCKMULTIPLIER 0xff + +#define LOWEST_SET_BIT(mask) ((((mask) - 1) & (mask)) ^ (mask)) +#define SHIFTIN(x, mask) ((x) * LOWEST_SET_BIT(mask)) + +#define EMMCCARDCLK_ID 1000 +#endif + static struct ofw_compat_data compat_data[] = { { "marvell,armada-380-sdhci", SDHCI_FDT_ARMADA38X }, { "sdhci_generic", SDHCI_FDT_GENERIC }, { "qcom,sdhci-msm-v4", SDHCI_FDT_QUALCOMM }, + { "rockchip,rk3399-sdhci-5.1", SDHCI_FDT_RK3399 }, { "xlnx,zy7_sdhci", SDHCI_FDT_XLNX_ZY7 }, { NULL, 0 } }; @@ -90,8 +119,209 @@ struct sdhci_fdt_softc { bool wp_inverted; /* WP pin is inverted */ bool no_18v; /* No 1.8V support */ + +#ifdef EXT_RESOURCES + clk_t clk_xin; /* xin24m fixed clock */ + clk_t clk_ahb; /* ahb clock */ + phy_t phy; /* phy to be used */ +#endif }; +#ifdef EXT_RESOURCES +struct rk3399_emmccardclk_sc { + device_t clkdev; + bus_addr_t reg; +}; + +static int +rk3399_emmccardclk_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static clknode_method_t rk3399_emmccardclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk3399_emmccardclk_init), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(rk3399_emmccardclk_clknode, rk3399_emmccardclk_clknode_class, + rk3399_emmccardclk_clknode_methods, sizeof(struct rk3399_emmccardclk_sc), + clknode_class); + +static int +rk3399_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) +{ + + if (ncells == 0) + *clk = clknode_find_by_id(clkdom, EMMCCARDCLK_ID); + else + return (ERANGE); + + if (*clk == NULL) + return (ENXIO); + return (0); +} + +static void +sdhci_init_rk3399_emmccardclk(device_t dev) +{ + struct clknode_init_def def; + struct rk3399_emmccardclk_sc *sc; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + bus_addr_t paddr; + bus_size_t psize; + const char **clknames; + phandle_t node; + int i, nclocks, ncells, error; + + node = ofw_bus_get_node(dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(dev, "cannot parse 'reg' property\n"); + return; + } + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0 || ncells != 2) { + device_printf(dev, "couldn't find parent clocks\n"); + return; + } + + nclocks = ofw_bus_string_list_to_array(node, "clock-output-names", + &clknames); + /* No clocks to export */ + if (nclocks <= 0) + return; + + if (nclocks != 1) { + device_printf(dev, "Having %d clock instead of 1, aborting\n", + nclocks); + return; + } + + clkdom = clkdom_create(dev); + clkdom_set_ofw_mapper(clkdom, rk3399_ofw_map); + + memset(&def, 0, sizeof(def)); + def.id = EMMCCARDCLK_ID; + def.name = clknames[0]; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(dev, "cannot get clock %d\n", error); + return; + } + def.parent_names[i] = clk_get_name(clk_parent); + if (bootverbose) + device_printf(dev, "clk parent: %s\n", + def.parent_names[i]); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + clk = clknode_create(clkdom, &rk3399_emmccardclk_clknode_class, &def); + if (clk == NULL) { + device_printf(dev, "cannot create clknode\n"); + return; + } + + sc = clknode_get_softc(clk); + sc->reg = paddr; + sc->clkdev = device_get_parent(dev); + + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(dev, "cannot finalize clkdom initialization\n"); + return; + } + + if (bootverbose) + clkdom_dump(clkdom); +} + +static int +sdhci_init_rk3399(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + struct syscon *grf = NULL; + phandle_t node; + uint64_t freq; + uint32_t mask, val; + int error; + + /* Get and activate clocks */ + error = clk_get_by_ofw_name(dev, 0, "clk_xin", &sc->clk_xin); + if (error != 0) { + device_printf(dev, "cannot get xin clock\n"); + return (ENXIO); + } + error = clk_enable(sc->clk_xin); + if (error != 0) { + device_printf(dev, "cannot enable xin clock\n"); + return (ENXIO); + } + error = clk_get_freq(sc->clk_xin, &freq); + if (error != 0) { + device_printf(dev, "cannot get xin clock frequency\n"); + return (ENXIO); + } + error = clk_get_by_ofw_name(dev, 0, "clk_ahb", &sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot get ahb clock\n"); + return (ENXIO); + } + error = clk_enable(sc->clk_ahb); + if (error != 0) { + device_printf(dev, "cannot enable ahb clock\n"); + return (ENXIO); + } + + /* Register clock */ + sdhci_init_rk3399_emmccardclk(dev); + + /* Enable PHY */ + error = phy_get_by_ofw_name(dev, 0, "phy_arasan", &sc->phy); + if (error != 0) { + device_printf(dev, "Could not get phy\n"); + return (ENXIO); + } + error = phy_enable(sc->phy); + if (error != 0) { + device_printf(dev, "Could not enable phy\n"); + return (ENXIO); + } + /* Get syscon */ + node = ofw_bus_get_node(dev); + if (OF_hasprop(node, "arasan,soc-ctl-syscon") && + syscon_get_by_ofw_property(dev, node, + "arasan,soc-ctl-syscon", &grf) != 0) { + device_printf(dev, "cannot get grf driver handle\n"); + return (ENXIO); + } + + /* Disable clock multiplier */ + mask = RK3399_CORECFG_CLOCKMULTIPLIER; + val = 0; + SYSCON_WRITE_4(grf, RK3399_GRF_EMMCCORE_CON11, (mask << 16) | val); + + /* Set base clock frequency */ + mask = RK3399_CORECFG_BASECLKFREQ; + val = SHIFTIN((freq + (1000000 / 2)) / 1000000, + RK3399_CORECFG_BASECLKFREQ); + SYSCON_WRITE_4(grf, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val); + + return (0); +} +#endif + static uint8_t sdhci_fdt_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { @@ -212,6 +442,9 @@ sdhci_fdt_probe(device_t dev) sc->sdma_boundary = SDHCI_BLKSZ_SDMA_BNDRY_4K; device_set_desc(dev, "Qualcomm FDT SDHCI controller"); break; + case SDHCI_FDT_RK3399: + device_set_desc(dev, "Rockchip RK3399 fdt SDHCI controller"); + break; case SDHCI_FDT_XLNX_ZY7: sc->quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; device_set_desc(dev, "Zynq-7000 generic fdt SDHCI controller"); @@ -254,6 +487,18 @@ sdhci_fdt_attach(device_t dev) device_printf(dev, "Can't allocate IRQ\n"); return (ENOMEM); } + +#ifdef EXT_RESOURCES + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == + SDHCI_FDT_RK3399) { + /* Initialize SDHCI */ + err = sdhci_init_rk3399(dev); + if (err != 0) { + device_printf(dev, "Cannot init RK3399 SDHCI\n"); + return (err); + } + } +#endif /* Scan all slots. */ slots = sc->num_slots; /* number of slots determined in probe(). */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201907200253.x6K2r64J070385>