diff --git a/sys/arm/mv/files.arm7 b/sys/arm/mv/files.arm7 --- a/sys/arm/mv/files.arm7 +++ b/sys/arm/mv/files.arm7 @@ -16,7 +16,7 @@ arm/mv/armada38x/armada38x_rtc.c standard arm/mv/armada38x/armada38x_pl310.c optional pl310 arm/mv/mv_spi.c optional mv_spi spibus -dev/sdhci/sdhci_fdt.c optional sdhci +dev/sdhci/sdhci_fdt_rockchip.c optional sdhci arm/mv/rtc.c standard arm/mv/armadaxp/armadaxp_mp.c optional smp diff --git a/sys/dev/sdhci/sdhci_fdt.h b/sys/dev/sdhci/sdhci_fdt.h new file mode 100644 --- /dev/null +++ b/sys/dev/sdhci/sdhci_fdt.h @@ -0,0 +1,59 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef _SDHCI_FDT_H_ +#define _SDHCI_FDT_H_ + +#define SDHCI_FDT_MAX_SLOTS 6 + +struct sdhci_fdt_softc { + device_t dev; /* Controller device */ + u_int quirks; /* Chip specific quirks */ + u_int caps; /* If we override SDHCI_CAPABILITIES */ + uint32_t max_clk; /* Max possible freq */ + uint8_t sdma_boundary; /* If we override the SDMA boundary */ + struct resource *irq_res; /* IRQ resource */ + void *intrhand; /* Interrupt handle */ + + int num_slots; /* Number of slots on this controller*/ + struct sdhci_slot slots[SDHCI_FDT_MAX_SLOTS]; + struct resource *mem_res[SDHCI_FDT_MAX_SLOTS]; /* Memory resource */ + + bool wp_inverted; /* WP pin is inverted */ + bool wp_disabled; /* WP pin is not supported */ + bool no_18v; /* No 1.8V support */ + + clk_t clk_xin; /* xin24m fixed clock */ + clk_t clk_ahb; /* ahb clock */ + clk_t clk_core; /* core clock */ + phy_t phy; /* phy to be used */ + + struct syscon *syscon; /* Handle to the syscon */ +}; + +int sdhci_fdt_attach(device_t dev); +int sdhci_fdt_detach(device_t dev); + +#endif diff --git a/sys/dev/sdhci/sdhci_fdt.c b/sys/dev/sdhci/sdhci_fdt.c --- a/sys/dev/sdhci/sdhci_fdt.c +++ b/sys/dev/sdhci/sdhci_fdt.c @@ -47,305 +47,37 @@ #include #include -#include -#include #include #include +#include + +#include +#include #include #include #include #include +#include #include "mmcbr_if.h" #include "sdhci_if.h" #include "opt_mmccam.h" -#include "clkdev_if.h" -#include "syscon_if.h" - -#define MAX_SLOTS 6 #define SDHCI_FDT_ARMADA38X 1 #define SDHCI_FDT_XLNX_ZY7 2 #define SDHCI_FDT_QUALCOMM 3 -#define SDHCI_FDT_RK3399 4 -#define SDHCI_FDT_RK3568 5 -#define SDHCI_FDT_XLNX_ZMP 6 - -#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 RK3568_EMMC_HOST_CTRL 0x0508 -#define RK3568_EMMC_EMMC_CTRL 0x052c -#define RK3568_EMMC_ATCTRL 0x0540 -#define RK3568_EMMC_DLL_CTRL 0x0800 -#define DLL_CTRL_SRST 0x00000001 -#define DLL_CTRL_START 0x00000002 -#define DLL_CTRL_START_POINT_DEFAULT 0x00050000 -#define DLL_CTRL_INCREMENT_DEFAULT 0x00000200 - -#define RK3568_EMMC_DLL_RXCLK 0x0804 -#define DLL_RXCLK_DELAY_ENABLE 0x08000000 -#define DLL_RXCLK_NO_INV 0x20000000 - -#define RK3568_EMMC_DLL_TXCLK 0x0808 -#define DLL_TXCLK_DELAY_ENABLE 0x08000000 -#define DLL_TXCLK_TAPNUM_DEFAULT 0x00000008 -#define DLL_TXCLK_TAPNUM_FROM_SW 0x01000000 - -#define RK3568_EMMC_DLL_STRBIN 0x080c -#define DLL_STRBIN_DELAY_ENABLE 0x08000000 -#define DLL_STRBIN_TAPNUM_DEFAULT 0x00000008 -#define DLL_STRBIN_TAPNUM_FROM_SW 0x01000000 - -#define RK3568_EMMC_DLL_STATUS0 0x0840 -#define DLL_STATUS0_DLL_LOCK 0x00000100 -#define DLL_STATUS0_DLL_TIMEOUT 0x00000200 - -#define LOWEST_SET_BIT(mask) ((((mask) - 1) & (mask)) ^ (mask)) -#define SHIFTIN(x, mask) ((x) * LOWEST_SET_BIT(mask)) static struct ofw_compat_data compat_data[] = { { "marvell,armada-380-sdhci", SDHCI_FDT_ARMADA38X }, { "qcom,sdhci-msm-v4", SDHCI_FDT_QUALCOMM }, - { "rockchip,rk3399-sdhci-5.1", SDHCI_FDT_RK3399 }, { "xlnx,zy7_sdhci", SDHCI_FDT_XLNX_ZY7 }, - { "rockchip,rk3568-dwcmshc", SDHCI_FDT_RK3568 }, - { "xlnx,zynqmp-8.9a", SDHCI_FDT_XLNX_ZMP }, { NULL, 0 } }; -struct sdhci_fdt_softc { - device_t dev; /* Controller device */ - u_int quirks; /* Chip specific quirks */ - u_int caps; /* If we override SDHCI_CAPABILITIES */ - uint32_t max_clk; /* Max possible freq */ - uint8_t sdma_boundary; /* If we override the SDMA boundary */ - struct resource *irq_res; /* IRQ resource */ - void *intrhand; /* Interrupt handle */ - - int num_slots; /* Number of slots on this controller*/ - struct sdhci_slot slots[MAX_SLOTS]; - struct resource *mem_res[MAX_SLOTS]; /* Memory resource */ - - bool wp_inverted; /* WP pin is inverted */ - bool wp_disabled; /* WP pin is not supported */ - bool no_18v; /* No 1.8V support */ - - clk_t clk_xin; /* xin24m fixed clock */ - clk_t clk_ahb; /* ahb clock */ - clk_t clk_core; /* core clock */ - phy_t phy; /* phy to be used */ - - struct syscon *syscon; /* Handle to the syscon */ -}; - -struct sdhci_exported_clocks_sc { - device_t clkdev; -}; - -static int -sdhci_exported_clocks_init(struct clknode *clk, device_t dev) -{ - - clknode_init_parent_idx(clk, 0); - return (0); -} - -static clknode_method_t sdhci_exported_clocks_clknode_methods[] = { - /* Device interface */ - CLKNODEMETHOD(clknode_init, sdhci_exported_clocks_init), - CLKNODEMETHOD_END -}; -DEFINE_CLASS_1(sdhci_exported_clocks_clknode, sdhci_exported_clocks_clknode_class, - sdhci_exported_clocks_clknode_methods, sizeof(struct sdhci_exported_clocks_sc), - clknode_class); - -static int -sdhci_clock_ofw_map(struct clkdom *clkdom, uint32_t ncells, - phandle_t *cells, struct clknode **clk) -{ - int id = 1; /* Our clock id starts at 1 */ - - if (ncells != 0) - id = cells[1]; - *clk = clknode_find_by_id(clkdom, id); - - if (*clk == NULL) - return (ENXIO); - return (0); -} - -static void -sdhci_export_clocks(struct sdhci_fdt_softc *sc) -{ - struct clknode_init_def def; - struct sdhci_exported_clocks_sc *clksc; - struct clkdom *clkdom; - struct clknode *clk; - bus_addr_t paddr; - bus_size_t psize; - const char **clknames; - phandle_t node; - int i, nclocks, ncells, error; - - node = ofw_bus_get_node(sc->dev); - - if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { - device_printf(sc->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(sc->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; - - clkdom = clkdom_create(sc->dev); - clkdom_set_ofw_mapper(clkdom, sdhci_clock_ofw_map); - - for (i = 0; i < nclocks; i++) { - memset(&def, 0, sizeof(def)); - def.id = i + 1; /* Exported clock IDs starts at 1 */ - def.name = clknames[i]; - def.parent_names = malloc(sizeof(char *) * 1, M_OFWPROP, M_WAITOK); - def.parent_names[0] = clk_get_name(sc->clk_xin); - def.parent_cnt = 1; - - clk = clknode_create(clkdom, &sdhci_exported_clocks_clknode_class, &def); - if (clk == NULL) { - device_printf(sc->dev, "cannot create clknode\n"); - return; - } - - clksc = clknode_get_softc(clk); - clksc->clkdev = device_get_parent(sc->dev); - - clknode_register(clkdom, clk); - } - - if (clkdom_finit(clkdom) != 0) { - device_printf(sc->dev, "cannot finalize clkdom initialization\n"); - return; - } - - if (bootverbose) - clkdom_dump(clkdom); -} - -static int -sdhci_init_clocks(device_t dev) -{ - struct sdhci_fdt_softc *sc = device_get_softc(dev); - 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_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); - } - - return (0); -} - -static int -sdhci_init_phy(struct sdhci_fdt_softc *sc) -{ - int error; - - /* Enable PHY */ - error = phy_get_by_ofw_name(sc->dev, 0, "phy_arasan", &sc->phy); - if (error == ENOENT) - return (0); - if (error != 0) { - device_printf(sc->dev, "Could not get phy\n"); - return (ENXIO); - } - error = phy_enable(sc->phy); - if (error != 0) { - device_printf(sc->dev, "Could not enable phy\n"); - return (ENXIO); - } - - return (0); -} - -static int -sdhci_get_syscon(struct sdhci_fdt_softc *sc) -{ - phandle_t node; - - /* Get syscon */ - node = ofw_bus_get_node(sc->dev); - if (OF_hasprop(node, "arasan,soc-ctl-syscon") && - syscon_get_by_ofw_property(sc->dev, node, - "arasan,soc-ctl-syscon", &sc->syscon) != 0) { - device_printf(sc->dev, "cannot get syscon handle\n"); - return (ENXIO); - } - - return (0); -} - -static int -sdhci_init_rk3399(device_t dev) -{ - struct sdhci_fdt_softc *sc = device_get_softc(dev); - uint64_t freq; - uint32_t mask, val; - int error; - - error = clk_get_freq(sc->clk_xin, &freq); - if (error != 0) { - device_printf(dev, "cannot get xin clock frequency\n"); - return (ENXIO); - } - - /* Disable clock multiplier */ - mask = RK3399_CORECFG_CLOCKMULTIPLIER; - val = 0; - SYSCON_WRITE_4(sc->syscon, 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(sc->syscon, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val); - - return (0); -} - static uint8_t sdhci_fdt_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { @@ -440,80 +172,15 @@ return (sdhci_generic_get_ro(bus, dev) ^ sc->wp_inverted); } -static int -sdhci_fdt_set_clock(device_t dev, struct sdhci_slot *slot, int clock) -{ - struct sdhci_fdt_softc *sc = device_get_softc(dev); - int32_t val; - int i; - - if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == - SDHCI_FDT_RK3568) { - if (clock == 400000) - clock = 375000; - - if (clock) { - clk_set_freq(sc->clk_core, clock, 0); - - if (clock <= 52000000) { - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_CTRL, 0x0); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_RXCLK, DLL_RXCLK_NO_INV); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_TXCLK, 0x0); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_STRBIN, 0x0); - return (clock); - } - - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_CTRL, DLL_CTRL_START); - DELAY(1000); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_CTRL, 0); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_CTRL, DLL_CTRL_START_POINT_DEFAULT | - DLL_CTRL_INCREMENT_DEFAULT | DLL_CTRL_START); - for (i = 0; i < 500; i++) { - val = bus_read_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_STATUS0); - if (val & DLL_STATUS0_DLL_LOCK && - !(val & DLL_STATUS0_DLL_TIMEOUT)) - break; - DELAY(1000); - } - bus_write_4(sc->mem_res[slot->num], RK3568_EMMC_ATCTRL, - (0x1 << 16 | 0x2 << 17 | 0x3 << 19)); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_RXCLK, - DLL_RXCLK_DELAY_ENABLE | DLL_RXCLK_NO_INV); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_TXCLK, DLL_TXCLK_DELAY_ENABLE | - DLL_TXCLK_TAPNUM_DEFAULT|DLL_TXCLK_TAPNUM_FROM_SW); - bus_write_4(sc->mem_res[slot->num], - RK3568_EMMC_DLL_STRBIN, DLL_STRBIN_DELAY_ENABLE | - DLL_STRBIN_TAPNUM_DEFAULT | - DLL_STRBIN_TAPNUM_FROM_SW); - } - } - return (clock); -} - static int sdhci_fdt_probe(device_t dev) { struct sdhci_fdt_softc *sc = device_get_softc(dev); - phandle_t node; - pcell_t cid; - - sc->quirks = 0; - sc->num_slots = 1; - sc->max_clk = 0; if (!ofw_bus_status_okay(dev)) return (ENXIO); + sc->quirks = 0; switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { case SDHCI_FDT_ARMADA38X: sc->quirks = SDHCI_QUIRK_BROKEN_AUTO_STOP; @@ -525,25 +192,33 @@ 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"); break; - case SDHCI_FDT_RK3568: - device_set_desc(dev, "Rockchip RK3568 fdt SDHCI controller"); - break; - case SDHCI_FDT_XLNX_ZMP: - device_set_desc(dev, "ZynqMP generic fdt SDHCI controller"); - break; default: return (ENXIO); } + return (0); +} + +int +sdhci_fdt_attach(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + struct sdhci_slot *slot; + int err, slots, rid, i; + phandle_t node; + pcell_t cid; + + sc->dev = dev; + node = ofw_bus_get_node(dev); + sc->num_slots = 1; + sc->max_clk = 0; + /* Allow dts to patch quirks, slots, and max-frequency. */ if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0) sc->quirks = cid; @@ -558,18 +233,6 @@ if (OF_hasprop(node, "disable-wp")) sc->wp_disabled = true; - return (0); -} - -static int -sdhci_fdt_attach(device_t dev) -{ - struct sdhci_fdt_softc *sc = device_get_softc(dev); - struct sdhci_slot *slot; - int err, slots, rid, i, compat; - - sc->dev = dev; - /* Allocate IRQ. */ rid = 0; sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, @@ -579,44 +242,6 @@ return (ENOMEM); } - compat = ofw_bus_search_compatible(dev, compat_data)->ocd_data; - switch (compat) { - case SDHCI_FDT_RK3399: - case SDHCI_FDT_XLNX_ZMP: - err = sdhci_init_clocks(dev); - if (err != 0) { - device_printf(dev, "Cannot init clocks\n"); - return (err); - } - sdhci_export_clocks(sc); - if ((err = sdhci_init_phy(sc)) != 0) { - device_printf(dev, "Cannot init phy\n"); - return (err); - } - if ((err = sdhci_get_syscon(sc)) != 0) { - device_printf(dev, "Cannot get syscon handle\n"); - return (err); - } - if (compat == SDHCI_FDT_RK3399) { - err = sdhci_init_rk3399(dev); - if (err != 0) { - device_printf(dev, "Cannot init RK3399 SDHCI\n"); - return (err); - } - } - break; - case SDHCI_FDT_RK3568: - /* setup & enable clocks */ - if (clk_get_by_ofw_name(dev, 0, "core", &sc->clk_core)) { - device_printf(dev, "cannot get core clock\n"); - return (ENXIO); - } - clk_enable(sc->clk_core); - break; - default: - break; - } - /* Scan all slots. */ slots = sc->num_slots; /* number of slots determined in probe(). */ sc->num_slots = 0; @@ -640,7 +265,6 @@ if (sdhci_init_slot(dev, slot, i) != 0) continue; - sc->num_slots++; } device_printf(dev, "%d slot(s) allocated\n", sc->num_slots); @@ -660,7 +284,7 @@ return (0); } -static int +int sdhci_fdt_detach(device_t dev) { struct sdhci_fdt_softc *sc = device_get_softc(dev); @@ -706,12 +330,11 @@ DEVMETHOD(sdhci_write_2, sdhci_fdt_write_2), DEVMETHOD(sdhci_write_4, sdhci_fdt_write_4), DEVMETHOD(sdhci_write_multi_4, sdhci_fdt_write_multi_4), - DEVMETHOD(sdhci_set_clock, sdhci_fdt_set_clock), DEVMETHOD_END }; -static driver_t sdhci_fdt_driver = { +driver_t sdhci_fdt_driver = { "sdhci_fdt", sdhci_fdt_methods, sizeof(struct sdhci_fdt_softc), diff --git a/sys/dev/sdhci/sdhci_fdt_rockchip.c b/sys/dev/sdhci/sdhci_fdt_rockchip.c new file mode 100644 --- /dev/null +++ b/sys/dev/sdhci/sdhci_fdt_rockchip.c @@ -0,0 +1,462 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2012 Thomas Skibo + * Copyright (c) 2008 Alexander Motin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "mmcbr_if.h" +#include "sdhci_if.h" + +#include "opt_mmccam.h" + +#include "clkdev_if.h" +#include "syscon_if.h" + +#define SDHCI_FDT_RK3399 1 +#define SDHCI_FDT_RK3568 2 +#define SDHCI_FDT_XLNX_ZMP 3 + +#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 RK3568_EMMC_HOST_CTRL 0x0508 +#define RK3568_EMMC_EMMC_CTRL 0x052c +#define RK3568_EMMC_ATCTRL 0x0540 +#define RK3568_EMMC_DLL_CTRL 0x0800 +#define DLL_CTRL_SRST 0x00000001 +#define DLL_CTRL_START 0x00000002 +#define DLL_CTRL_START_POINT_DEFAULT 0x00050000 +#define DLL_CTRL_INCREMENT_DEFAULT 0x00000200 + +#define RK3568_EMMC_DLL_RXCLK 0x0804 +#define DLL_RXCLK_DELAY_ENABLE 0x08000000 +#define DLL_RXCLK_NO_INV 0x20000000 + +#define RK3568_EMMC_DLL_TXCLK 0x0808 +#define DLL_TXCLK_DELAY_ENABLE 0x08000000 +#define DLL_TXCLK_TAPNUM_DEFAULT 0x00000008 +#define DLL_TXCLK_TAPNUM_FROM_SW 0x01000000 + +#define RK3568_EMMC_DLL_STRBIN 0x080c +#define DLL_STRBIN_DELAY_ENABLE 0x08000000 +#define DLL_STRBIN_TAPNUM_DEFAULT 0x00000008 +#define DLL_STRBIN_TAPNUM_FROM_SW 0x01000000 + +#define RK3568_EMMC_DLL_STATUS0 0x0840 +#define DLL_STATUS0_DLL_LOCK 0x00000100 +#define DLL_STATUS0_DLL_TIMEOUT 0x00000200 + +#define LOWEST_SET_BIT(mask) ((((mask) - 1) & (mask)) ^ (mask)) +#define SHIFTIN(x, mask) ((x) * LOWEST_SET_BIT(mask)) + +static struct ofw_compat_data compat_data[] = { + { "rockchip,rk3399-sdhci-5.1", SDHCI_FDT_RK3399 }, + { "rockchip,rk3568-dwcmshc", SDHCI_FDT_RK3568 }, + { "xlnx,zynqmp-8.9a", SDHCI_FDT_XLNX_ZMP }, + { NULL, 0 } +}; + +struct sdhci_exported_clocks_sc { + device_t clkdev; +}; + +static int +sdhci_exported_clocks_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static clknode_method_t sdhci_exported_clocks_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, sdhci_exported_clocks_init), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(sdhci_exported_clocks_clknode, sdhci_exported_clocks_clknode_class, + sdhci_exported_clocks_clknode_methods, sizeof(struct sdhci_exported_clocks_sc), + clknode_class); + +static int +sdhci_clock_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) +{ + int id = 1; /* Our clock id starts at 1 */ + + if (ncells != 0) + id = cells[1]; + *clk = clknode_find_by_id(clkdom, id); + + if (*clk == NULL) + return (ENXIO); + return (0); +} + + +static int +sdhci_fdt_rockchip_probe(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + + sc->quirks = 0; + switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + case SDHCI_FDT_RK3399: + device_set_desc(dev, "Rockchip RK3399 fdt SDHCI controller"); + break; + case SDHCI_FDT_RK3568: + device_set_desc(dev, "Rockchip RK3568 fdt SDHCI controller"); + break; + case SDHCI_FDT_XLNX_ZMP: + device_set_desc(dev, "ZynqMP generic fdt SDHCI controller"); + break; + default: + return (ENXIO); + } + + return (0); +} + +static void +sdhci_export_clocks(struct sdhci_fdt_softc *sc) +{ + struct clknode_init_def def; + struct sdhci_exported_clocks_sc *clksc; + struct clkdom *clkdom; + struct clknode *clk; + bus_addr_t paddr; + bus_size_t psize; + const char **clknames; + phandle_t node; + int i, nclocks, ncells, error; + + node = ofw_bus_get_node(sc->dev); + + if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { + device_printf(sc->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(sc->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; + + clkdom = clkdom_create(sc->dev); + clkdom_set_ofw_mapper(clkdom, sdhci_clock_ofw_map); + + for (i = 0; i < nclocks; i++) { + memset(&def, 0, sizeof(def)); + def.id = i + 1; /* Exported clock IDs starts at 1 */ + def.name = clknames[i]; + def.parent_names = malloc(sizeof(char *) * 1, M_OFWPROP, M_WAITOK); + def.parent_names[0] = clk_get_name(sc->clk_xin); + def.parent_cnt = 1; + + clk = clknode_create(clkdom, &sdhci_exported_clocks_clknode_class, &def); + if (clk == NULL) { + device_printf(sc->dev, "cannot create clknode\n"); + return; + } + + clksc = clknode_get_softc(clk); + clksc->clkdev = device_get_parent(sc->dev); + + clknode_register(clkdom, clk); + } + + if (clkdom_finit(clkdom) != 0) { + device_printf(sc->dev, "cannot finalize clkdom initialization\n"); + return; + } + + if (bootverbose) + clkdom_dump(clkdom); +} + +static int +sdhci_init_clocks(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + 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_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); + } + + return (0); +} + +static int +sdhci_init_phy(struct sdhci_fdt_softc *sc) +{ + int error; + + /* Enable PHY */ + error = phy_get_by_ofw_name(sc->dev, 0, "phy_arasan", &sc->phy); + if (error == ENOENT) + return (0); + if (error != 0) { + device_printf(sc->dev, "Could not get phy\n"); + return (ENXIO); + } + error = phy_enable(sc->phy); + if (error != 0) { + device_printf(sc->dev, "Could not enable phy\n"); + return (ENXIO); + } + + return (0); +} + +static int +sdhci_get_syscon(struct sdhci_fdt_softc *sc) +{ + phandle_t node; + + /* Get syscon */ + node = ofw_bus_get_node(sc->dev); + if (OF_hasprop(node, "arasan,soc-ctl-syscon") && + syscon_get_by_ofw_property(sc->dev, node, + "arasan,soc-ctl-syscon", &sc->syscon) != 0) { + device_printf(sc->dev, "cannot get syscon handle\n"); + return (ENXIO); + } + + return (0); +} + +static int +sdhci_init_rk3399(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + uint64_t freq; + uint32_t mask, val; + int error; + + error = clk_get_freq(sc->clk_xin, &freq); + if (error != 0) { + device_printf(dev, "cannot get xin clock frequency\n"); + return (ENXIO); + } + + /* Disable clock multiplier */ + mask = RK3399_CORECFG_CLOCKMULTIPLIER; + val = 0; + SYSCON_WRITE_4(sc->syscon, 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(sc->syscon, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val); + + return (0); +} + +static int +sdhci_fdt_set_clock(device_t dev, struct sdhci_slot *slot, int clock) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + int32_t val; + int i; + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == + SDHCI_FDT_RK3568) { + if (clock == 400000) + clock = 375000; + + if (clock) { + clk_set_freq(sc->clk_core, clock, 0); + + if (clock <= 52000000) { + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_CTRL, 0x0); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_RXCLK, DLL_RXCLK_NO_INV); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_TXCLK, 0x0); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_STRBIN, 0x0); + return (clock); + } + + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_CTRL, DLL_CTRL_START); + DELAY(1000); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_CTRL, 0); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_CTRL, DLL_CTRL_START_POINT_DEFAULT | + DLL_CTRL_INCREMENT_DEFAULT | DLL_CTRL_START); + for (i = 0; i < 500; i++) { + val = bus_read_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_STATUS0); + if (val & DLL_STATUS0_DLL_LOCK && + !(val & DLL_STATUS0_DLL_TIMEOUT)) + break; + DELAY(1000); + } + bus_write_4(sc->mem_res[slot->num], RK3568_EMMC_ATCTRL, + (0x1 << 16 | 0x2 << 17 | 0x3 << 19)); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_RXCLK, + DLL_RXCLK_DELAY_ENABLE | DLL_RXCLK_NO_INV); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_TXCLK, DLL_TXCLK_DELAY_ENABLE | + DLL_TXCLK_TAPNUM_DEFAULT|DLL_TXCLK_TAPNUM_FROM_SW); + bus_write_4(sc->mem_res[slot->num], + RK3568_EMMC_DLL_STRBIN, DLL_STRBIN_DELAY_ENABLE | + DLL_STRBIN_TAPNUM_DEFAULT | + DLL_STRBIN_TAPNUM_FROM_SW); + } + } + return (clock); +} + +static int +sdhci_fdt_rockchip_attach(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + int err, compat; + + compat = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (compat) { + case SDHCI_FDT_RK3399: + case SDHCI_FDT_XLNX_ZMP: + err = sdhci_init_clocks(dev); + if (err != 0) { + device_printf(dev, "Cannot init clocks\n"); + return (err); + } + sdhci_export_clocks(sc); + if ((err = sdhci_init_phy(sc)) != 0) { + device_printf(dev, "Cannot init phy\n"); + return (err); + } + if ((err = sdhci_get_syscon(sc)) != 0) { + device_printf(dev, "Cannot get syscon handle\n"); + return (err); + } + if (compat == SDHCI_FDT_RK3399) { + err = sdhci_init_rk3399(dev); + if (err != 0) { + device_printf(dev, "Cannot init RK3399 SDHCI\n"); + return (err); + } + } + break; + case SDHCI_FDT_RK3568: + /* setup & enable clocks */ + if (clk_get_by_ofw_name(dev, 0, "core", &sc->clk_core)) { + device_printf(dev, "cannot get core clock\n"); + return (ENXIO); + } + clk_enable(sc->clk_core); + break; + default: + break; + } + + return (sdhci_fdt_attach(dev)); +} + +static device_method_t sdhci_fdt_rockchip_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, sdhci_fdt_rockchip_probe), + DEVMETHOD(device_attach, sdhci_fdt_rockchip_attach), + + /* SDHCI methods */ + DEVMETHOD(sdhci_set_clock, sdhci_fdt_set_clock), + + DEVMETHOD_END +}; +extern driver_t sdhci_fdt_driver; + +DEFINE_CLASS_1(sdhci_rockchip, sdhci_fdt_rockchip_driver, sdhci_fdt_rockchip_methods, + sizeof(struct sdhci_fdt_softc), sdhci_fdt_driver); +DRIVER_MODULE(sdhci_rockchip, simplebus, sdhci_fdt_rockchip_driver, NULL, NULL);