summaryrefslogtreecommitdiffstats
path: root/sys/dev/fdt/mvclock.c
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2018-03-17 18:50:23 +0000
committerkettenis <kettenis@openbsd.org>2018-03-17 18:50:23 +0000
commit33c180b22299989f5fcec1d5615a9af4559ac219 (patch)
tree186ae1259a61b02340a55b01394299006f61d594 /sys/dev/fdt/mvclock.c
parentIn ssl.h rev. 1.152 2018/03/17 15:55:52, tb@ provided (diff)
downloadwireguard-openbsd-33c180b22299989f5fcec1d5615a9af4559ac219.tar.xz
wireguard-openbsd-33c180b22299989f5fcec1d5615a9af4559ac219.zip
Add mvclock(4) a clock driver for the AP806 and CP110 blocks found on Marvell
Armada 7K and 8K SoCs. ok patrick@
Diffstat (limited to 'sys/dev/fdt/mvclock.c')
-rw-r--r--sys/dev/fdt/mvclock.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/sys/dev/fdt/mvclock.c b/sys/dev/fdt/mvclock.c
new file mode 100644
index 00000000000..42433272880
--- /dev/null
+++ b/sys/dev/fdt/mvclock.c
@@ -0,0 +1,198 @@
+/* $OpenBSD: mvclock.c,v 1.1 2018/03/17 18:50:23 kettenis Exp $ */
+/*
+ * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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/intr.h>
+#include <machine/bus.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>
+
+struct mvclock_softc {
+ struct device sc_dev;
+
+ struct clock_device sc_cd;
+};
+
+int mvclock_match(struct device *, void *, void *);
+void mvclock_attach(struct device *, struct device *, void *);
+
+struct cfattach mvclock_ca = {
+ sizeof (struct mvclock_softc), mvclock_match, mvclock_attach
+};
+
+struct cfdriver mvclock_cd = {
+ NULL, "mvclock", DV_DULL
+};
+
+uint32_t ap806_get_frequency(void *, uint32_t *);
+uint32_t cp110_get_frequency(void *, uint32_t *);
+void cp110_enable(void *, uint32_t *, int);
+
+int
+mvclock_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return (OF_is_compatible(faa->fa_node, "marvell,ap806-clock") ||
+ OF_is_compatible(faa->fa_node, "marvell,cp110-clock"));
+}
+
+void
+mvclock_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct mvclock_softc *sc = (struct mvclock_softc *)self;
+ struct fdt_attach_args *faa = aux;
+
+ printf("\n");
+
+ sc->sc_cd.cd_node = faa->fa_node;
+ sc->sc_cd.cd_cookie = sc;
+ if (OF_is_compatible(faa->fa_node, "marvell,ap806-clock")) {
+ sc->sc_cd.cd_get_frequency = ap806_get_frequency;
+ } else {
+ sc->sc_cd.cd_get_frequency = cp110_get_frequency;
+ sc->sc_cd.cd_enable = cp110_enable;
+ }
+ clock_register(&sc->sc_cd);
+}
+
+/* AP806 block */
+
+#define AP806_CORE_FIXED 2
+#define AP806_CORE_MSS 3
+#define AP806_CORE_SDIO 4
+
+uint32_t
+ap806_get_frequency(void *cookie, uint32_t *cells)
+{
+ uint32_t idx = cells[0];
+
+ switch (idx) {
+ case AP806_CORE_FIXED:
+ /* fixed PLL at 1200MHz */
+ return 1200000000;
+ case AP806_CORE_MSS:
+ /* MSS clock is fixed clock divided by 6 */
+ return 200000000;
+ case AP806_CORE_SDIO:
+ /* SDIO/eMMC clock is fixed clock divided by 3 */
+ return 400000000;
+ default:
+ break;
+ }
+
+ printf("%s: 0x%08x\n", __func__, idx);
+ return 0;
+}
+
+/* CP110 block */
+
+#define CP110_PM_CLOCK_GATING_CTRL 0x220
+
+#define CP110_CORE_APLL 0
+#define CP110_CORE_PPV2 1
+#define CP110_CORE_EIP 2
+#define CP110_CORE_CORE 3
+#define CP110_CORE_SDIO 5
+
+#define CP110_GATE_SDIO 4
+
+uint32_t
+cp110_get_frequency(void *cookie, uint32_t *cells)
+{
+ struct mvclock_softc *sc = cookie;
+ uint32_t mod = cells[0];
+ uint32_t idx = cells[1];
+ uint32_t parent[2] = { 0, 0 };
+
+ /* Core clocks */
+ if (mod == 0) {
+ switch (idx) {
+ case CP110_CORE_APLL:
+ /* fixed PLL at 1GHz */
+ return 1000000000;
+ case CP110_CORE_PPV2:
+ /* PPv2 clock is APLL/3 */
+ return 333333333;
+ case CP110_CORE_EIP:
+ /* EIP clock is APLL/2 */
+ return 500000000;
+ case CP110_CORE_CORE:
+ /* Core clock is EIP/2 */
+ return 250000000;
+ case CP110_CORE_SDIO:
+ /* SDIO clock is APLL/2.5 */
+ return 400000000;
+ default:
+ break;
+ }
+ }
+
+ /* Gatable clocks */
+ if (mod == 1) {
+ switch (idx) {
+ case CP110_GATE_SDIO:
+ parent[1] = CP110_CORE_SDIO;
+ break;
+ default:
+ break;
+ }
+
+ if (parent[1] != 0)
+ return cp110_get_frequency(sc, parent);
+ }
+
+ printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
+ return 0;
+}
+
+void
+cp110_enable(void *cookie, uint32_t *cells, int on)
+{
+ struct mvclock_softc *sc = cookie;
+ uint32_t mod = cells[0];
+ uint32_t idx = cells[1];
+
+ /* Gatable clocks */
+ if (mod == 1 && idx < 32) {
+ struct regmap *rm;
+ uint32_t reg;
+
+ rm = regmap_bynode(OF_parent(sc->sc_cd.cd_node));
+ if (rm == NULL) {
+ printf("%s: can't enable clock 0x%08x 0x%08x\n",
+ sc->sc_dev.dv_xname, mod, idx);
+ return;
+ }
+ reg = regmap_read_4(rm, CP110_PM_CLOCK_GATING_CTRL);
+ if (on)
+ reg |= (1U << idx);
+ else
+ reg &= ~(1U << idx);
+ regmap_write_4(rm, CP110_PM_CLOCK_GATING_CTRL, reg);
+ return;
+ }
+
+ printf("%s: 0x%08x 0x%08x\n", __func__, mod, idx);
+}