Page MenuHomeFreeBSD

D20156.id59956.diff
No OneTemporary

D20156.id59956.diff

Index: head/sys/dev/sdhci/sdhci_fdt.c
===================================================================
--- head/sys/dev/sdhci/sdhci_fdt.c
+++ head/sys/dev/sdhci/sdhci_fdt.c
@@ -52,6 +52,14 @@
#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 @@
#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 @@
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 @@
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 @@
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(). */

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 22, 12:13 AM (21 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15546487
Default Alt Text
D20156.id59956.diff (7 KB)

Event Timeline