Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115790597
D48527.id149562.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
D48527.id149562.diff
View Options
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 <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-#include <dev/ofw/ofw_subr.h>
#include <dev/clk/clk.h>
#include <dev/clk/clk_fixed.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/ofw/ofw_subr.h>
+#include <dev/ofw/openfirm.h>
#include <dev/syscon/syscon.h>
#include <dev/phy/phy.h>
#include <dev/mmc/bridge.h>
#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt.h>
#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 <mav@FreeBSD.org>
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/ofw/ofw_subr.h>
+#include <dev/clk/clk.h>
+#include <dev/clk/clk_fixed.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/syscon/syscon.h>
+#include <dev/phy/phy.h>
+
+#include <dev/mmc/bridge.h>
+
+#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt.h>
+
+#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);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 29, 5:20 PM (8 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17843552
Default Alt Text
D48527.id149562.diff (29 KB)
Attached To
Mode
D48527: sdhci: Refactor the generic FDT driver
Attached
Detach File
Event Timeline
Log In to Comment