From bb96de707c2e882044864a4f0a79d1f98e872f2d Mon Sep 17 00:00:00 2001 From: Emmanuel Vadot Date: Mon, 5 Aug 2019 17:43:44 +0000 Subject: [PATCH] MFC r342008, r342010-r342020 r342008: fdt: Add support for simple-mfd bus Quoting the binding Documentation : "These devices comprise a nexus for heterogeneous hardware blocks containing more than one non-unique yet varying hardware functionality." Reviewed by: loos Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D17751 r342010: arm64: Add new SoC type MARVELL_8K Sponsored by: Rubicon Communications, LLC ("Netgate") r342011: arm64: mvebu_pinctrl: Add driver for Marvell Pinmux Controller Add a driver compatible with Marvell mvebu-pinctrl and add ap806-pinctrl support. Sponsored by: Rubicon Communications, LCC ("Netgate") r342012: arm64: marvell: Add driver for Marvell Ap806 System Controller The first two clocks are for the clusters and their frequencies can be found reading a register. Then a fixed 1200Mhz clock is present and two fixed clocks, 'mss' which is 1200 / 6 and 'sdio' which is 1200 / 3. Sponsored by: Rubicon Communications, LLC ("Netgate") r342013: arm64: mv_gpio: Add Marvell 8K support While here put the interrupts setup in it's own function Sponsored by: Rubicon Communications, LCC ("Netgate") r342014: arm64: marvell: Add cp110 clock controller support The cp110 clock controller controls the clocks and gate of the CP110 hardware block. Every clock/gate are implemented except the NAND clock. Sponsored by: Rubicon Communications, LLC ("Netgate") r342015: twsi: Clean up marvell part and add support for Marvell 7k/8k Sponsored by: Rubicon Communications, LLC ("Netgate") r342016: arm64: Add mv_cp110_icu and mv_cp110_gicp icu is a interrupt concentrator in the CP110 block and gicp is a gic extension to allow interrupts in the CP block to be turned into GIC SPI interrupts Sponsored by: Rubicon Communications, LLC ("Netgate") r342017: sdhci_xenon: Add Marvell 8k compatible string Sponsored by: Rubicon Communications, LLC ("Netgate") r342018: mv_gpio: Since it's also an interrupt controller, attach sooner Sponsored by: Rubicon Communications, LLC ("Netgate") r342019: arm64: mv_cp110_icu: Fix build r342020: mv_thermal: Add thermal driver for AP806 and CP110 thermal sensor Sponsored by: Rubicon Communications, LLC ("Netgate") --- sys/arm/mv/gpio.c | 173 ++++++++------- sys/arm/mv/mv_ap806_clock.c | 210 +++++++++++++++++++ sys/arm/mv/mv_ap806_gicp.c | 289 ++++++++++++++++++++++++++ sys/arm/mv/mv_cp110_clock.c | 375 +++++++++++++++++++++++++++++++++ sys/arm/mv/mv_cp110_clock.h | 82 ++++++++ sys/arm/mv/mv_cp110_icu.c | 299 ++++++++++++++++++++++++++ sys/arm/mv/mv_thermal.c | 381 ++++++++++++++++++++++++++++++++++ sys/arm/mv/mvebu_pinctrl.c | 246 ++++++++++++++++++++++ sys/arm64/conf/GENERIC | 8 +- sys/conf/files | 1 + sys/conf/files.arm64 | 8 + sys/conf/options.arm64 | 1 + sys/dev/fdt/simple_mfd.c | 176 ++++++++++++++++ sys/dev/fdt/simplebus.c | 9 +- sys/dev/fdt/simplebus.h | 2 + sys/dev/iicbus/twsi/mv_twsi.c | 116 +++++------ sys/dev/iicbus/twsi/twsi.h | 4 + sys/dev/sdhci/sdhci_xenon.c | 5 + 18 files changed, 2237 insertions(+), 148 deletions(-) create mode 100644 sys/arm/mv/mv_ap806_clock.c create mode 100644 sys/arm/mv/mv_ap806_gicp.c create mode 100644 sys/arm/mv/mv_cp110_clock.c create mode 100644 sys/arm/mv/mv_cp110_clock.h create mode 100644 sys/arm/mv/mv_cp110_icu.c create mode 100644 sys/arm/mv/mv_thermal.c create mode 100644 sys/arm/mv/mvebu_pinctrl.c create mode 100644 sys/dev/fdt/simple_mfd.c diff --git a/sys/arm/mv/gpio.c b/sys/arm/mv/gpio.c index 80155932dee..ef4a4c8e70d 100644 --- a/sys/arm/mv/gpio.c +++ b/sys/arm/mv/gpio.c @@ -60,6 +60,10 @@ __FBSDID("$FreeBSD$"); #include "gpio_if.h" +#ifdef __aarch64__ +#include "opt_soc.h" +#endif + #define GPIO_MAX_INTR_COUNT 8 #define GPIO_PINS_PER_REG 32 #define GPIO_GENERIC_CAP (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ @@ -74,6 +78,7 @@ __FBSDID("$FreeBSD$"); #define DEBOUNCE_CHECK_TICKS ((hz / 1000) * DEBOUNCE_CHECK_MS) struct mv_gpio_softc { + device_t dev; device_t sc_busdev; struct resource * mem_res; int mem_rid; @@ -83,6 +88,7 @@ struct mv_gpio_softc { void *ih_cookie[GPIO_MAX_INTR_COUNT]; bus_space_tag_t bst; bus_space_handle_t bsh; + uint32_t offset; struct mtx mutex; uint8_t pin_num; /* number of GPIO pins */ uint8_t irq_num; /* number of real IRQs occupied by GPIO controller */ @@ -187,11 +193,15 @@ static driver_t mv_gpio_driver = { static devclass_t mv_gpio_devclass; -DRIVER_MODULE(mv_gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0); +EARLY_DRIVER_MODULE(mv_gpio, simplebus, mv_gpio_driver, mv_gpio_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); -struct ofw_compat_data gpio_controllers[] = { - { "mrvl,gpio", (uintptr_t)true }, - { "marvell,orion-gpio", (uintptr_t)true }, +struct ofw_compat_data compat_data[] = { + { "mrvl,gpio", 1 }, + { "marvell,orion-gpio", 1 }, +#ifdef SOC_MARVELL_8K + { "marvell,armada-8k-gpio", 1 }, +#endif { NULL, 0 } }; @@ -201,7 +211,7 @@ mv_gpio_probe(device_t dev) if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (ofw_bus_search_compatible(dev, gpio_controllers)->ocd_data == 0) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Marvell Integrated GPIO Controller"); @@ -209,61 +219,75 @@ mv_gpio_probe(device_t dev) } static int -mv_gpio_attach(device_t dev) +mv_gpio_setup_interrupts(struct mv_gpio_softc *sc, phandle_t node) { - int i, size; - struct mv_gpio_softc *sc; - pcell_t pincnt = 0; - pcell_t irq_cells = 0; phandle_t iparent; - - sc = (struct mv_gpio_softc *)device_get_softc(dev); - if (sc == NULL) - return (ENXIO); - - if (OF_getencprop(ofw_bus_get_node(dev), "pin-count", &pincnt, - sizeof(pcell_t)) >= 0 || - OF_getencprop(ofw_bus_get_node(dev), "ngpios", &pincnt, - sizeof(pcell_t)) >= 0) { - sc->pin_num = MIN(pincnt, MV_GPIO_MAX_NPINS); - if (bootverbose) - device_printf(dev, "%d pins available\n", sc->pin_num); - } else { - device_printf(dev, "ERROR: no pin-count or ngpios entry found!\n"); - return (ENXIO); - } - - /* Assign generic capabilities to every gpio pin */ - for(i = 0; i < sc->pin_num; i++) - sc->gpio_setup[i].gp_caps = GPIO_GENERIC_CAP; + pcell_t irq_cells; + int i, size; /* Find root interrupt controller */ - iparent = ofw_bus_find_iparent(ofw_bus_get_node(dev)); + iparent = ofw_bus_find_iparent(node); if (iparent == 0) { - device_printf(dev, "No interrupt-parrent found. " + device_printf(sc->dev, "No interrupt-parrent found. " "Error in DTB\n"); return (ENXIO); } else { /* While at parent - store interrupt cells prop */ if (OF_searchencprop(OF_node_from_xref(iparent), "#interrupt-cells", &irq_cells, sizeof(irq_cells)) == -1) { - device_printf(dev, "DTB: Missing #interrupt-cells " + device_printf(sc->dev, "DTB: Missing #interrupt-cells " "property in interrupt parent node\n"); return (ENXIO); } } - size = OF_getproplen(ofw_bus_get_node(dev), "interrupts"); + size = OF_getproplen(node, "interrupts"); if (size != -1) { size = size / sizeof(pcell_t); size = size / irq_cells; sc->irq_num = size; - device_printf(dev, "%d IRQs available\n", sc->irq_num); + device_printf(sc->dev, "%d IRQs available\n", sc->irq_num); } else { - device_printf(dev, "ERROR: no interrupts entry found!\n"); + device_printf(sc->dev, "ERROR: no interrupts entry found!\n"); return (ENXIO); } + for (i = 0; i < sc->irq_num; i++) { + sc->irq_rid[i] = i; + sc->irq_res[i] = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, + &sc->irq_rid[i], RF_ACTIVE); + if (!sc->irq_res[i]) { + mtx_destroy(&sc->mutex); + device_printf(sc->dev, + "could not allocate gpio%d interrupt\n", i+1); + return (ENXIO); + } + } + + device_printf(sc->dev, "Disable interrupts (offset = %x + EDGE(0x18)\n", sc->offset); + /* Disable all interrupts */ + bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_EDGE_MASK, 0); + device_printf(sc->dev, "Disable interrupts (offset = %x + LEV(0x1C))\n", sc->offset); + bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_LEV_MASK, 0); + + for (i = 0; i < sc->irq_num; i++) { + device_printf(sc->dev, "Setup intr %d\n", i); + if (bus_setup_intr(sc->dev, sc->irq_res[i], + INTR_TYPE_MISC, + (driver_filter_t *)mv_gpio_intr, NULL, + sc, &sc->ih_cookie[i]) != 0) { + mtx_destroy(&sc->mutex); + bus_release_resource(sc->dev, SYS_RES_IRQ, + sc->irq_rid[i], sc->irq_res[i]); + device_printf(sc->dev, "could not set up intr %d\n", i); + return (ENXIO); + } + } + + /* Clear interrupt status. */ + device_printf(sc->dev, "Clear int status (offset = %x)\n", sc->offset); + bus_space_write_4(sc->bst, sc->bsh, sc->offset + GPIO_INT_CAUSE, 0); + sc->debounce_callouts = (struct callout **)malloc(sc->pin_num * sizeof(struct callout *), M_DEVBUF, M_WAITOK | M_ZERO); if (sc->debounce_callouts == NULL) @@ -274,11 +298,46 @@ mv_gpio_attach(device_t dev) if (sc->debounce_counters == NULL) return (ENOMEM); + return (0); +} + +static int +mv_gpio_attach(device_t dev) +{ + int i, rv; + struct mv_gpio_softc *sc; + phandle_t node; + pcell_t pincnt = 0; + + sc = (struct mv_gpio_softc *)device_get_softc(dev); + if (sc == NULL) + return (ENXIO); + + node = ofw_bus_get_node(dev); + sc->dev = dev; + + if (OF_getencprop(node, "pin-count", &pincnt, sizeof(pcell_t)) >= 0 || + OF_getencprop(node, "ngpios", &pincnt, sizeof(pcell_t)) >= 0) { + sc->pin_num = MIN(pincnt, MV_GPIO_MAX_NPINS); + if (bootverbose) + device_printf(dev, "%d pins available\n", sc->pin_num); + } else { + device_printf(dev, "ERROR: no pin-count or ngpios entry found!\n"); + return (ENXIO); + } + + if (OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset)) == -1) + sc->offset = 0; + + /* Assign generic capabilities to every gpio pin */ + for(i = 0; i < sc->pin_num; i++) + sc->gpio_setup[i].gp_caps = GPIO_GENERIC_CAP; + mtx_init(&sc->mutex, device_get_nameunit(dev), NULL, MTX_SPIN); sc->mem_rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, - RF_ACTIVE); + RF_ACTIVE | RF_SHAREABLE ); if (!sc->mem_res) { mtx_destroy(&sc->mutex); @@ -289,37 +348,9 @@ mv_gpio_attach(device_t dev) sc->bst = rman_get_bustag(sc->mem_res); sc->bsh = rman_get_bushandle(sc->mem_res); - for (i = 0; i < sc->irq_num; i++) { - sc->irq_rid[i] = i; - sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ, - &sc->irq_rid[i], RF_ACTIVE); - if (!sc->irq_res[i]) { - mtx_destroy(&sc->mutex); - device_printf(dev, - "could not allocate gpio%d interrupt\n", i+1); - return (ENXIO); - } - } - - /* Disable all interrupts */ - bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_EDGE_MASK, 0); - bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_LEV_MASK, 0); - - for (i = 0; i < sc->irq_num; i++) { - if (bus_setup_intr(dev, sc->irq_res[i], - INTR_TYPE_MISC, - (driver_filter_t *)mv_gpio_intr, NULL, - sc, &sc->ih_cookie[i]) != 0) { - mtx_destroy(&sc->mutex); - bus_release_resource(dev, SYS_RES_IRQ, - sc->irq_rid[i], sc->irq_res[i]); - device_printf(dev, "could not set up intr %d\n", i); - return (ENXIO); - } - } - - /* Clear interrupt status. */ - bus_space_write_4(sc->bst, sc->bsh, GPIO_INT_CAUSE, 0); + rv = mv_gpio_setup_interrupts(sc, node); + if (rv != 0) + return (rv); sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { @@ -540,6 +571,8 @@ mv_gpio_configure(device_t dev, uint32_t pin, uint32_t flags, uint32_t mask) return (EINVAL); if (mask & MV_GPIO_IN_DEBOUNCE) { + if (sc->irq_num == 0) + return (EINVAL); error = mv_gpio_debounce_prepare(dev, pin); if (error != 0) return (error); @@ -845,7 +878,7 @@ mv_gpio_reg_read(device_t dev, uint32_t reg) struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); - return (bus_space_read_4(sc->bst, sc->bsh, reg)); + return (bus_space_read_4(sc->bst, sc->bsh, sc->offset + reg)); } static void @@ -854,7 +887,7 @@ mv_gpio_reg_write(device_t dev, uint32_t reg, uint32_t val) struct mv_gpio_softc *sc; sc = (struct mv_gpio_softc *)device_get_softc(dev); - bus_space_write_4(sc->bst, sc->bsh, reg, val); + bus_space_write_4(sc->bst, sc->bsh, sc->offset + reg, val); } static void diff --git a/sys/arm/mv/mv_ap806_clock.c b/sys/arm/mv/mv_ap806_clock.c new file mode 100644 index 00000000000..1df07c0d439 --- /dev/null +++ b/sys/arm/mv/mv_ap806_clock.c @@ -0,0 +1,210 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +static struct clk_fixed_def ap806_clk_cluster_0 = { + .clkdef.id = 0, + .clkdef.name = "ap806-cpu-cluster-0", + .freq = 0, +}; + +static struct clk_fixed_def ap806_clk_cluster_1 = { + .clkdef.id = 1, + .clkdef.name = "ap806-cpu-cluster-1", + .freq = 0, +}; + +static struct clk_fixed_def ap806_clk_fixed = { + .clkdef.id = 2, + .clkdef.name = "ap806-fixed", + .freq = 1200000000, +}; + +/* Thoses are the only exported clocks AFAICT */ + +static const char *mss_parents[] = {"ap806-fixed"}; +static struct clk_fixed_def ap806_clk_mss = { + .clkdef.id = 3, + .clkdef.name = "ap806-mss", + .clkdef.parent_names = mss_parents, + .clkdef.parent_cnt = 1, + .mult = 1, + .div = 6, +}; + +static const char *sdio_parents[] = {"ap806-fixed"}; +static struct clk_fixed_def ap806_clk_sdio = { + .clkdef.id = 4, + .clkdef.name = "ap806-sdio", + .clkdef.parent_names = sdio_parents, + .clkdef.parent_cnt = 1, + .mult = 1, + .div = 3, +}; + +struct mv_ap806_clock_softc { + struct simplebus_softc simplebus_sc; + device_t dev; + struct resource *res; +}; + +static struct resource_spec mv_ap806_clock_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,ap806-clock", 1}, + {NULL, 0} +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int +mv_ap806_clock_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell AP806 Clock Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_ap806_clock_attach(device_t dev) +{ + struct mv_ap806_clock_softc *sc; + struct clkdom *clkdom; + uint64_t clock_freq; + uint32_t reg; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, mv_ap806_clock_res_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + /* + * We might miss some combinations + * Those are the only possible ones on the mcbin + */ + reg = RD4(sc, 0x400); + switch (reg & 0x1f) { + case 0x0: + case 0x1: + clock_freq = 2000000000; + break; + case 0x6: + clock_freq = 1800000000; + break; + case 0xd: + clock_freq = 1600000000; + break; + case 0x14: + clock_freq = 1333000000; + break; + default: + device_printf(dev, "Cannot guess clock freq with reg %x\n", reg & 0x1f); + return (ENXIO); + break; + }; + + ap806_clk_cluster_0.freq = clock_freq; + ap806_clk_cluster_1.freq = clock_freq; + clkdom = clkdom_create(dev); + + clknode_fixed_register(clkdom, &ap806_clk_cluster_0); + clknode_fixed_register(clkdom, &ap806_clk_cluster_1); + clknode_fixed_register(clkdom, &ap806_clk_fixed); + clknode_fixed_register(clkdom, &ap806_clk_mss); + clknode_fixed_register(clkdom, &ap806_clk_sdio); + + clkdom_finit(clkdom); + + if (bootverbose) + clkdom_dump(clkdom); + return (0); +} + +static int +mv_ap806_clock_detach(device_t dev) +{ + + return (EBUSY); +} + +static device_method_t mv_ap806_clock_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_ap806_clock_probe), + DEVMETHOD(device_attach, mv_ap806_clock_attach), + DEVMETHOD(device_detach, mv_ap806_clock_detach), + + DEVMETHOD_END +}; + +static devclass_t mv_ap806_clock_devclass; + +static driver_t mv_ap806_clock_driver = { + "mv_ap806_clock", + mv_ap806_clock_methods, + sizeof(struct mv_ap806_clock_softc), +}; + +EARLY_DRIVER_MODULE(mv_ap806_clock, simplebus, mv_ap806_clock_driver, + mv_ap806_clock_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE); diff --git a/sys/arm/mv/mv_ap806_gicp.c b/sys/arm/mv/mv_ap806_gicp.c new file mode 100644 index 00000000000..98494e88e65 --- /dev/null +++ b/sys/arm/mv/mv_ap806_gicp.c @@ -0,0 +1,289 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "pic_if.h" + +#define MV_AP806_GICP_MAX_NIRQS 207 + +struct mv_ap806_gicp_softc { + device_t dev; + device_t parent; + struct resource *res; + + ssize_t spi_ranges_cnt; + uint32_t *spi_ranges; +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,ap806-gicp", 1}, + {NULL, 0} +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int +mv_ap806_gicp_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell GICP"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_ap806_gicp_attach(device_t dev) +{ + struct mv_ap806_gicp_softc *sc; + phandle_t node, xref, intr_parent; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + /* Look for our parent */ + if ((intr_parent = ofw_bus_find_iparent(node)) == 0) { + device_printf(dev, "Cannot find our parent interrupt controller\n"); + return (ENXIO); + } + if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) { + device_printf(dev, "cannot find parent interrupt controller device\n"); + return (ENXIO); + } + + sc->spi_ranges_cnt = OF_getencprop_alloc(node, "marvell,spi-ranges", + (void **)&sc->spi_ranges); + + xref = OF_xref_from_node(node); + if (intr_pic_register(dev, xref) == NULL) { + device_printf(dev, "Cannot register GICP\n"); + return (ENXIO); + } + + OF_device_register_xref(xref, dev); + + return (0); +} + +static int +mv_ap806_gicp_detach(device_t dev) +{ + + return (EBUSY); +} + +static int +mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); +} + +static void +mv_ap806_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + PIC_ENABLE_INTR(sc->parent, isrc); +} + +static void +mv_ap806_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + PIC_DISABLE_INTR(sc->parent, isrc); +} + +static int +mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct mv_ap806_gicp_softc *sc; + struct intr_map_data_fdt *daf; + uint32_t group, irq_num, irq_type; + int i; + + sc = device_get_softc(dev); + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 3 || daf->cells[0] >= MV_AP806_GICP_MAX_NIRQS) + return (EINVAL); + + group = daf->cells[0]; + irq_num = daf->cells[1]; + irq_type = daf->cells[2]; + + /* Map the interrupt number to spi number */ + for (i = 0; i < sc->spi_ranges_cnt / 2; i += 2) { + if (irq_num < sc->spi_ranges[i + 1]) { + irq_num += sc->spi_ranges[i]; + break; + } + + irq_num -= sc->spi_ranges[i]; + } + + daf->cells[1] = irq_num - 32; + + return (PIC_MAP_INTR(sc->parent, data, isrcp)); +} + +static int +mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); +} + +static int +mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); +} + +static int +mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); +} + +static void +mv_ap806_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + PIC_PRE_ITHREAD(sc->parent, isrc); +} + +static void +mv_ap806_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + PIC_POST_ITHREAD(sc->parent, isrc); +} + +static void +mv_ap806_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_gicp_softc *sc; + + sc = device_get_softc(dev); + + PIC_POST_FILTER(sc->parent, isrc); +} + +static device_method_t mv_ap806_gicp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_ap806_gicp_probe), + DEVMETHOD(device_attach, mv_ap806_gicp_attach), + DEVMETHOD(device_detach, mv_ap806_gicp_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_activate_intr, mv_ap806_gicp_activate_intr), + DEVMETHOD(pic_disable_intr, mv_ap806_gicp_disable_intr), + DEVMETHOD(pic_enable_intr, mv_ap806_gicp_enable_intr), + DEVMETHOD(pic_map_intr, mv_ap806_gicp_map_intr), + DEVMETHOD(pic_deactivate_intr, mv_ap806_gicp_deactivate_intr), + DEVMETHOD(pic_setup_intr, mv_ap806_gicp_setup_intr), + DEVMETHOD(pic_teardown_intr, mv_ap806_gicp_teardown_intr), + DEVMETHOD(pic_post_filter, mv_ap806_gicp_post_filter), + DEVMETHOD(pic_post_ithread, mv_ap806_gicp_post_ithread), + DEVMETHOD(pic_pre_ithread, mv_ap806_gicp_pre_ithread), + + DEVMETHOD_END +}; + +static devclass_t mv_ap806_gicp_devclass; + +static driver_t mv_ap806_gicp_driver = { + "mv_ap806_gicp", + mv_ap806_gicp_methods, + sizeof(struct mv_ap806_gicp_softc), +}; + +EARLY_DRIVER_MODULE(mv_ap806_gicp, simplebus, mv_ap806_gicp_driver, + mv_ap806_gicp_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/mv/mv_cp110_clock.c b/sys/arm/mv/mv_cp110_clock.c new file mode 100644 index 00000000000..06165f3e844 --- /dev/null +++ b/sys/arm/mv/mv_cp110_clock.c @@ -0,0 +1,375 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include "clkdev_if.h" + +/* Clocks */ +static struct clk_fixed_def cp110_clk_pll_0 = { + .clkdef.id = CP110_PLL_0, + .freq = 1000000000, +}; + +static const char *clk_parents_0[] = {"cp110-pll0-0"}; +static const char *clk_parents_1[] = {"cp110-pll0-1"}; + +static struct clk_fixed_def cp110_clk_ppv2_core = { + .clkdef.id = CP110_PPV2_CORE, + .clkdef.parent_cnt = 1, + .mult = 1, + .div = 3, +}; + +static struct clk_fixed_def cp110_clk_x2core = { + .clkdef.id = CP110_X2CORE, + .clkdef.parent_cnt = 1, + .mult = 1, + .div = 2, +}; + +static const char *core_parents_0[] = {"cp110-x2core-0"}; +static const char *core_parents_1[] = {"cp110-x2core-1"}; + +static struct clk_fixed_def cp110_clk_core = { + .clkdef.id = CP110_CORE, + .clkdef.parent_cnt = 1, + .mult = 1, + .div = 2, +}; + +static struct clk_fixed_def cp110_clk_sdio = { + .clkdef.id = CP110_SDIO, + .clkdef.parent_cnt = 1, + .mult = 2, + .div = 5, +}; + +/* Gates */ + +static struct cp110_gate cp110_gates[] = { + CCU_GATE(CP110_GATE_AUDIO, "cp110-gate-audio", 0) + CCU_GATE(CP110_GATE_COMM_UNIT, "cp110-gate-comm_unit", 1) + /* CCU_GATE(CP110_GATE_NAND, "cp110-gate-nand", 2) */ + CCU_GATE(CP110_GATE_PPV2, "cp110-gate-ppv2", 3) + CCU_GATE(CP110_GATE_SDIO, "cp110-gate-sdio", 4) + CCU_GATE(CP110_GATE_MG, "cp110-gate-mg", 5) + CCU_GATE(CP110_GATE_MG_CORE, "cp110-gate-mg_core", 6) + CCU_GATE(CP110_GATE_XOR1, "cp110-gate-xor1", 7) + CCU_GATE(CP110_GATE_XOR0, "cp110-gate-xor0", 8) + CCU_GATE(CP110_GATE_GOP_DP, "cp110-gate-gop_dp", 9) + CCU_GATE(CP110_GATE_PCIE_X1_0, "cp110-gate-pcie_x10", 11) + CCU_GATE(CP110_GATE_PCIE_X1_1, "cp110-gate-pcie_x11", 12) + CCU_GATE(CP110_GATE_PCIE_X4, "cp110-gate-pcie_x4", 13) + CCU_GATE(CP110_GATE_PCIE_XOR, "cp110-gate-pcie_xor", 14) + CCU_GATE(CP110_GATE_SATA, "cp110-gate-sata", 15) + CCU_GATE(CP110_GATE_SATA_USB, "cp110-gate-sata_usb", 16) + CCU_GATE(CP110_GATE_MAIN, "cp110-gate-main", 17) + CCU_GATE(CP110_GATE_SDMMC_GOP, "cp110-gate-sdmmc_gop", 18) + CCU_GATE(CP110_GATE_SLOW_IO, "cp110-gate-slow_io", 21) + CCU_GATE(CP110_GATE_USB3H0, "cp110-gate-usb3h0", 22) + CCU_GATE(CP110_GATE_USB3H1, "cp110-gate-usb3h1", 23) + CCU_GATE(CP110_GATE_USB3DEV, "cp110-gate-usb3dev", 24) + CCU_GATE(CP110_GATE_EIP150, "cp110-gate-eip150", 25) + CCU_GATE(CP110_GATE_EIP197, "cp110-gate-eip197", 26) +}; + +struct mv_cp110_clock_softc { + struct simplebus_softc simplebus_sc; + device_t dev; + struct resource *res; + struct mtx mtx; +}; + +static struct resource_spec mv_cp110_clock_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,cp110-clock", 1}, + {NULL, 0} +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static char * +mv_cp110_clock_name(device_t dev, const char *name) +{ + char *clkname = NULL; + int unit; + + unit = device_get_unit(dev); + if (asprintf(&clkname, M_DEVBUF, "%s-%d", name, unit) <= 0) + panic("Cannot generate unique clock name for %s\n", name); + return (clkname); +} + +static int +mv_cp110_clock_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell CP110 Clock Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +cp110_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) +{ + int id = 0; + + if (ncells != 2) + return (ENXIO); + + id = cells[1]; + if (cells[0] == 1) + id += CP110_MAX_CLOCK; + + *clk = clknode_find_by_id(clkdom, id); + + return (0); +} + +static int +mv_cp110_clock_attach(device_t dev) +{ + struct mv_cp110_clock_softc *sc; + struct clkdom *clkdom; + struct clk_gate_def def; + char *pll0_name; + int unit, i; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, mv_cp110_clock_res_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + unit = device_get_unit(dev); + if (unit > 1) { + device_printf(dev, "Bogus cp110-system-controller unit %d\n", unit); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + clkdom = clkdom_create(dev); + clkdom_set_ofw_mapper(clkdom, cp110_ofw_map); + + pll0_name = mv_cp110_clock_name(dev, "cp110-pll0"); + cp110_clk_pll_0.clkdef.name = pll0_name; + clknode_fixed_register(clkdom, &cp110_clk_pll_0); + + cp110_clk_ppv2_core.clkdef.name = mv_cp110_clock_name(dev, "cp110-ppv2"); + cp110_clk_ppv2_core.clkdef.parent_names = (unit == 0) ? clk_parents_0 : clk_parents_1; + clknode_fixed_register(clkdom, &cp110_clk_ppv2_core); + + cp110_clk_x2core.clkdef.name = mv_cp110_clock_name(dev, "cp110-x2core"); + cp110_clk_x2core.clkdef.parent_names = (unit == 0) ? clk_parents_0 : clk_parents_1; + clknode_fixed_register(clkdom, &cp110_clk_x2core); + + cp110_clk_core.clkdef.name = mv_cp110_clock_name(dev, "cp110-core"); + cp110_clk_core.clkdef.parent_names = (unit == 0) ? core_parents_0 : core_parents_1; + clknode_fixed_register(clkdom, &cp110_clk_core); + + /* NAND missing */ + + cp110_clk_sdio.clkdef.name = mv_cp110_clock_name(dev, "cp110-sdio"); + cp110_clk_sdio.clkdef.parent_names = (unit == 0) ? clk_parents_0 : clk_parents_1; + clknode_fixed_register(clkdom, &cp110_clk_sdio); + + for (i = 0; i < nitems(cp110_gates); i++) { + if (cp110_gates[i].name == NULL) + continue; + + memset(&def, 0, sizeof(def)); + def.clkdef.id = CP110_MAX_CLOCK + i; + def.clkdef.name = mv_cp110_clock_name(dev, cp110_gates[i].name); + def.clkdef.parent_cnt = 1; + def.offset = CP110_CLOCK_GATING_OFFSET; + def.shift = cp110_gates[i].shift; + def.mask = 1; + def.on_value = 1; + def.off_value = 0; + + switch (i) { + case CP110_GATE_MG: + case CP110_GATE_GOP_DP: + case CP110_GATE_PPV2: + def.clkdef.parent_names = &cp110_clk_ppv2_core.clkdef.name; + break; + case CP110_GATE_SDIO: + def.clkdef.parent_names = &cp110_clk_sdio.clkdef.name; + break; + case CP110_GATE_MAIN: + case CP110_GATE_PCIE_XOR: + case CP110_GATE_PCIE_X4: + case CP110_GATE_EIP150: + case CP110_GATE_EIP197: + def.clkdef.parent_names = &cp110_clk_x2core.clkdef.name; + break; + default: + def.clkdef.parent_names = &cp110_clk_core.clkdef.name; + break; + } + + clknode_gate_register(clkdom, &def); + } + + clkdom_finit(clkdom); + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); +} + +static int +mv_cp110_clock_detach(device_t dev) +{ + + return (EBUSY); +} + +static int +mv_cp110_clock_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct mv_cp110_clock_softc *sc; + + sc = device_get_softc(dev); + WR4(sc, addr, val); + return (0); +} + +static int +mv_cp110_clock_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct mv_cp110_clock_softc *sc; + + sc = device_get_softc(dev); + + *val = RD4(sc, addr); + return (0); +} + +static int +mv_cp110_clock_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) +{ + struct mv_cp110_clock_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + reg = RD4(sc, addr); + reg &= ~clr; + reg |= set; + WR4(sc, addr, reg); + + return (0); +} + +static void +mv_cp110_clock_device_lock(device_t dev) +{ + struct mv_cp110_clock_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +mv_cp110_clock_device_unlock(device_t dev) +{ + struct mv_cp110_clock_softc *sc; + + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static device_method_t mv_cp110_clock_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_cp110_clock_probe), + DEVMETHOD(device_attach, mv_cp110_clock_attach), + DEVMETHOD(device_detach, mv_cp110_clock_detach), + + /* clkdev interface */ + DEVMETHOD(clkdev_write_4, mv_cp110_clock_write_4), + DEVMETHOD(clkdev_read_4, mv_cp110_clock_read_4), + DEVMETHOD(clkdev_modify_4, mv_cp110_clock_modify_4), + DEVMETHOD(clkdev_device_lock, mv_cp110_clock_device_lock), + DEVMETHOD(clkdev_device_unlock, mv_cp110_clock_device_unlock), + + DEVMETHOD_END +}; + +static devclass_t mv_cp110_clock_devclass; + +static driver_t mv_cp110_clock_driver = { + "mv_cp110_clock", + mv_cp110_clock_methods, + sizeof(struct mv_cp110_clock_softc), +}; + +EARLY_DRIVER_MODULE(mv_cp110_clock, simplebus, mv_cp110_clock_driver, + mv_cp110_clock_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_LATE); diff --git a/sys/arm/mv/mv_cp110_clock.h b/sys/arm/mv/mv_cp110_clock.h new file mode 100644 index 00000000000..7c67254817e --- /dev/null +++ b/sys/arm/mv/mv_cp110_clock.h @@ -0,0 +1,82 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef _MV_CP110_SYSCON_H_ +#define _MV_CP110_SYSCON_H_ + +enum mv_cp110_clk_id { + CP110_PLL_0 = 0, + CP110_PPV2_CORE, + CP110_X2CORE, + CP110_CORE, + CP110_NAND, + CP110_SDIO, + CP110_MAX_CLOCK +}; + +/* Gates */ +#define CP110_CLOCK_GATING_OFFSET 0x220 + +struct cp110_gate { + const char *name; + uint32_t shift; +}; + +#define CCU_GATE(idx, clkname, s) \ + [idx] = { \ + .name = clkname, \ + .shift = s, \ + }, + +#define CP110_GATE_AUDIO 0 +#define CP110_GATE_COMM_UNIT 1 +#define CP110_GATE_NAND 2 +#define CP110_GATE_PPV2 3 +#define CP110_GATE_SDIO 4 +#define CP110_GATE_MG 5 +#define CP110_GATE_MG_CORE 6 +#define CP110_GATE_XOR1 7 +#define CP110_GATE_XOR0 8 +#define CP110_GATE_GOP_DP 9 +#define CP110_GATE_PCIE_X1_0 11 +#define CP110_GATE_PCIE_X1_1 12 +#define CP110_GATE_PCIE_X4 13 +#define CP110_GATE_PCIE_XOR 14 +#define CP110_GATE_SATA 15 +#define CP110_GATE_SATA_USB 16 +#define CP110_GATE_MAIN 17 +#define CP110_GATE_SDMMC_GOP 18 +#define CP110_GATE_SLOW_IO 21 +#define CP110_GATE_USB3H0 22 +#define CP110_GATE_USB3H1 23 +#define CP110_GATE_USB3DEV 24 +#define CP110_GATE_EIP150 25 +#define CP110_GATE_EIP197 26 + +#endif diff --git a/sys/arm/mv/mv_cp110_icu.c b/sys/arm/mv/mv_cp110_icu.c new file mode 100644 index 00000000000..4d62670e3a6 --- /dev/null +++ b/sys/arm/mv/mv_cp110_icu.c @@ -0,0 +1,299 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "pic_if.h" + +#define ICU_GRP_NSR 0x0 +#define ICU_GRP_SR 0x1 +#define ICU_GRP_SEI 0x4 +#define ICU_GRP_REI 0x5 + +#define ICU_SETSPI_NSR_AL 0x10 +#define ICU_SETSPI_NSR_AH 0x14 +#define ICU_CLRSPI_NSR_AL 0x18 +#define ICU_CLRSPI_NSR_AH 0x1c +#define ICU_INT_CFG(x) (0x100 + (x) * 4) +#define ICU_INT_ENABLE (1 << 24) +#define ICU_INT_EDGE (1 << 28) +#define ICU_INT_GROUP_SHIFT 29 +#define ICU_INT_MASK 0x3ff + +#define MV_CP110_ICU_MAX_NIRQS 207 + +struct mv_cp110_icu_softc { + device_t dev; + device_t parent; + struct resource *res; +}; + +static struct resource_spec mv_cp110_icu_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,cp110-icu", 1}, + {NULL, 0} +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static int +mv_cp110_icu_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell Interrupt Consolidation Unit"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_cp110_icu_attach(device_t dev) +{ + struct mv_cp110_icu_softc *sc; + phandle_t node, msi_parent; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + if (OF_getencprop(node, "msi-parent", &msi_parent, + sizeof(phandle_t)) <= 0) { + device_printf(dev, "cannot find msi-parent property\n"); + return (ENXIO); + } + + if ((sc->parent = OF_device_from_xref(msi_parent)) == NULL) { + device_printf(dev, "cannot find msi-parent device\n"); + return (ENXIO); + } + if (bus_alloc_resources(dev, mv_cp110_icu_res_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { + device_printf(dev, "Cannot register ICU\n"); + goto fail; + } + return (0); + +fail: + bus_release_resources(dev, mv_cp110_icu_res_spec, &sc->res); + return (ENXIO); +} + +static int +mv_cp110_icu_detach(device_t dev) +{ + + return (EBUSY); +} + +static int +mv_cp110_icu_activate_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); +} + +static void +mv_cp110_icu_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + PIC_ENABLE_INTR(sc->parent, isrc); +} + +static void +mv_cp110_icu_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + PIC_DISABLE_INTR(sc->parent, isrc); +} + +static int +mv_cp110_icu_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct mv_cp110_icu_softc *sc; + struct intr_map_data_fdt *daf; + uint32_t reg; + + sc = device_get_softc(dev); + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 3 || daf->cells[0] >= MV_CP110_ICU_MAX_NIRQS) + return (EINVAL); + + reg = RD4(sc, ICU_INT_CFG(daf->cells[1])); + + if ((reg & ICU_INT_ENABLE) == 0) { + reg |= ICU_INT_ENABLE; + WR4(sc, ICU_INT_CFG(daf->cells[1]), reg); + } + + daf->cells[1] = reg & ICU_INT_MASK; + return (PIC_MAP_INTR(sc->parent, data, isrcp)); +} + +static int +mv_cp110_icu_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); +} + +static int +mv_cp110_icu_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); +} + +static int +mv_cp110_icu_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); +} + +static void +mv_cp110_icu_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + PIC_PRE_ITHREAD(sc->parent, isrc); +} + +static void +mv_cp110_icu_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + PIC_POST_ITHREAD(sc->parent, isrc); +} + +static void +mv_cp110_icu_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_cp110_icu_softc *sc; + + sc = device_get_softc(dev); + + PIC_POST_FILTER(sc->parent, isrc); +} + +static device_method_t mv_cp110_icu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_cp110_icu_probe), + DEVMETHOD(device_attach, mv_cp110_icu_attach), + DEVMETHOD(device_detach, mv_cp110_icu_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_activate_intr, mv_cp110_icu_activate_intr), + DEVMETHOD(pic_disable_intr, mv_cp110_icu_disable_intr), + DEVMETHOD(pic_enable_intr, mv_cp110_icu_enable_intr), + DEVMETHOD(pic_map_intr, mv_cp110_icu_map_intr), + DEVMETHOD(pic_deactivate_intr, mv_cp110_icu_deactivate_intr), + DEVMETHOD(pic_setup_intr, mv_cp110_icu_setup_intr), + DEVMETHOD(pic_teardown_intr, mv_cp110_icu_teardown_intr), + DEVMETHOD(pic_post_filter, mv_cp110_icu_post_filter), + DEVMETHOD(pic_post_ithread, mv_cp110_icu_post_ithread), + DEVMETHOD(pic_pre_ithread, mv_cp110_icu_pre_ithread), + + DEVMETHOD_END +}; + +static devclass_t mv_cp110_icu_devclass; + +static driver_t mv_cp110_icu_driver = { + "mv_cp110_icu", + mv_cp110_icu_methods, + sizeof(struct mv_cp110_icu_softc), +}; + +EARLY_DRIVER_MODULE(mv_cp110_icu, simplebus, mv_cp110_icu_driver, + mv_cp110_icu_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); diff --git a/sys/arm/mv/mv_thermal.c b/sys/arm/mv/mv_thermal.c new file mode 100644 index 00000000000..60eb854ac04 --- /dev/null +++ b/sys/arm/mv/mv_thermal.c @@ -0,0 +1,381 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#define CONTROL0 0x00 +#define CONTROL0_TSEN_START (1 << 0) +#define CONTROL0_TSEN_RESET (1 << 1) +#define CONTROL0_TSEN_EN (1 << 2) +#define CONTROL0_CHANNEL_SHIFT 13 +#define CONTROL0_CHANNEL_MASK 0xF +#define CONTROL0_OSR_SHIFT 24 +#define CONTROL0_OSR_MAX 3 /* OSR = 512 * 4uS = ~2mS */ +#define CONTROL0_MODE_SHIFT 30 +#define CONTROL0_MODE_EXTERNAL 0x2 +#define CONTROL0_MODE_MASK 0x3 + +#define CONTROL1 0x04 +/* This doesn't seems to work */ +#define CONTROL1_TSEN_SENS_SHIFT 21 +#define CONTROL1_TSEN_SENS_MASK 0x7 + +#define STATUS 0x00 +#define STATUS_TEMP_MASK 0x3FF + +enum mv_thermal_type { + MV_AP806 = 1, + MV_CP110, +}; + +struct mv_thermal_config { + enum mv_thermal_type type; + int ncpus; + int64_t calib_mul; + int64_t calib_add; + int64_t calib_div; + uint32_t valid_mask; + bool signed_value; +}; + +struct mv_thermal_softc { + device_t dev; + struct resource *res[2]; + struct mtx mtx; + + struct mv_thermal_config *config; + int cur_sensor; +}; + +static struct mv_thermal_config mv_ap806_config = { + .type = MV_AP806, + .ncpus = 4, + .calib_mul = 423, + .calib_add = -150000, + .calib_div = 100, + .valid_mask = (1 << 16), + .signed_value = true, +}; + +static struct mv_thermal_config mv_cp110_config = { + .type = MV_CP110, + .calib_mul = 2000096, + .calib_add = 1172499100, + .calib_div = 420100, + .valid_mask = (1 << 10), + .signed_value = false, +}; + +static struct resource_spec mv_thermal_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,armada-ap806-thermal", (uintptr_t) &mv_ap806_config}, + {"marvell,armada-cp110-thermal", (uintptr_t) &mv_cp110_config}, + {NULL, 0} +}; + +#define RD_STA(sc, reg) bus_read_4((sc)->res[0], (reg)) +#define WR_STA(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) +#define RD_CON(sc, reg) bus_read_4((sc)->res[1], (reg)) +#define WR_CON(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val)) + +static inline int32_t sign_extend(uint32_t value, int index) +{ + uint8_t shift; + + shift = 31 - index; + return ((int32_t)(value << shift) >> shift); +} + +static int +mv_thermal_wait_sensor(struct mv_thermal_softc *sc) +{ + uint32_t reg; + uint32_t timeout; + + timeout = 100000; + while (--timeout > 0) { + reg = RD_STA(sc, STATUS); + if ((reg & sc->config->valid_mask) == sc->config->valid_mask) + break; + DELAY(100); + } + if (timeout == 0) { + return (ETIMEDOUT); + } + + return (0); +} + +static int +mv_thermal_select_sensor(struct mv_thermal_softc *sc, int sensor) +{ + uint32_t reg; + + if (sc->cur_sensor == sensor) + return (0); + + /* Stop the current reading and reset the module */ + reg = RD_CON(sc, CONTROL0); + reg &= ~(CONTROL0_TSEN_START | CONTROL0_TSEN_EN); + WR_CON(sc, CONTROL0, reg); + + /* Switch to the selected sensor */ + /* + * NOTE : Datasheet says to use CONTROL1 for selecting + * but when doing so the sensors >0 are never ready + * Do what Linux does using undocumented bits in CONTROL0 + */ + /* This reset automatically to the sensor 0 */ + reg &= ~(CONTROL0_MODE_MASK << CONTROL0_MODE_SHIFT); + if (sensor) { + /* Select external sensor */ + reg |= CONTROL0_MODE_EXTERNAL << CONTROL0_MODE_SHIFT; + reg &= ~(CONTROL0_CHANNEL_MASK << CONTROL0_CHANNEL_SHIFT); + reg |= (sensor - 1) << CONTROL0_CHANNEL_SHIFT; + } + WR_CON(sc, CONTROL0, reg); + sc->cur_sensor = sensor; + + /* Start the reading */ + reg = RD_CON(sc, CONTROL0); + reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN; + WR_CON(sc, CONTROL0, reg); + + return (mv_thermal_wait_sensor(sc)); +} + +static int +mv_thermal_read_sensor(struct mv_thermal_softc *sc, int sensor, int *temp) +{ + uint32_t reg; + int64_t sample, rv; + + rv = mv_thermal_select_sensor(sc, sensor); + if (rv != 0) + return (rv); + + reg = RD_STA(sc, STATUS) & STATUS_TEMP_MASK; + + if (sc->config->signed_value) + sample = sign_extend(reg, fls(STATUS_TEMP_MASK) - 1); + else + sample = reg; + + *temp = ((sample * sc->config->calib_mul) - sc->config->calib_add) / + sc->config->calib_div; + + return (0); +} + +static int +ap806_init(struct mv_thermal_softc *sc) +{ + uint32_t reg; + + /* Start the temp capture/conversion */ + reg = RD_CON(sc, CONTROL0); + reg &= ~CONTROL0_TSEN_RESET; + reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN; + + /* Sample every ~2ms */ + reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT; + + WR_CON(sc, CONTROL0, reg); + + /* Since we just started the module wait for the sensor to be ready */ + mv_thermal_wait_sensor(sc); + + return (0); +} + +static int +cp110_init(struct mv_thermal_softc *sc) +{ + uint32_t reg; + + reg = RD_CON(sc, CONTROL1); + reg &= (1 << 7); + reg |= (1 << 8); + WR_CON(sc, CONTROL1, reg); + + /* Sample every ~2ms */ + reg = RD_CON(sc, CONTROL0); + reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT; + WR_CON(sc, CONTROL0, reg); + + return (0); +} + +static int +mv_thermal_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct mv_thermal_softc *sc; + device_t dev = arg1; + int sensor = arg2; + int val = 0; + + sc = device_get_softc(dev); + mtx_lock(&(sc)->mtx); + + if (mv_thermal_read_sensor(sc, sensor, &val) == 0) { + /* Convert to Kelvin */ + val = val + 2732; + } else { + device_printf(dev, "Timeout waiting for sensor\n"); + } + + mtx_unlock(&(sc)->mtx); + return sysctl_handle_opaque(oidp, &val, sizeof(val), req); +} + +static int +mv_thermal_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell Thermal Sensor Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_thermal_attach(device_t dev) +{ + struct mv_thermal_softc *sc; + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *oid; + char name[255]; + char desc[255]; + int i; + + sc = device_get_softc(dev); + sc->dev = dev; + + sc->config = (struct mv_thermal_config *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + if (bus_alloc_resources(dev, mv_thermal_res_spec, sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + sc->cur_sensor = -1; + switch (sc->config->type) { + case MV_AP806: + ap806_init(sc); + break; + case MV_CP110: + cp110_init(sc); + break; + } + + ctx = device_get_sysctl_ctx(dev); + oid = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + /* There is always at least one sensor */ + SYSCTL_ADD_PROC(ctx, oid, OID_AUTO, "internal", + CTLTYPE_INT | CTLFLAG_RD, + dev, 0, mv_thermal_sysctl, + "IK", + "Internal Temperature"); + + for (i = 0; i < sc->config->ncpus; i++) { + snprintf(name, sizeof(name), "cpu%d", i); + snprintf(desc, sizeof(desc), "CPU%d Temperature", i); + SYSCTL_ADD_PROC(ctx, oid, OID_AUTO, name, + CTLTYPE_INT | CTLFLAG_RD, + dev, i + 1, mv_thermal_sysctl, + "IK", + desc); + } + + return (0); +} + +static int +mv_thermal_detach(device_t dev) +{ + struct mv_thermal_softc *sc; + + sc = device_get_softc(dev); + + bus_release_resources(dev, mv_thermal_res_spec, sc->res); + + return (0); +} + +static device_method_t mv_thermal_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_thermal_probe), + DEVMETHOD(device_attach, mv_thermal_attach), + DEVMETHOD(device_detach, mv_thermal_detach), + + DEVMETHOD_END +}; + +static devclass_t mv_thermal_devclass; + +static driver_t mv_thermal_driver = { + "mv_thermal", + mv_thermal_methods, + sizeof(struct mv_thermal_softc), +}; + +DRIVER_MODULE(mv_thermal, simplebus, mv_thermal_driver, + mv_thermal_devclass, 0, 0); diff --git a/sys/arm/mv/mvebu_pinctrl.c b/sys/arm/mv/mvebu_pinctrl.c new file mode 100644 index 00000000000..3b183dc8767 --- /dev/null +++ b/sys/arm/mv/mvebu_pinctrl.c @@ -0,0 +1,246 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include "opt_soc.h" + +#define PINS_PER_REG 8 +#define BITS_PER_PIN 4 +#define PINS_MASK 0xf +#define MAX_PIN_FUNC 5 + +struct mv_pins { + const char *name; + const char *functions[MAX_PIN_FUNC]; +}; + +struct mv_padconf { + const struct mv_pins *pins; + size_t npins; +}; + +#ifdef SOC_MARVELL_8K +const static struct mv_pins ap806_pins[] = { + {"mpp0", {"gpio", "sdio", NULL, "spi0"}}, + {"mpp1", {"gpio", "sdio", NULL, "spi0"}}, + {"mpp2", {"gpio", "sdio", NULL, "spi0"}}, + {"mpp3", {"gpio", "sdio", NULL, "spi0"}}, + {"mpp4", {"gpio", "sdio", NULL, "i2c0"}}, + {"mpp5", {"gpio", "sdio", NULL, "i2c0"}}, + {"mpp6", {"gpio", "sdio", NULL, NULL}}, + {"mpp7", {"gpio", "sdio", NULL, "uart1"}}, + {"mpp8", {"gpio", "sdio", NULL, "uart1"}}, + {"mpp9", {"gpio", "sdio", NULL, "spi0"}}, + {"mpp10", {"gpio", "sdio", NULL, NULL}}, + {"mpp11", {"gpio", NULL, NULL, "uart0"}}, + {"mpp12", {"gpio", "sdio", "sdio", NULL}}, + {"mpp13", {"gpio", NULL, NULL}}, + {"mpp14", {"gpio", NULL, NULL}}, + {"mpp15", {"gpio", NULL, NULL}}, + {"mpp16", {"gpio", NULL, NULL}}, + {"mpp17", {"gpio", NULL, NULL}}, + {"mpp18", {"gpio", NULL, NULL}}, + {"mpp19", {"gpio", NULL, NULL, "uart0", "sdio"}}, +}; + +const struct mv_padconf ap806_padconf = { + .npins = nitems(ap806_pins), + .pins = ap806_pins, +}; +#endif + +struct mv_pinctrl_softc { + device_t dev; + struct resource *res; + + struct mv_padconf *padconf; +}; + +static struct resource_spec mv_pinctrl_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0 } +}; + +static struct ofw_compat_data compat_data[] = { +#ifdef SOC_MARVELL_8K + {"marvell,ap806-pinctrl", (uintptr_t)&ap806_padconf}, +#endif + {NULL, 0} +}; + +#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +static void +mv_pinctrl_configure_pin(struct mv_pinctrl_softc *sc, uint32_t pin, + uint32_t function) +{ + uint32_t offset, shift, reg; + + offset = (pin / PINS_PER_REG) * BITS_PER_PIN; + shift = (pin % PINS_PER_REG) * BITS_PER_PIN; + reg = RD4(sc, offset); + reg &= ~(PINS_MASK << shift); + reg |= function << shift; + WR4(sc, offset, reg); +} + +static int +mv_pinctrl_configure_pins(device_t dev, phandle_t cfgxref) +{ + struct mv_pinctrl_softc *sc; + phandle_t node; + char *function; + const char **pins; + int i, pin_num, pin_func, npins; + + sc = device_get_softc(dev); + node = OF_node_from_xref(cfgxref); + + if (OF_getprop_alloc(node, "marvell,function", + (void **)&function) == -1) + return (ENOMEM); + + npins = ofw_bus_string_list_to_array(node, "marvell,pins", &pins); + if (npins == -1) + return (ENOMEM); + + for (i = 0; i < npins; i++) { + for (pin_num = 0; pin_num < sc->padconf->npins; pin_num++) { + if (strcmp(pins[i], sc->padconf->pins[pin_num].name) == 0) + break; + } + if (pin_num == sc->padconf->npins) + continue; + + for (pin_func = 0; pin_func < MAX_PIN_FUNC; pin_func++) + if (sc->padconf->pins[pin_num].functions[pin_func] && + strcmp(function, sc->padconf->pins[pin_num].functions[pin_func]) == 0) + break; + + if (pin_func == MAX_PIN_FUNC) + continue; + + mv_pinctrl_configure_pin(sc, pin_num, pin_func); + } + + OF_prop_free(pins); + + return (0); +} + +static int +mv_pinctrl_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell Pinctrl controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_pinctrl_attach(device_t dev) +{ + struct mv_pinctrl_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->padconf = (struct mv_padconf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + if (bus_alloc_resources(dev, mv_pinctrl_res_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + + fdt_pinctrl_register(dev, "marvell,pins"); + fdt_pinctrl_configure_tree(dev); + + return (0); +} + +static int +mv_pinctrl_detach(device_t dev) +{ + + return (EBUSY); +} + +static device_method_t mv_pinctrl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_pinctrl_probe), + DEVMETHOD(device_attach, mv_pinctrl_attach), + DEVMETHOD(device_detach, mv_pinctrl_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,mv_pinctrl_configure_pins), + + DEVMETHOD_END +}; + +static devclass_t mv_pinctrl_devclass; + +static driver_t mv_pinctrl_driver = { + "mv_pinctrl", + mv_pinctrl_methods, + sizeof(struct mv_pinctrl_softc), +}; + +EARLY_DRIVER_MODULE(mv_pinctrl, simplebus, mv_pinctrl_driver, + mv_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC index ba4005b0b4d..5881bca80c4 100644 --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -93,6 +93,7 @@ options SOC_ALLWINNER_H5 options SOC_CAVM_THUNDERX options SOC_HISI_HI6220 options SOC_BRCM_BCM2837 +options SOC_MARVELL_8K options SOC_ROCKCHIP_RK3328 options SOC_XILINX_ZYNQ @@ -191,11 +192,13 @@ device muge device smcphy device smsc -# GPIO +# GPIO / PINCTRL device aw_gpio # Allwinner GPIO controller device gpio device gpioled device fdt_pinctrl +device mv_gpio # Marvell GPIO controller +device mvebu_pinctrl # Marvell Pinmux Controller # I2C device aw_rsb # Allwinner Reduced Serial Bus @@ -210,6 +213,8 @@ device aw_ccu # Allwinner clock controller # Interrupt controllers device aw_nmi # Allwinner NMI support +device mv_cp110_icu # Marvell CP110 ICU +device mv_ap806_gicp # Marvell AP806 GICP # Real-time clock support device aw_rtc # Allwinner Real-time Clock @@ -226,6 +231,7 @@ device aw_sid # Allwinner Secure ID EFUSE # Thermal sensors device aw_thermal # Allwinner Thermal Sensor Controller +device mv_thermal # Marvell Thermal Sensor Controller # SPI device spibus diff --git a/sys/conf/files b/sys/conf/files index 1d1b0e0a217..2e7af9e7b55 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1782,6 +1782,7 @@ dev/fdt/fdt_slicer.c optional fdt cfi | fdt nand | fdt mx25l | fdt n25q | fdt a dev/fdt/fdt_static_dtb.S optional fdt fdt_dtb_static \ dependency "${FDT_DTS_FILE:T:R}.dtb" dev/fdt/simplebus.c optional fdt +dev/fdt/simple_mfd.c optional fdt dev/fe/if_fe.c optional fe dev/fe/if_fe_pccard.c optional fe pccard dev/filemon/filemon.c optional filemon diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index ba8a792c116..a23aa07ae83 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -90,6 +90,13 @@ arm/broadcom/bcm2835/bcm2835_vcio.c optional soc_brcm_bcm2837 fdt arm/broadcom/bcm2835/bcm2835_wdog.c optional soc_brcm_bcm2837 fdt arm/broadcom/bcm2835/bcm2836.c optional soc_brcm_bcm2837 fdt arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt soc_brcm_bcm2837 +arm/mv/gpio.c optional mv_gpio fdt +arm/mv/mvebu_pinctrl.c optional mvebu_pinctrl fdt +arm/mv/mv_cp110_icu.c optional mv_cp110_icu fdt +arm/mv/mv_ap806_gicp.c optional mv_ap806_gicp fdt +arm/mv/mv_ap806_clock.c optional SOC_MARVELL_8K fdt +arm/mv/mv_cp110_clock.c optional SOC_MARVELL_8K fdt +arm/mv/mv_thermal.c optional SOC_MARVELL_8K mv_thermal fdt arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt arm/xilinx/uart_dev_cdnc.c optional uart soc_xilinx_zynq arm64/acpica/acpi_machdep.c optional acpi @@ -197,6 +204,7 @@ dev/axgbe/xgbe-dev.c optional axgbe dev/axgbe/xgbe-drv.c optional axgbe dev/axgbe/xgbe-mdio.c optional axgbe dev/cpufreq/cpufreq_dt.c optional cpufreq fdt +dev/iicbus/twsi/mv_twsi.c optional twsi fdt dev/iicbus/twsi/a10_twsi.c optional twsi fdt dev/iicbus/twsi/twsi.c optional twsi fdt dev/hwpmc/hwpmc_arm64.c optional hwpmc diff --git a/sys/conf/options.arm64 b/sys/conf/options.arm64 index 8abe09fae99..62b6408776f 100644 --- a/sys/conf/options.arm64 +++ b/sys/conf/options.arm64 @@ -19,5 +19,6 @@ SOC_ALLWINNER_H5 opt_soc.h SOC_BRCM_BCM2837 opt_soc.h SOC_CAVM_THUNDERX opt_soc.h SOC_HISI_HI6220 opt_soc.h +SOC_MARVELL_8K opt_soc.h SOC_ROCKCHIP_RK3328 opt_soc.h SOC_XILINX_ZYNQ opt_soc.h diff --git a/sys/dev/fdt/simple_mfd.c b/sys/dev/fdt/simple_mfd.c new file mode 100644 index 00000000000..0219c4637f4 --- /dev/null +++ b/sys/dev/fdt/simple_mfd.c @@ -0,0 +1,176 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) + * + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +struct simple_mfd_softc { + struct simplebus_softc sc; +}; + +device_t simple_mfd_add_device(device_t dev, phandle_t node, u_int order, + const char *name, int unit, struct simplebus_devinfo *di); +struct simplebus_devinfo *simple_mfd_setup_dinfo(device_t dev, phandle_t node, struct simplebus_devinfo *di); + +static int +simple_mfd_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "simple-mfd")) + return (ENXIO); + + device_set_desc(dev, "Simple MFD (Multi-Functions Device)"); + + return (BUS_PROBE_GENERIC); +} + +static int +simple_mfd_attach(device_t dev) +{ + struct simple_mfd_softc *sc; + phandle_t node, child; + device_t cdev; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + /* Parse address-cells and size-cells from the parent node as a fallback */ + if (OF_getencprop(node, "#address-cells", &sc->sc.acells, + sizeof(sc->sc.acells)) == -1) { + if (OF_getencprop(OF_parent(node), "#address-cells", &sc->sc.acells, + sizeof(sc->sc.acells)) == -1) { + sc->sc.acells = 2; + } + } + if (OF_getencprop(node, "#size-cells", &sc->sc.scells, + sizeof(sc->sc.scells)) == -1) { + if (OF_getencprop(OF_parent(node), "#size-cells", &sc->sc.scells, + sizeof(sc->sc.scells)) == -1) { + sc->sc.scells = 1; + } + } + + /* If the node has a ranges prop, parse it so children mapping will be done correctly */ + if (OF_hasprop(node, "ranges")) { + if (simplebus_fill_ranges(node, &sc->sc) < 0) { + device_printf(dev, "could not get ranges\n"); + return (ENXIO); + } + } + + /* Attach child devices */ + for (child = OF_child(node); child > 0; child = OF_peer(child)) { + cdev = simple_mfd_add_device(dev, child, 0, NULL, -1, NULL); + if (cdev != NULL) + device_probe_and_attach(cdev); + } + + return (bus_generic_attach(dev)); +} + +struct simplebus_devinfo * +simple_mfd_setup_dinfo(device_t dev, phandle_t node, + struct simplebus_devinfo *di) +{ + struct simplebus_softc *sc; + struct simplebus_devinfo *ndi; + + sc = device_get_softc(dev); + if (di == NULL) + ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); + else + ndi = di; + if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { + if (di == NULL) + free(ndi, M_DEVBUF); + return (NULL); + } + + /* reg resources is from the parent but interrupts is on the node itself */ + resource_list_init(&ndi->rl); + ofw_bus_reg_to_rl(dev, OF_parent(node), sc->acells, sc->scells, &ndi->rl); + ofw_bus_intr_to_rl(dev, node, &ndi->rl, NULL); + + return (ndi); +} + +device_t +simple_mfd_add_device(device_t dev, phandle_t node, u_int order, + const char *name, int unit, struct simplebus_devinfo *di) +{ + struct simplebus_devinfo *ndi; + device_t cdev; + + if ((ndi = simple_mfd_setup_dinfo(dev, node, di)) == NULL) + return (NULL); + cdev = device_add_child_ordered(dev, order, name, unit); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + ndi->obdinfo.obd_name); + resource_list_free(&ndi->rl); + ofw_bus_gen_destroy_devinfo(&ndi->obdinfo); + if (di == NULL) + free(ndi, M_DEVBUF); + return (NULL); + } + device_set_ivars(cdev, ndi); + + return(cdev); +} + +static device_method_t simple_mfd_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, simple_mfd_probe), + DEVMETHOD(device_attach, simple_mfd_attach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(simple_mfd, simple_mfd_driver, simple_mfd_methods, + sizeof(struct simple_mfd_softc), simplebus_driver); + +static devclass_t simple_mfd_devclass; + +EARLY_DRIVER_MODULE(simple_mfd, simplebus, simple_mfd_driver, + simple_mfd_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LATE); +MODULE_VERSION(simple_mfd, 1); diff --git a/sys/dev/fdt/simplebus.c b/sys/dev/fdt/simplebus.c index 23f3d2c8378..2f4fa860862 100644 --- a/sys/dev/fdt/simplebus.c +++ b/sys/dev/fdt/simplebus.c @@ -61,13 +61,6 @@ static struct resource_list *simplebus_get_resource_list(device_t bus, static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, device_t child); -/* - * local methods - */ - -static int simplebus_fill_ranges(phandle_t node, - struct simplebus_softc *sc); - /* * Driver methods. */ @@ -184,7 +177,7 @@ simplebus_init(device_t dev, phandle_t node) OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); } -static int +int simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) { int host_address_cells; diff --git a/sys/dev/fdt/simplebus.h b/sys/dev/fdt/simplebus.h index caccf162da8..bc695578e87 100644 --- a/sys/dev/fdt/simplebus.h +++ b/sys/dev/fdt/simplebus.h @@ -61,4 +61,6 @@ device_t simplebus_add_device(device_t dev, phandle_t node, u_int order, const char *name, int unit, struct simplebus_devinfo *di); struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev, phandle_t node, struct simplebus_devinfo *di); +int simplebus_fill_ranges(phandle_t node, + struct simplebus_softc *sc); #endif /* _FDT_SIMPLEBUS_H */ diff --git a/sys/dev/iicbus/twsi/mv_twsi.c b/sys/dev/iicbus/twsi/mv_twsi.c index 39d6ca74279..97ed63524b9 100644 --- a/sys/dev/iicbus/twsi/mv_twsi.c +++ b/sys/dev/iicbus/twsi/mv_twsi.c @@ -61,6 +61,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #include #include #include @@ -98,6 +100,7 @@ static int mv_twsi_attach(device_t); static struct ofw_compat_data compat_data[] = { { "mrvl,twsi", true }, { "marvell,mv64xxx-i2c", true }, + { "marvell,mv78230-i2c", true }, { NULL, false } }; @@ -142,29 +145,27 @@ mv_twsi_probe(device_t dev) if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); - sc->reg_data = TWSI_DATA; - sc->reg_slave_addr = TWSI_ADDR; - sc->reg_slave_ext_addr = TWSI_XADDR; - sc->reg_control = TWSI_CNTR; - sc->reg_status = TWSI_STAT; - sc->reg_baud_rate = TWSI_BAUD_RATE; - sc->reg_soft_reset = TWSI_SRST; - device_set_desc(dev, "Marvell Integrated I2C Bus Controller"); return (BUS_PROBE_DEFAULT); } #define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a)) static void -mv_twsi_cal_baud_rate(const uint32_t target, struct twsi_baud_rate *rate) +mv_twsi_cal_baud_rate(struct twsi_softc *sc, const uint32_t target, + struct twsi_baud_rate *rate) { - uint32_t clk, cur, diff, diff0; + uint64_t clk; + uint32_t cur, diff, diff0; int m, n, m0, n0; /* Calculate baud rate. */ m0 = n0 = 4; /* Default values on reset */ diff0 = 0xffffffff; +#ifdef __aarch64__ + clk_get_freq(sc->clk_core, &clk); +#else clk = get_tclk(); +#endif for (n = 0; n < 8; n++) { for (m = 0; m < 16; m++) { @@ -187,17 +188,37 @@ static int mv_twsi_attach(device_t dev) { struct twsi_softc *sc; - phandle_t child, iicbusnode; - device_t childdev; - struct iicbus_ivar *devi; - char dname[32]; /* 32 is taken from struct u_device */ - uint32_t paddr; - int len, error, ret; +#ifdef __aarch64__ + int error; +#endif sc = device_get_softc(dev); + sc->dev = dev; + +#ifdef __aarch64__ + /* Activate clock */ + error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_core); + if (error != 0) { + device_printf(dev, "could not find core clock\n"); + return (error); + } + error = clk_enable(sc->clk_core); + if (error != 0) { + device_printf(dev, "could not enable core clock\n"); + return (error); + } + + if (clk_get_by_ofw_index(dev, 0, 1, &sc->clk_reg) == 0) { + error = clk_enable(sc->clk_reg); + if (error != 0) { + device_printf(dev, "could not enable core clock\n"); + return (error); + } + } +#endif - mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]); - mv_twsi_cal_baud_rate(TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]); + mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]); + mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]); if (bootverbose) device_printf(dev, "calculated baud rates are:\n" " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n" @@ -209,56 +230,13 @@ mv_twsi_attach(device_t dev) sc->baud_rate[IIC_FAST].m, sc->baud_rate[IIC_FAST].n); + sc->reg_data = TWSI_DATA; + sc->reg_slave_addr = TWSI_ADDR; + sc->reg_slave_ext_addr = TWSI_XADDR; + sc->reg_control = TWSI_CNTR; + sc->reg_status = TWSI_STAT; + sc->reg_baud_rate = TWSI_BAUD_RATE; + sc->reg_soft_reset = TWSI_SRST; - ret = twsi_attach(dev); - if (ret != 0) - return (ret); - - iicbusnode = 0; - /* Find iicbus as the child devices in the device tree. */ - for (child = OF_child(ofw_bus_get_node(dev)); child != 0; - child = OF_peer(child)) { - len = OF_getproplen(child, "model"); - if (len <= 0 || len > sizeof(dname)) - continue; - error = OF_getprop(child, "model", &dname, len); - if (error == -1) - continue; - len = strlen(dname); - if (len == strlen(IICBUS_DEVNAME) && - strncasecmp(dname, IICBUS_DEVNAME, len) == 0) { - iicbusnode = child; - break; - } - } - if (iicbusnode == 0) - goto attach_end; - - /* Attach child devices onto iicbus. */ - for (child = OF_child(iicbusnode); child != 0; child = OF_peer(child)) { - /* Get slave address. */ - error = OF_getencprop(child, "i2c-address", &paddr, sizeof(paddr)); - if (error == -1) - error = OF_getencprop(child, "reg", &paddr, sizeof(paddr)); - if (error == -1) - continue; - - /* Get device driver name. */ - len = OF_getproplen(child, "model"); - if (len <= 0 || len > sizeof(dname)) - continue; - OF_getprop(child, "model", &dname, len); - - if (bootverbose) - device_printf(dev, "adding a device %s at %d.\n", - dname, paddr); - childdev = BUS_ADD_CHILD(sc->iicbus, 0, dname, -1); - devi = IICBUS_IVAR(childdev); - devi->addr = paddr; - } - -attach_end: - bus_generic_attach(sc->iicbus); - - return (0); + return (twsi_attach(dev)); } diff --git a/sys/dev/iicbus/twsi/twsi.h b/sys/dev/iicbus/twsi/twsi.h index 44dce631f38..dd3873e249c 100644 --- a/sys/dev/iicbus/twsi/twsi.h +++ b/sys/dev/iicbus/twsi/twsi.h @@ -34,6 +34,8 @@ #ifndef _TWSI_H_ #define _TWSI_H_ +#include + struct twsi_baud_rate { uint32_t raw; int param; @@ -48,6 +50,8 @@ struct twsi_softc { device_t iicbus; void * intrhand; bool have_intr; + clk_t clk_core; + clk_t clk_reg; struct iic_msg *msg; uint16_t sent_bytes; diff --git a/sys/dev/sdhci/sdhci_xenon.c b/sys/dev/sdhci/sdhci_xenon.c index a335a7b9b9a..4f11be2cf3a 100644 --- a/sys/dev/sdhci/sdhci_xenon.c +++ b/sys/dev/sdhci/sdhci_xenon.c @@ -62,11 +62,16 @@ __FBSDID("$FreeBSD$"); #include "sdhci_if.h" #include "opt_mmccam.h" +#include "opt_soc.h" #define MAX_SLOTS 6 static struct ofw_compat_data compat_data[] = { { "marvell,armada-3700-sdhci", 1 }, +#ifdef SOC_MARVELL_8K + { "marvell,armada-cp110-sdhci", 1 }, + { "marvell,armada-ap806-sdhci", 1 }, +#endif { NULL, 0 } }; -- 2.45.0