Module Name: src Committed By: jmcneill Date: Wed Mar 13 12:17:45 UTC 2019
Modified Files: src/sys/dev/fdt: files.fdt Added Files: src/sys/dev/fdt: arasan_sdhc_fdt.c Log Message: Add support for Arasan SDHCI 5.1 To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/arasan_sdhc_fdt.c cvs rdiff -u -r1.43 -r1.44 src/sys/dev/fdt/files.fdt Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/files.fdt diff -u src/sys/dev/fdt/files.fdt:1.43 src/sys/dev/fdt/files.fdt:1.44 --- src/sys/dev/fdt/files.fdt:1.43 Sun Mar 3 12:54:07 2019 +++ src/sys/dev/fdt/files.fdt Wed Mar 13 12:17:45 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.fdt,v 1.43 2019/03/03 12:54:07 jmcneill Exp $ +# $NetBSD: files.fdt,v 1.44 2019/03/13 12:17:45 jmcneill Exp $ include "external/bsd/libfdt/conf/files.libfdt" @@ -144,3 +144,7 @@ file dev/fdt/dwiic_fdt.c dwiic_fdt # AMD Cryptographic Coprocessor attach amdccp at fdt with amdccp_fdt file dev/fdt/amdccp_fdt.c amdccp_fdt + +# Arasan SDHCI controller +attach sdhc at fdt with arasan_sdhc_fdt +file dev/fdt/arasan_sdhc_fdt.c arasan_sdhc_fdt Added files: Index: src/sys/dev/fdt/arasan_sdhc_fdt.c diff -u /dev/null src/sys/dev/fdt/arasan_sdhc_fdt.c:1.1 --- /dev/null Wed Mar 13 12:17:45 2019 +++ src/sys/dev/fdt/arasan_sdhc_fdt.c Wed Mar 13 12:17:45 2019 @@ -0,0 +1,306 @@ +/* $NetBSD: arasan_sdhc_fdt.c,v 1.1 2019/03/13 12:17:45 jmcneill Exp $ */ + +/*- + * Copyright (c) 2019 Jared McNeill <jmcne...@invisible.ca> + * 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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: arasan_sdhc_fdt.c,v 1.1 2019/03/13 12:17:45 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/sysctl.h> +#include <sys/kmem.h> + +#include <dev/sdmmc/sdhcreg.h> +#include <dev/sdmmc/sdhcvar.h> +#include <dev/sdmmc/sdmmcvar.h> + +#include <dev/clk/clk_backend.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/syscon.h> + +#define RK3399_GRF_EMMCCORE_CON0 0xf000 +#define RK3399_CORECFG_BASECLKFREQ __BITS(15,8) +#define RK3399_CORECFG_TIMEOUTCLKUNIT __BIT(7) +#define RK3399_CORECFG_TUNINGCOUNT __BITS(5,0) +#define RK3399_GRF_EMMCCORE_CON11 0xf02c +#define RK3399_CORECFG_CLOCKMULTIPLIER __BITS(7,0) + +enum arasan_sdhc_type { + AS_TYPE_RK3399 = 1, +}; + +struct arasan_sdhc_softc { + struct sdhc_softc sc_base; + struct sdhc_host *sc_host[1]; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_size_t sc_bsz; + int sc_phandle; + struct fdtbus_phy *sc_phy; + struct syscon *sc_syscon; + struct clk *sc_clk_xin; + struct clk *sc_clk_ahb; + enum arasan_sdhc_type sc_type; + struct clk_domain sc_clkdom; + struct clk sc_clk_card; +}; + +static const struct of_compat_data compat_data[] = { + { "rockchip,rk3399-sdhci-5.1", AS_TYPE_RK3399 }, + { NULL } +}; + +static struct clk * +arasan_sdhc_clk_decode(device_t dev, int cc_phandle, const void *data, size_t len) +{ + struct arasan_sdhc_softc * const sc = device_private(dev); + + if (len != 0) + return NULL; + + return &sc->sc_clk_card; +} + +static const struct fdtbus_clock_controller_func arasan_sdhc_fdt_clk_funcs = { + .decode = arasan_sdhc_clk_decode, +}; + +static struct clk * +arasan_sdhc_clk_get(void *priv, const char *name) +{ + struct arasan_sdhc_softc * const sc = priv; + + if (strcmp(name, sc->sc_clk_card.name) != 0) + return NULL; + + return &sc->sc_clk_card; +} + +static u_int +arasan_sdhc_clk_get_rate(void *priv, struct clk *clk) +{ + struct arasan_sdhc_softc * const sc = priv; + + return clk_get_rate(sc->sc_clk_xin); +} + +static const struct clk_funcs arasan_sdhc_clk_funcs = { + .get = arasan_sdhc_clk_get, + .get_rate = arasan_sdhc_clk_get_rate, +}; + +static int +arasan_sdhc_signal_voltage(struct sdhc_softc *sdhc, int signal_voltage) +{ + if (signal_voltage == SDMMC_SIGNAL_VOLTAGE_180) + return 0; + + return EINVAL; +} + +static int +arasan_sdhc_bus_clock_pre(struct sdhc_softc *sdhc, int freq) +{ + struct arasan_sdhc_softc * const sc = device_private(sdhc->sc_dev); + int error; + + if (sc->sc_phy != NULL) { + error = fdtbus_phy_enable(sc->sc_phy, false); + if (error != 0) + return error; + } + + return 0; +} + +static int +arasan_sdhc_bus_clock_post(struct sdhc_softc *sdhc, int freq) +{ + struct arasan_sdhc_softc * const sc = device_private(sdhc->sc_dev); + int error; + + if (sc->sc_phy != NULL) { + error = fdtbus_phy_enable(sc->sc_phy, true); + if (error != 0) + return error; + } + + return 0; +} + +static void +arasan_sdhc_init_rk3399(struct arasan_sdhc_softc *sc) +{ + uint32_t mask, val; + + if (sc->sc_syscon == NULL) + return; + + syscon_lock(sc->sc_syscon); + + /* Disable clock multiplier */ + mask = RK3399_CORECFG_CLOCKMULTIPLIER; + val = 0; + syscon_write_4(sc->sc_syscon, RK3399_GRF_EMMCCORE_CON11, (mask << 16) | val); + + /* Set base clock frequency */ + const u_int xin_rate = clk_get_rate(sc->sc_clk_xin); + mask = RK3399_CORECFG_BASECLKFREQ; + val = __SHIFTIN((xin_rate + (1000000 / 2)) / 1000000, RK3399_CORECFG_BASECLKFREQ); + syscon_write_4(sc->sc_syscon, RK3399_GRF_EMMCCORE_CON0, (mask << 16) | val); + + syscon_unlock(sc->sc_syscon); +} + +static void +arasan_sdhc_init(device_t dev) +{ + struct arasan_sdhc_softc * const sc = device_private(dev); + const char * sdhci_5_1_compat[] = { "arasan,sdhci-5.1", NULL }; + int error; + + if (sc->sc_type == AS_TYPE_RK3399) + arasan_sdhc_init_rk3399(sc); + + if (of_match_compatible(sc->sc_phandle, sdhci_5_1_compat)) { + sc->sc_phy = fdtbus_phy_get(sc->sc_phandle, "phy_arasan"); + if (sc->sc_phy == NULL) { + aprint_error_dev(dev, "couldn't get PHY\n"); + return; + } + sc->sc_base.sc_vendor_signal_voltage = arasan_sdhc_signal_voltage; + } + + error = sdhc_host_found(&sc->sc_base, sc->sc_bst, sc->sc_bsh, sc->sc_bsz); + if (error != 0) { + aprint_error_dev(dev, "couldn't initialize host, error = %d\n", error); + return; + } +} + +static int +arasan_sdhc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_match_compat_data(faa->faa_phandle, compat_data); +} + +static void +arasan_sdhc_attach(device_t parent, device_t self, void *aux) +{ + struct arasan_sdhc_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + char intrstr[128]; + const char *clkname; + bus_addr_t addr; + bus_size_t size; + u_int bus_width; + void *ih; + + fdtbus_clock_assign(phandle); + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": couldn't decode interrupt\n"); + return; + } + + sc->sc_clk_xin = fdtbus_clock_get(phandle, "clk_xin"); + sc->sc_clk_ahb = fdtbus_clock_get(phandle, "clk_ahb"); + if (sc->sc_clk_xin == NULL || sc->sc_clk_ahb == NULL) { + aprint_error(": couldn't get clocks\n"); + return; + } + if (clk_enable(sc->sc_clk_xin) != 0 || clk_enable(sc->sc_clk_ahb) != 0) { + aprint_error(": couldn't enable clocks\n"); + return; + } + + sc->sc_syscon = fdtbus_syscon_acquire(phandle, "arasan,soc-ctl-syscon"); + + if (of_getprop_uint32(phandle, "bus-width", &bus_width) != 0) + bus_width = 4; + + sc->sc_phandle = phandle; + sc->sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + sc->sc_bsz = size; + sc->sc_type = of_search_compatible(phandle, compat_data)->data; + + sc->sc_base.sc_dev = self; + sc->sc_base.sc_dmat = faa->faa_dmat; + sc->sc_base.sc_host = sc->sc_host; + sc->sc_base.sc_flags = SDHC_FLAG_NO_CLKBASE | + SDHC_FLAG_USE_DMA | + SDHC_FLAG_USE_ADMA2 | + SDHC_FLAG_STOP_WITH_TC; + if (bus_width == 8) + sc->sc_base.sc_flags |= SDHC_FLAG_8BIT_MODE; + sc->sc_base.sc_clkbase = clk_get_rate(sc->sc_clk_xin) / 1000; + sc->sc_base.sc_vendor_bus_clock = arasan_sdhc_bus_clock_pre; + sc->sc_base.sc_vendor_bus_clock_post = arasan_sdhc_bus_clock_post; + + aprint_naive("\n"); + aprint_normal(": Arasan SDHCI controller\n"); + + clkname = fdtbus_get_string(phandle, "clock-output-names"); + if (clkname == NULL) + clkname = faa->faa_name; + + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &arasan_sdhc_clk_funcs; + sc->sc_clkdom.priv = sc; + sc->sc_clk_card.domain = &sc->sc_clkdom; + sc->sc_clk_card.name = kmem_asprintf("%s", clkname); + clk_attach(&sc->sc_clk_card); + + fdtbus_register_clock_controller(self, phandle, &arasan_sdhc_fdt_clk_funcs); + + ih = fdtbus_intr_establish(phandle, 0, IPL_SDMMC, 0, sdhc_intr, &sc->sc_base); + if (ih == NULL) { + aprint_error_dev(self, "couldn't establish interrupt on %s\n", intrstr); + return; + } + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + + arasan_sdhc_init(self); +} + +CFATTACH_DECL_NEW(arasan_sdhc_fdt, sizeof(struct arasan_sdhc_softc), + arasan_sdhc_match, arasan_sdhc_attach, NULL, NULL);