svn commit: r304794 - head/sys/arm/allwinner

Jared McNeill jmcneill at FreeBSD.org
Thu Aug 25 10:20:28 UTC 2016


Author: jmcneill
Date: Thu Aug 25 10:20:27 2016
New Revision: 304794
URL: https://svnweb.freebsd.org/changeset/base/304794

Log:
  Expose DC1SW as a regulator switch. On Pine64 this is used to control EMAC
  PHY power.
  
  Reviewed by:	andrew, manu

Modified:
  head/sys/arm/allwinner/axp81x.c

Modified: head/sys/arm/allwinner/axp81x.c
==============================================================================
--- head/sys/arm/allwinner/axp81x.c	Thu Aug 25 10:14:56 2016	(r304793)
+++ head/sys/arm/allwinner/axp81x.c	Thu Aug 25 10:20:27 2016	(r304794)
@@ -52,10 +52,17 @@ __FBSDID("$FreeBSD$");
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
-#include "iicbus_if.h"
+#include <dev/extres/regulator/regulator.h>
+
 #include "gpio_if.h"
+#include "iicbus_if.h"
+#include "regdev_if.h"
+
+MALLOC_DEFINE(M_AXP81X_REG, "AXP81x regulator", "AXP81x power regulator");
 
 #define	AXP_ICTYPE		0x03
+#define	AXP_POWERCTL2		0x12
+#define	 AXP_POWERCTL2_DC1SW	(1 << 7)
 #define	AXP_POWERBAT		0x32
 #define	 AXP_POWERBAT_SHUTDOWN	(1 << 7)
 #define	AXP_IRQEN1		0x40
@@ -96,6 +103,37 @@ static struct resource_spec axp81x_spec[
 	{ -1, 0 }
 };
 
+struct axp81x_regdef {
+	intptr_t		id;
+	char			*name;
+	char			*supply_name;
+	uint8_t			enable_reg;
+	uint8_t			enable_mask;
+};
+
+enum axp81x_reg_id {
+	AXP81X_REG_ID_DC1SW
+};
+
+static struct axp81x_regdef axp81x_regdefs[] = {
+	{
+		.id = AXP81X_REG_ID_DC1SW,
+		.name = "dc1sw",
+		.enable_reg = AXP_POWERCTL2,
+		.enable_mask = AXP_POWERCTL2_DC1SW,
+	},
+};
+
+struct axp81x_softc;
+
+struct axp81x_reg_sc {
+	struct regnode		*regnode;
+	device_t		base_dev;
+	struct axp81x_regdef	*def;
+	phandle_t		xref;
+	struct regnode_std_param *param;
+};
+
 struct axp81x_softc {
 	struct resource		*res;
 	uint16_t		addr;
@@ -103,6 +141,10 @@ struct axp81x_softc {
 	device_t		gpiodev;
 	struct mtx		mtx;
 	int			busy;
+
+	/* Regulators */
+	struct axp81x_reg_sc	**regs;
+	int			nregs;
 };
 
 #define	AXP_LOCK(sc)	mtx_lock(&(sc)->mtx)
@@ -150,6 +192,56 @@ axp81x_write(device_t dev, uint8_t reg, 
 	return (iicbus_transfer(dev, msg, 2));
 }
 
+static int
+axp81x_regnode_init(struct regnode *regnode)
+{
+	return (0);
+}
+
+static int
+axp81x_regnode_enable(struct regnode *regnode, bool enable, int *udelay)
+{
+	struct axp81x_reg_sc *sc;
+	uint8_t val;
+
+	sc = regnode_get_softc(regnode);
+
+	axp81x_read(sc->base_dev, sc->def->enable_reg, &val, 1);
+	if (enable)
+		val |= sc->def->enable_mask;
+	else
+		val &= ~sc->def->enable_mask;
+	axp81x_write(sc->base_dev, sc->def->enable_reg, val);
+
+	*udelay = 0;
+
+	return (0);
+}
+
+static int
+axp81x_regnode_set_voltage(struct regnode *regnode, int min_uvolt,
+    int max_uvolt, int *udelay)
+{
+	return (ENXIO);
+}
+
+static int
+axp81x_regnode_get_voltage(struct regnode *regnode, int *uvolt)
+{
+	return (ENXIO);
+}
+
+static regnode_method_t axp81x_regnode_methods[] = {
+	/* Regulator interface */
+	REGNODEMETHOD(regnode_init,		axp81x_regnode_init),
+	REGNODEMETHOD(regnode_enable,		axp81x_regnode_enable),
+	REGNODEMETHOD(regnode_set_voltage,	axp81x_regnode_set_voltage),
+	REGNODEMETHOD(regnode_get_voltage,	axp81x_regnode_get_voltage),
+	REGNODEMETHOD_END
+};
+DEFINE_CLASS_1(axp81x_regnode, axp81x_regnode_class, axp81x_regnode_methods,
+    sizeof(struct axp81x_reg_sc), regnode_class);
+
 static void
 axp81x_shutdown(void *devp, int howto)
 {
@@ -417,6 +509,56 @@ axp81x_get_node(device_t dev, device_t b
 	return (ofw_bus_get_node(dev));
 }
 
+static struct axp81x_reg_sc *
+axp81x_reg_attach(device_t dev, phandle_t node,
+    struct axp81x_regdef *def)
+{
+	struct axp81x_reg_sc *reg_sc;
+	struct regnode_init_def initdef;
+	struct regnode *regnode;
+
+	memset(&initdef, 0, sizeof(initdef));
+	regulator_parse_ofw_stdparam(dev, node, &initdef);
+	initdef.id = def->id;
+	initdef.ofw_node = node;
+	regnode = regnode_create(dev, &axp81x_regnode_class, &initdef);
+	if (regnode == NULL) {
+		device_printf(dev, "cannot create regulator\n");
+		return (NULL);
+	}
+
+	reg_sc = regnode_get_softc(regnode);
+	reg_sc->regnode = regnode;
+	reg_sc->base_dev = dev;
+	reg_sc->def = def;
+	reg_sc->xref = OF_xref_from_node(node);
+	reg_sc->param = regnode_get_stdparam(regnode);
+
+	regnode_register(regnode);
+
+	return (reg_sc);
+}
+
+static int
+axp81x_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells,
+    intptr_t *num)
+{
+	struct axp81x_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+	for (i = 0; i < sc->nregs; i++) {
+		if (sc->regs[i] == NULL)
+			continue;
+		if (sc->regs[i]->xref == xref) {
+			*num = sc->regs[i]->def->id;
+			return (0);
+		}
+	}
+
+	return (ENXIO);
+}
+
 static int
 axp81x_probe(device_t dev)
 {
@@ -435,8 +577,10 @@ static int
 axp81x_attach(device_t dev)
 {
 	struct axp81x_softc *sc;
+	struct axp81x_reg_sc *reg;
 	uint8_t chip_id;
-	int error;
+	phandle_t rnode, child;
+	int error, i;
 
 	sc = device_get_softc(dev);
 
@@ -454,6 +598,29 @@ axp81x_attach(device_t dev)
 		device_printf(dev, "chip ID 0x%02x\n", chip_id);
 	}
 
+	sc->nregs = nitems(axp81x_regdefs);
+	sc->regs = malloc(sizeof(struct axp81x_reg_sc *) * sc->nregs,
+	    M_AXP81X_REG, M_WAITOK | M_ZERO);
+
+	/* Attach known regulators that exist in the DT */
+	rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators");
+	if (rnode > 0) {
+		for (i = 0; i < sc->nregs; i++) {
+			child = ofw_bus_find_child(rnode,
+			    axp81x_regdefs[i].name);
+			if (child == 0)
+				continue;
+			reg = axp81x_reg_attach(dev, child, &axp81x_regdefs[i]);
+			if (reg == NULL) {
+				device_printf(dev,
+				    "cannot attach regulator %s\n",
+				    axp81x_regdefs[i].name);
+				return (ENXIO);
+			}
+			sc->regs[i] = reg;
+		}
+	}
+
 	/* Enable IRQ on short power key press */
 	axp81x_write(dev, AXP_IRQEN1, 0);
 	axp81x_write(dev, AXP_IRQEN2, 0);
@@ -495,6 +662,9 @@ static device_method_t axp81x_methods[] 
 	DEVMETHOD(gpio_pin_toggle,	axp81x_gpio_pin_toggle),
 	DEVMETHOD(gpio_map_gpios,	axp81x_gpio_map_gpios),
 
+	/* Regdev interface */
+	DEVMETHOD(regdev_map,		axp81x_regdev_map),
+
 	/* OFW bus interface */
 	DEVMETHOD(ofw_bus_get_node,	axp81x_get_node),
 
@@ -511,9 +681,10 @@ static devclass_t axp81x_devclass;
 extern devclass_t ofwgpiobus_devclass, gpioc_devclass;
 extern driver_t ofw_gpiobus_driver, gpioc_driver;
 
-DRIVER_MODULE(axp81x, iicbus, axp81x_driver, axp81x_devclass, 0, 0);
-DRIVER_MODULE(ofw_gpiobus, axp81x_pmu, ofw_gpiobus_driver,
-    ofwgpiobus_devclass, 0, 0);
+EARLY_DRIVER_MODULE(axp81x, iicbus, axp81x_driver, axp81x_devclass, 0, 0,
+    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
+EARLY_DRIVER_MODULE(ofw_gpiobus, axp81x_pmu, ofw_gpiobus_driver,
+    ofwgpiobus_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
 DRIVER_MODULE(gpioc, axp81x_pmu, gpioc_driver, gpioc_devclass, 0, 0);
 MODULE_VERSION(axp81x, 1);
 MODULE_DEPEND(axp81x, iicbus, 1, 1, 1);


More information about the svn-src-head mailing list