svn commit: r351139 - in stable/12/sys: arm/mv arm64/conf conf dev/sdhci
Emmanuel Vadot
manu at FreeBSD.org
Fri Aug 16 20:49:11 UTC 2019
Author: manu
Date: Fri Aug 16 20:49:10 2019
New Revision: 351139
URL: https://svnweb.freebsd.org/changeset/base/351139
Log:
MFC r348880, r348882
r348880 by loos:
Add the GPIO driver for the North/South bridge in Marvell Armada 37x0.
The A3700 has a different GPIO controller and thus, do not use the old (and
shared) code for Marvell.
The pinctrl driver, also part of the controller, is not supported yet (but
the implementation should be straightforward).
Sponsored by: Rubicon Communications, LLC (Netgate)
r348882 by loos:
Add support for the GPIO SD Card VCC regulator/switch and the GPIO SD Card
detection pins to the Marvell Xenon SDHCI controller.
These features are enable by 'vqmmc-supply' and 'cd-gpios' properties in the
DTS.
This fixes the SD Card detection on espressobin.
Sponsored by: Rubicon Communications, LLC (Netgate)
Added:
stable/12/sys/arm/mv/a37x0_gpio.c
- copied unchanged from r348880, head/sys/arm/mv/a37x0_gpio.c
Modified:
stable/12/sys/arm64/conf/GENERIC
stable/12/sys/conf/files.arm64
stable/12/sys/dev/sdhci/sdhci_xenon.c
Directory Properties:
stable/12/ (props changed)
Copied: stable/12/sys/arm/mv/a37x0_gpio.c (from r348880, head/sys/arm/mv/a37x0_gpio.c)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/12/sys/arm/mv/a37x0_gpio.c Fri Aug 16 20:49:10 2019 (r351139, copy of r348880, head/sys/arm/mv/a37x0_gpio.c)
@@ -0,0 +1,352 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2018-2019, Rubicon Communications, LLC (Netgate)
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/gpio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "gpio_if.h"
+
+static struct resource_spec a37x0_gpio_res_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Pinctl / GPIO */
+ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Interrupts control */
+ { -1, 0, 0 }
+};
+
+struct a37x0_gpio_softc {
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ device_t sc_busdev;
+ int sc_type;
+ uint32_t sc_max_pins;
+ uint32_t sc_npins;
+ struct resource *sc_mem_res[nitems(a37x0_gpio_res_spec) - 1];
+};
+
+/* Memory regions. */
+#define A37X0_GPIO 0
+#define A37X0_INTR 1
+
+/* North Bridge / South Bridge. */
+#define A37X0_NB_GPIO 1
+#define A37X0_SB_GPIO 2
+
+#define A37X0_GPIO_WRITE(_sc, _off, _val) \
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
+#define A37X0_GPIO_READ(_sc, _off) \
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
+
+#define A37X0_GPIO_BIT(_p) (1U << ((_p) % 32))
+#define A37X0_GPIO_OUT_EN(_p) (0x0 + ((_p) / 32) * 4)
+#define A37X0_GPIO_LATCH(_p) (0x8 + ((_p) / 32) * 4)
+#define A37X0_GPIO_INPUT(_p) (0x10 + ((_p) / 32) * 4)
+#define A37X0_GPIO_OUTPUT(_p) (0x18 + ((_p) / 32) * 4)
+#define A37X0_GPIO_SEL 0x30
+
+
+static struct ofw_compat_data compat_data[] = {
+ { "marvell,armada3710-nb-pinctrl", A37X0_NB_GPIO },
+ { "marvell,armada3710-sb-pinctrl", A37X0_SB_GPIO },
+ { NULL, 0 }
+};
+
+static phandle_t
+a37x0_gpio_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static device_t
+a37x0_gpio_get_bus(device_t dev)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->sc_busdev);
+}
+
+static int
+a37x0_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->sc_npins - 1;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+ snprintf(name, GPIOMAXNAME, "pin %d", pin);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+ *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if ((reg & A37X0_GPIO_BIT(pin)) != 0)
+ *flags = GPIO_PIN_OUTPUT;
+ else
+ *flags = GPIO_PIN_INPUT;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if (flags & GPIO_PIN_OUTPUT)
+ reg |= A37X0_GPIO_BIT(pin);
+ else
+ reg &= ~A37X0_GPIO_BIT(pin);
+ A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUT_EN(pin), reg);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if ((reg & A37X0_GPIO_BIT(pin)) != 0)
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
+ else
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_INPUT(pin));
+ *val = ((reg & A37X0_GPIO_BIT(pin)) != 0) ? 1 : 0;
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
+ if (val != 0)
+ reg |= A37X0_GPIO_BIT(pin);
+ else
+ reg &= ~A37X0_GPIO_BIT(pin);
+ A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUTPUT(pin), reg);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct a37x0_gpio_softc *sc;
+ uint32_t reg;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->sc_npins)
+ return (EINVAL);
+
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
+ if ((reg & A37X0_GPIO_BIT(pin)) == 0)
+ return (EINVAL);
+ reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
+ reg ^= A37X0_GPIO_BIT(pin);
+ A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUTPUT(pin), reg);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_probe(device_t dev)
+{
+ const char *desc;
+ struct a37x0_gpio_softc *sc;
+
+ if (!OF_hasprop(ofw_bus_get_node(dev), "gpio-controller"))
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+ sc->sc_type = ofw_bus_search_compatible(
+ device_get_parent(dev), compat_data)->ocd_data;
+ switch (sc->sc_type) {
+ case A37X0_NB_GPIO:
+ sc->sc_max_pins = 36;
+ desc = "Armada 37x0 North Bridge GPIO Controller";
+ break;
+ case A37X0_SB_GPIO:
+ sc->sc_max_pins = 30;
+ desc = "Armada 37x0 South Bridge GPIO Controller";
+ break;
+ default:
+ return (ENXIO);
+ }
+ device_set_desc(dev, desc);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+a37x0_gpio_attach(device_t dev)
+{
+ int err, ncells;
+ pcell_t *ranges;
+ struct a37x0_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ /* Read and verify the "gpio-ranges" property. */
+ ncells = OF_getencprop_alloc(ofw_bus_get_node(dev), "gpio-ranges",
+ (void **)&ranges);
+ if (ncells == -1)
+ return (ENXIO);
+ if (ncells != sizeof(*ranges) * 4 || ranges[1] != 0 || ranges[2] != 0) {
+ OF_prop_free(ranges);
+ return (ENXIO);
+ }
+ sc->sc_npins = ranges[3];
+ OF_prop_free(ranges);
+
+ /* Check the number of pins in the DTS vs HW capabilities. */
+ if (sc->sc_npins > sc->sc_max_pins)
+ return (ENXIO);
+
+ err = bus_alloc_resources(dev, a37x0_gpio_res_spec, sc->sc_mem_res);
+ if (err != 0) {
+ device_printf(dev, "cannot allocate memory window\n");
+ return (ENXIO);
+ }
+ sc->sc_bst = rman_get_bustag(sc->sc_mem_res[A37X0_GPIO]);
+ sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res[A37X0_GPIO]);
+
+ sc->sc_busdev = gpiobus_attach_bus(dev);
+ if (sc->sc_busdev == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+a37x0_gpio_detach(device_t dev)
+{
+
+ return (EBUSY);
+}
+
+static device_method_t a37x0_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, a37x0_gpio_probe),
+ DEVMETHOD(device_attach, a37x0_gpio_attach),
+ DEVMETHOD(device_detach, a37x0_gpio_detach),
+
+ /* GPIO interface */
+ DEVMETHOD(gpio_get_bus, a37x0_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, a37x0_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, a37x0_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getcaps, a37x0_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, a37x0_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, a37x0_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, a37x0_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, a37x0_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, a37x0_gpio_pin_toggle),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, a37x0_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static devclass_t a37x0_gpio_devclass;
+static driver_t a37x0_gpio_driver = {
+ "gpio",
+ a37x0_gpio_methods,
+ sizeof(struct a37x0_gpio_softc),
+};
+
+EARLY_DRIVER_MODULE(a37x0_gpio, simple_mfd, a37x0_gpio_driver,
+ a37x0_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);
Modified: stable/12/sys/arm64/conf/GENERIC
==============================================================================
--- stable/12/sys/arm64/conf/GENERIC Fri Aug 16 20:30:31 2019 (r351138)
+++ stable/12/sys/arm64/conf/GENERIC Fri Aug 16 20:49:10 2019 (r351139)
@@ -200,6 +200,7 @@ device a10_codec
device a31_dmac
# GPIO / PINCTRL
+device a37x0_gpio # Marvell Armada 37x0 GPIO controller
device aw_gpio # Allwinner GPIO controller
device gpio
device gpioled
Modified: stable/12/sys/conf/files.arm64
==============================================================================
--- stable/12/sys/conf/files.arm64 Fri Aug 16 20:30:31 2019 (r351138)
+++ stable/12/sys/conf/files.arm64 Fri Aug 16 20:49:10 2019 (r351139)
@@ -95,6 +95,7 @@ arm/broadcom/bcm2835/bcm2835_vcio.c optional soc_brcm
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/a37x0_gpio.c optional a37x0_gpio gpio fdt
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
Modified: stable/12/sys/dev/sdhci/sdhci_xenon.c
==============================================================================
--- stable/12/sys/dev/sdhci/sdhci_xenon.c Fri Aug 16 20:30:31 2019 (r351138)
+++ stable/12/sys/dev/sdhci/sdhci_xenon.c Fri Aug 16 20:49:10 2019 (r351139)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <machine/resource.h>
+#include <dev/extres/regulator/regulator.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
@@ -56,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <dev/mmc/mmcreg.h>
#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt_gpio.h>
#include <dev/sdhci/sdhci_xenon.h>
#include "mmcbr_if.h"
@@ -84,10 +86,12 @@ struct sdhci_xenon_softc {
uint32_t max_clk; /* Max possible freq */
struct resource *irq_res; /* IRQ resource */
void *intrhand; /* Interrupt handle */
+ struct sdhci_fdt_gpio *gpio; /* GPIO pins for CD detection. */
struct sdhci_slot *slot; /* SDHCI internal data */
struct resource *mem_res; /* Memory resource */
+ regulator_t reg_vqmmc; /* vqmmc-supply regulator */
uint8_t znr; /* PHY ZNR */
uint8_t zpr; /* PHY ZPR */
bool no_18v; /* No 1.8V support */
@@ -188,6 +192,14 @@ sdhci_xenon_get_ro(device_t bus, device_t dev)
return (sdhci_generic_get_ro(bus, dev) ^ sc->wp_inverted);
}
+static bool
+sdhci_xenon_get_card_present(device_t dev, struct sdhci_slot *slot)
+{
+ struct sdhci_xenon_softc *sc = device_get_softc(dev);
+
+ return (sdhci_fdt_gpio_get_present(sc->gpio));
+}
+
static int
sdhci_xenon_phy_init(device_t brdev, struct mmc_ios *ios)
{
@@ -337,6 +349,25 @@ sdhci_xenon_update_ios(device_t brdev, device_t reqdev
slot = device_get_ivars(reqdev);
ios = &slot->host.ios;
+ switch (ios->power_mode) {
+ case power_on:
+ break;
+ case power_off:
+ if (bootverbose)
+ device_printf(sc->dev, "Powering down sd/mmc\n");
+
+ if (sc->reg_vqmmc)
+ regulator_disable(sc->reg_vqmmc);
+ break;
+ case power_up:
+ if (bootverbose)
+ device_printf(sc->dev, "Powering up sd/mmc\n");
+
+ if (sc->reg_vqmmc)
+ regulator_enable(sc->reg_vqmmc);
+ break;
+ };
+
/* Update the PHY settings. */
if (ios->clock != 0)
sdhci_xenon_phy_set(brdev, ios);
@@ -352,6 +383,42 @@ sdhci_xenon_update_ios(device_t brdev, device_t reqdev
}
static int
+sdhci_xenon_switch_vccq(device_t brdev, device_t reqdev)
+{
+ struct sdhci_xenon_softc *sc;
+ struct sdhci_slot *slot;
+ int uvolt, err;
+
+ sc = device_get_softc(brdev);
+
+ if (sc->reg_vqmmc == NULL)
+ return EOPNOTSUPP;
+
+ slot = device_get_ivars(reqdev);
+ switch (slot->host.ios.vccq) {
+ case vccq_180:
+ uvolt = 1800000;
+ break;
+ case vccq_330:
+ uvolt = 3300000;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ err = regulator_set_voltage(sc->reg_vqmmc, uvolt, uvolt);
+ if (err != 0) {
+ device_printf(sc->dev,
+ "Cannot set vqmmc to %d<->%d\n",
+ uvolt,
+ uvolt);
+ return (err);
+ }
+
+ return (0);
+}
+
+static int
sdhci_xenon_probe(device_t dev)
{
struct sdhci_xenon_softc *sc = device_get_softc(dev);
@@ -389,6 +456,11 @@ sdhci_xenon_probe(device_t dev)
if ((OF_getencprop(sc->node, "marvell,xenon-phy-zpr", &cid,
sizeof(cid))) > 0)
sc->zpr = cid & XENON_ZPR_MASK;
+ if (regulator_get_by_ofw_property(dev, 0, "vqmmc-supply",
+ &sc->reg_vqmmc) == 0 && bootverbose) {
+ if (bootverbose)
+ device_printf(dev, "vqmmc-supply regulator found\n");
+ }
return (0);
}
@@ -437,6 +509,12 @@ sdhci_xenon_attach(device_t dev)
slot->max_clk = sc->max_clk;
sc->slot = slot;
+ /*
+ * Set up any gpio pin handling described in the FDT data. This cannot
+ * fail; see comments in sdhci_fdt_gpio.h for details.
+ */
+ sc->gpio = sdhci_fdt_gpio_setup(dev, slot);
+
if (sdhci_init_slot(dev, sc->slot, 0))
goto fail;
@@ -497,6 +575,9 @@ sdhci_xenon_detach(device_t dev)
{
struct sdhci_xenon_softc *sc = device_get_softc(dev);
+ if (sc->gpio != NULL)
+ sdhci_fdt_gpio_teardown(sc->gpio);
+
bus_generic_detach(dev);
bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res),
@@ -526,6 +607,7 @@ static device_method_t sdhci_xenon_methods[] = {
DEVMETHOD(mmcbr_get_ro, sdhci_xenon_get_ro),
DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+ DEVMETHOD(mmcbr_switch_vccq, sdhci_xenon_switch_vccq),
/* SDHCI registers accessors */
DEVMETHOD(sdhci_read_1, sdhci_xenon_read_1),
@@ -536,6 +618,7 @@ static device_method_t sdhci_xenon_methods[] = {
DEVMETHOD(sdhci_write_2, sdhci_xenon_write_2),
DEVMETHOD(sdhci_write_4, sdhci_xenon_write_4),
DEVMETHOD(sdhci_write_multi_4, sdhci_xenon_write_multi_4),
+ DEVMETHOD(sdhci_get_card_present, sdhci_xenon_get_card_present),
DEVMETHOD_END
};
More information about the svn-src-stable-12
mailing list