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);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to