summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2019-11-29 22:00:54 +0000
committerpatrick <patrick@openbsd.org>2019-11-29 22:00:54 +0000
commita1ad9a59919226ef9bd79a302a9313cab8dea84c (patch)
treef72d81742f4f6bb98db8012f3816676f3908915a
parentMake rkgrf(4) behave like a simplebus(4) so we can attach drivers (diff)
downloadwireguard-openbsd-a1ad9a59919226ef9bd79a302a9313cab8dea84c.tar.xz
wireguard-openbsd-a1ad9a59919226ef9bd79a302a9313cab8dea84c.zip
Add rkemmcphy(4), a driver for the RK3399's eMMC PHY.
-rw-r--r--sys/arch/arm64/conf/GENERIC3
-rw-r--r--sys/arch/arm64/conf/RAMDISK3
-rw-r--r--sys/dev/fdt/files.fdt6
-rw-r--r--sys/dev/fdt/rkemmcphy.c204
4 files changed, 213 insertions, 3 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC
index 4e8c413ef18..ed180c6510b 100644
--- a/sys/arch/arm64/conf/GENERIC
+++ b/sys/arch/arm64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.132 2019/10/23 20:34:04 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.133 2019/11/29 22:00:54 patrick Exp $
#
# GENERIC machine description file
#
@@ -186,6 +186,7 @@ sfp* at fdt?
rkclock* at fdt? early 1
rkgrf* at fdt? early 1
rkpinctrl* at fdt? early 1
+rkemmcphy* at fdt?
rkgpio* at fdt?
rkiic* at fdt?
iic* at rkiic?
diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK
index 6598356acad..a3417d5b532 100644
--- a/sys/arch/arm64/conf/RAMDISK
+++ b/sys/arch/arm64/conf/RAMDISK
@@ -1,4 +1,4 @@
-# $OpenBSD: RAMDISK,v 1.106 2019/10/23 20:34:04 kettenis Exp $
+# $OpenBSD: RAMDISK,v 1.107 2019/11/29 22:00:54 patrick Exp $
#
# GENERIC machine description file
#
@@ -169,6 +169,7 @@ sfp* at fdt?
rkclock* at fdt? early 1
rkgrf* at fdt? early 1
rkpinctrl* at fdt? early 1
+rkemmcphy* at fdt?
rkgpio* at fdt?
rkiic* at fdt?
iic* at rkiic?
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt
index 3d80daa27f2..caed8d5b84d 100644
--- a/sys/dev/fdt/files.fdt
+++ b/sys/dev/fdt/files.fdt
@@ -1,4 +1,4 @@
-# $OpenBSD: files.fdt,v 1.103 2019/11/29 21:59:55 patrick Exp $
+# $OpenBSD: files.fdt,v 1.104 2019/11/29 22:00:54 patrick Exp $
#
# Config file and device description for machine-independent FDT code.
# Included by ports that need it.
@@ -216,6 +216,10 @@ device rkdwusb: fdt
attach rkdwusb at fdt
file dev/fdt/rkdwusb.c rkdwusb
+device rkemmcphy
+attach rkemmcphy at fdt
+file dev/fdt/rkemmcphy.c rkemmcphy
+
device rkgrf: fdt
attach rkgrf at fdt
file dev/fdt/rkgrf.c rkgrf
diff --git a/sys/dev/fdt/rkemmcphy.c b/sys/dev/fdt/rkemmcphy.c
new file mode 100644
index 00000000000..554cf7fcabd
--- /dev/null
+++ b/sys/dev/fdt/rkemmcphy.c
@@ -0,0 +1,204 @@
+/* $OpenBSD: rkemmcphy.c,v 1.1 2019/11/29 22:00:54 patrick Exp $ */
+/*
+ * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_misc.h>
+#include <dev/ofw/fdt.h>
+
+/* Registers */
+#define GRF_EMMCPHY_CON0 0x00
+#define GRF_EMMCPHY_CON0_OTAPDLYSEL_4 (0x4 << 7)
+#define GRF_EMMCPHY_CON0_OTAPDLYENA_EN (1 << 11)
+#define GRF_EMMCPHY_CON0_FREQSEL_200M (0x0 << 12)
+#define GRF_EMMCPHY_CON0_FREQSEL_50M (0x1 << 12)
+#define GRF_EMMCPHY_CON0_FREQSEL_100M (0x2 << 12)
+#define GRF_EMMCPHY_CON0_FREQSEL_150M (0x3 << 12)
+#define GRF_EMMCPHY_CON0_OTAPDLYSEL_CLR (0xf << 23)
+#define GRF_EMMCPHY_CON0_OTAPDLYENA_CLR (1 << 27)
+#define GRF_EMMCPHY_CON0_FREQSEL_CLR (0x3 << 28)
+#define GRF_EMMCPHY_CON1 0x04
+#define GRF_EMMCPHY_CON2 0x08
+#define GRF_EMMCPHY_CON3 0x0c
+#define GRF_EMMCPHY_CON4 0x10
+#define GRF_EMMCPHY_CON5 0x14
+#define GRF_EMMCPHY_CON6 0x18
+#define GRF_EMMCPHY_CON6_PDB_OFF (0 << 0)
+#define GRF_EMMCPHY_CON6_PDB_ON (1 << 0)
+#define GRF_EMMCPHY_CON6_ENDLL_OFF (0 << 1)
+#define GRF_EMMCPHY_CON6_ENDLL_ON (1 << 1)
+#define GRF_EMMCPHY_CON6_DR_50OHM (0x0 << 4)
+#define GRF_EMMCPHY_CON6_DR_33OHM (0x1 << 4)
+#define GRF_EMMCPHY_CON6_DR_66OHM (0x2 << 4)
+#define GRF_EMMCPHY_CON6_DR_100OHM (0x3 << 4)
+#define GRF_EMMCPHY_CON6_DR_40OHM (0x4 << 4)
+#define GRF_EMMCPHY_CON6_PDB_CLR (1 << 16)
+#define GRF_EMMCPHY_CON6_ENDLL_CLR (1 << 17)
+#define GRF_EMMCPHY_CON6_DR_CLR (0x7 << 20)
+#define GRF_EMMCPHY_STATUS 0x20
+#define GRF_EMMCPHY_STATUS_DLLRDY (1 << 5)
+#define GRF_EMMCPHY_STATUS_CALDONE (1 << 6)
+
+#define HREAD4(sc, reg) \
+ (regmap_read_4((sc)->sc_rm, (sc)->sc_off + (reg)))
+#define HWRITE4(sc, reg, val) \
+ regmap_write_4((sc)->sc_rm, (sc)->sc_off + (reg), (val))
+
+struct rkemmcphy_softc {
+ struct device sc_dev;
+ struct regmap *sc_rm;
+ bus_size_t sc_off;
+
+ struct phy_device sc_pd;
+};
+
+int rkemmcphy_match(struct device *, void *, void *);
+void rkemmcphy_attach(struct device *, struct device *, void *);
+
+struct cfattach rkemmcphy_ca = {
+ sizeof (struct rkemmcphy_softc), rkemmcphy_match, rkemmcphy_attach
+};
+
+struct cfdriver rkemmcphy_cd = {
+ NULL, "rkemmcphy", DV_DULL
+};
+
+int rkemmcphy_enable(void *, uint32_t *);
+
+int
+rkemmcphy_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "rockchip,rk3399-emmc-phy");
+}
+
+void
+rkemmcphy_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rkemmcphy_softc *sc = (struct rkemmcphy_softc *)self;
+ struct fdt_attach_args *faa = aux;
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+ sc->sc_off = faa->fa_reg[0].addr;
+
+ sc->sc_rm = regmap_bynode(OF_parent(faa->fa_node));
+ if (sc->sc_rm == NULL) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ printf("\n");
+
+ sc->sc_pd.pd_node = faa->fa_node;
+ sc->sc_pd.pd_cookie = sc;
+ sc->sc_pd.pd_enable = rkemmcphy_enable;
+ phy_register(&sc->sc_pd);
+}
+
+int
+rkemmcphy_enable(void *cookie, uint32_t *cells)
+{
+ struct rkemmcphy_softc *sc = cookie;
+ uint32_t impedance, freqsel, freq, reg;
+ int node = sc->sc_pd.pd_node;
+ int i;
+
+ impedance = OF_getpropint(node, "drive-impedance-ohm", 0);
+ freq = clock_get_frequency(node, "emmcclk");
+
+ switch (impedance) {
+ case 100:
+ impedance = GRF_EMMCPHY_CON6_DR_100OHM;
+ break;
+ case 66:
+ impedance = GRF_EMMCPHY_CON6_DR_66OHM;
+ break;
+ case 50:
+ impedance = GRF_EMMCPHY_CON6_DR_50OHM;
+ break;
+ case 40:
+ impedance = GRF_EMMCPHY_CON6_DR_40OHM;
+ break;
+ case 33:
+ impedance = GRF_EMMCPHY_CON6_DR_33OHM;
+ break;
+ default:
+ impedance = GRF_EMMCPHY_CON6_DR_50OHM;
+ break;
+ }
+
+ if (freq == 0) {
+ freqsel = GRF_EMMCPHY_CON0_FREQSEL_200M;
+ } else if (freq < 75000000) {
+ freqsel = GRF_EMMCPHY_CON0_FREQSEL_50M;
+ } else if (freq < 125000000) {
+ freqsel = GRF_EMMCPHY_CON0_FREQSEL_100M;
+ } else if (freq < 175000000) {
+ freqsel = GRF_EMMCPHY_CON0_FREQSEL_150M;
+ } else {
+ freqsel = GRF_EMMCPHY_CON0_FREQSEL_200M;
+ }
+
+ HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_DR_CLR | impedance);
+ HWRITE4(sc, GRF_EMMCPHY_CON0,
+ GRF_EMMCPHY_CON0_OTAPDLYENA_CLR | GRF_EMMCPHY_CON0_OTAPDLYENA_EN |
+ GRF_EMMCPHY_CON0_OTAPDLYSEL_CLR | GRF_EMMCPHY_CON0_OTAPDLYSEL_4);
+
+ HWRITE4(sc, GRF_EMMCPHY_CON6,
+ GRF_EMMCPHY_CON6_PDB_CLR | GRF_EMMCPHY_CON6_PDB_OFF |
+ GRF_EMMCPHY_CON6_ENDLL_CLR | GRF_EMMCPHY_CON6_ENDLL_OFF);
+
+ delay(3);
+ HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_PDB_CLR |
+ GRF_EMMCPHY_CON6_PDB_ON);
+
+ for (i = 5; i > 0; i--) {
+ reg = HREAD4(sc, GRF_EMMCPHY_STATUS);
+ if (reg & GRF_EMMCPHY_STATUS_CALDONE)
+ break;
+ delay(10);
+ }
+ if (i == 0)
+ printf("%s: timeout\n", sc->sc_dev.dv_xname);
+
+ HWRITE4(sc, GRF_EMMCPHY_CON0, GRF_EMMCPHY_CON0_FREQSEL_CLR | freqsel);
+ HWRITE4(sc, GRF_EMMCPHY_CON6, GRF_EMMCPHY_CON6_ENDLL_CLR |
+ GRF_EMMCPHY_CON6_ENDLL_ON);
+
+ if (freq != 0) {
+ for (i = 5; i > 0; i--) {
+ reg = HREAD4(sc, GRF_EMMCPHY_STATUS);
+ if (reg & GRF_EMMCPHY_STATUS_DLLRDY)
+ break;
+ delay(10 * 1000);
+ }
+ if (i == 0)
+ printf("%s: timeout\n", sc->sc_dev.dv_xname);
+ }
+
+ return 0;
+}