Skip site navigation (1)Skip section navigation (2)
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>