The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/allwinner/aw_usbphy.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  *
   25  * $FreeBSD$
   26  */
   27 
   28 /*
   29  * Allwinner USB PHY
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/rman.h>
   39 #include <sys/kernel.h>
   40 #include <sys/module.h>
   41 #include <sys/gpio.h>
   42 #include <machine/bus.h>
   43 
   44 #include <dev/ofw/ofw_bus.h>
   45 #include <dev/ofw/ofw_bus_subr.h>
   46 #include <dev/gpio/gpiobusvar.h>
   47 
   48 #include <dev/extres/clk/clk.h>
   49 #include <dev/extres/hwreset/hwreset.h>
   50 #include <dev/extres/regulator/regulator.h>
   51 #include <dev/extres/phy/phy_usb.h>
   52 
   53 #include "phynode_if.h"
   54 
   55 enum awusbphy_type {
   56         AWUSBPHY_TYPE_A10 = 1,
   57         AWUSBPHY_TYPE_A13,
   58         AWUSBPHY_TYPE_A20,
   59         AWUSBPHY_TYPE_A31,
   60         AWUSBPHY_TYPE_H3,
   61         AWUSBPHY_TYPE_A64,
   62         AWUSBPHY_TYPE_A83T,
   63         AWUSBPHY_TYPE_H6,
   64 };
   65 
   66 struct aw_usbphy_conf {
   67         int                     num_phys;
   68         enum awusbphy_type      phy_type;
   69         bool                    pmu_unk1;
   70         bool                    phy0_route;
   71 };
   72 
   73 static const struct aw_usbphy_conf a10_usbphy_conf = {
   74         .num_phys = 3,
   75         .phy_type = AWUSBPHY_TYPE_A10,
   76         .pmu_unk1 = false,
   77         .phy0_route = false,
   78 };
   79 
   80 static const struct aw_usbphy_conf a13_usbphy_conf = {
   81         .num_phys = 2,
   82         .phy_type = AWUSBPHY_TYPE_A13,
   83         .pmu_unk1 = false,
   84         .phy0_route = false,
   85 };
   86 
   87 static const struct aw_usbphy_conf a20_usbphy_conf = {
   88         .num_phys = 3,
   89         .phy_type = AWUSBPHY_TYPE_A20,
   90         .pmu_unk1 = false,
   91         .phy0_route = false,
   92 };
   93 
   94 static const struct aw_usbphy_conf a31_usbphy_conf = {
   95         .num_phys = 3,
   96         .phy_type = AWUSBPHY_TYPE_A31,
   97         .pmu_unk1 = false,
   98         .phy0_route = false,
   99 };
  100 
  101 static const struct aw_usbphy_conf h3_usbphy_conf = {
  102         .num_phys = 4,
  103         .phy_type = AWUSBPHY_TYPE_H3,
  104         .pmu_unk1 = true,
  105         .phy0_route = true,
  106 };
  107 
  108 static const struct aw_usbphy_conf a64_usbphy_conf = {
  109         .num_phys = 2,
  110         .phy_type = AWUSBPHY_TYPE_A64,
  111         .pmu_unk1 = true,
  112         .phy0_route = true,
  113 };
  114 
  115 static const struct aw_usbphy_conf a83t_usbphy_conf = {
  116         .num_phys = 3,
  117         .phy_type = AWUSBPHY_TYPE_A83T,
  118         .pmu_unk1 = false,
  119         .phy0_route = false,
  120 };
  121 
  122 static const struct aw_usbphy_conf h6_usbphy_conf = {
  123         .num_phys = 4,
  124         .phy_type = AWUSBPHY_TYPE_H6,
  125         .pmu_unk1 = false,
  126         .phy0_route = true,
  127 };
  128 
  129 static struct ofw_compat_data compat_data[] = {
  130         { "allwinner,sun4i-a10-usb-phy",        (uintptr_t)&a10_usbphy_conf },
  131         { "allwinner,sun5i-a13-usb-phy",        (uintptr_t)&a13_usbphy_conf },
  132         { "allwinner,sun6i-a31-usb-phy",        (uintptr_t)&a31_usbphy_conf },
  133         { "allwinner,sun7i-a20-usb-phy",        (uintptr_t)&a20_usbphy_conf },
  134         { "allwinner,sun8i-h3-usb-phy",         (uintptr_t)&h3_usbphy_conf },
  135         { "allwinner,sun50i-a64-usb-phy",       (uintptr_t)&a64_usbphy_conf },
  136         { "allwinner,sun8i-a83t-usb-phy",       (uintptr_t)&a83t_usbphy_conf },
  137         { "allwinner,sun50i-h6-usb-phy",        (uintptr_t)&h6_usbphy_conf },
  138         { NULL,                                 0 }
  139 };
  140 
  141 struct awusbphy_softc {
  142         struct resource *       phy_ctrl;
  143         struct resource **      pmu;
  144         regulator_t *           reg;
  145         gpio_pin_t              id_det_pin;
  146         int                     id_det_valid;
  147         gpio_pin_t              vbus_det_pin;
  148         int                     vbus_det_valid;
  149         struct aw_usbphy_conf   *phy_conf;
  150         int                     mode;
  151 };
  152 
  153  /* Phy class and methods. */
  154 static int awusbphy_phy_enable(struct phynode *phy, bool enable);
  155 static int awusbphy_get_mode(struct phynode *phy, int *mode);
  156 static int awusbphy_set_mode(struct phynode *phy, int mode);
  157 static phynode_usb_method_t awusbphy_phynode_methods[] = {
  158         PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable),
  159         PHYNODEMETHOD(phynode_usb_get_mode, awusbphy_get_mode),
  160         PHYNODEMETHOD(phynode_usb_set_mode, awusbphy_set_mode),
  161 
  162         PHYNODEMETHOD_END
  163 };
  164 DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods,
  165   sizeof(struct phynode_usb_sc), phynode_usb_class);
  166 
  167 #define RD4(res, o)     bus_read_4(res, (o))
  168 #define WR4(res, o, v)  bus_write_4(res, (o), (v))
  169 #define CLR4(res, o, m) WR4(res, o, RD4(res, o) & ~(m))
  170 #define SET4(res, o, m) WR4(res, o, RD4(res, o) | (m))
  171 
  172 #define PHY_CSR         0x00
  173 #define  ID_PULLUP_EN           (1 << 17)
  174 #define  DPDM_PULLUP_EN         (1 << 16)
  175 #define  FORCE_ID               (0x3 << 14)
  176 #define  FORCE_ID_SHIFT         14
  177 #define  FORCE_ID_LOW           2
  178 #define  FORCE_ID_HIGH          3
  179 #define  FORCE_VBUS_VALID       (0x3 << 12)
  180 #define  FORCE_VBUS_VALID_SHIFT 12
  181 #define  FORCE_VBUS_VALID_LOW   2
  182 #define  FORCE_VBUS_VALID_HIGH  3
  183 #define  VBUS_CHANGE_DET        (1 << 6)
  184 #define  ID_CHANGE_DET          (1 << 5)
  185 #define  DPDM_CHANGE_DET        (1 << 4)
  186 #define OTG_PHY_CFG     0x20
  187 #define  OTG_PHY_ROUTE_OTG      (1 << 0)
  188 #define PMU_IRQ_ENABLE  0x00
  189 #define  PMU_AHB_INCR8          (1 << 10)
  190 #define  PMU_AHB_INCR4          (1 << 9)
  191 #define  PMU_AHB_INCRX_ALIGN    (1 << 8)
  192 #define  PMU_ULPI_BYPASS        (1 << 0)
  193 #define PMU_UNK_H3      0x10
  194 #define  PMU_UNK_H3_CLR         0x2
  195 
  196 static void
  197 awusbphy_configure(device_t dev, int phyno)
  198 {
  199         struct awusbphy_softc *sc;
  200 
  201         sc = device_get_softc(dev);
  202 
  203         if (sc->pmu[phyno] == NULL)
  204                 return;
  205 
  206         if (sc->phy_conf->pmu_unk1 == true)
  207                 CLR4(sc->pmu[phyno], PMU_UNK_H3, PMU_UNK_H3_CLR);
  208 
  209         SET4(sc->pmu[phyno], PMU_IRQ_ENABLE, PMU_ULPI_BYPASS |
  210             PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN);
  211 }
  212 
  213 static int
  214 awusbphy_init(device_t dev)
  215 {
  216         struct awusbphy_softc *sc;
  217         phandle_t node;
  218         char pname[20];
  219         uint32_t val;
  220         int error, off, rid;
  221         regulator_t reg;
  222         hwreset_t rst;
  223         clk_t clk;
  224 
  225         sc = device_get_softc(dev);
  226         node = ofw_bus_get_node(dev);
  227 
  228         sc->phy_conf = (struct aw_usbphy_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
  229 
  230         /* Get phy_ctrl region */
  231         if (ofw_bus_find_string_index(node, "reg-names", "phy_ctrl", &rid) != 0) {
  232                 device_printf(dev, "Cannot locate phy control resource\n");
  233                 return (ENXIO);
  234         }
  235         sc->phy_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  236             RF_ACTIVE);
  237         if (sc->phy_ctrl == NULL) {
  238                 device_printf(dev, "Cannot allocate resource\n");
  239                 return (ENXIO);
  240         }
  241 
  242         /* Enable clocks */
  243         for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
  244                 error = clk_enable(clk);
  245                 if (error != 0) {
  246                         device_printf(dev, "couldn't enable clock %s\n",
  247                             clk_get_name(clk));
  248                         return (error);
  249                 }
  250         }
  251 
  252         /* De-assert resets */
  253         for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
  254                 error = hwreset_deassert(rst);
  255                 if (error != 0) {
  256                         device_printf(dev, "couldn't de-assert reset %d\n",
  257                             off);
  258                         return (error);
  259                 }
  260         }
  261 
  262         /* Get GPIOs */
  263         error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios",
  264             &sc->id_det_pin);
  265         if (error == 0)
  266                 sc->id_det_valid = 1;
  267         error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios",
  268             &sc->vbus_det_pin);
  269         if (error == 0)
  270                 sc->vbus_det_valid = 1;
  271 
  272         sc->reg = malloc(sizeof(*(sc->reg)) * sc->phy_conf->num_phys, M_DEVBUF,
  273             M_WAITOK | M_ZERO);
  274         sc->pmu = malloc(sizeof(*(sc->pmu)) * sc->phy_conf->num_phys, M_DEVBUF,
  275             M_WAITOK | M_ZERO);
  276         /* Get regulators */
  277         for (off = 0; off < sc->phy_conf->num_phys; off++) {
  278                 snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
  279                 if (regulator_get_by_ofw_property(dev, 0, pname, &reg) == 0)
  280                         sc->reg[off] = reg;
  281 
  282                 snprintf(pname, sizeof(pname), "pmu%d", off);
  283                 if (ofw_bus_find_string_index(node, "reg-names",
  284                     pname, &rid) != 0)
  285                         continue;
  286 
  287                 sc->pmu[off] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  288                     RF_ACTIVE);
  289                 if (sc->pmu[off] == NULL) {
  290                         device_printf(dev, "Cannot allocate resource\n");
  291                         return (ENXIO);
  292                 }
  293         }
  294 
  295         /* Enable OTG PHY for host mode */
  296         val = bus_read_4(sc->phy_ctrl, PHY_CSR);
  297         val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
  298         val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
  299         val &= ~FORCE_ID;
  300         val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
  301         val &= ~FORCE_VBUS_VALID;
  302         val |= (FORCE_VBUS_VALID_HIGH << FORCE_VBUS_VALID_SHIFT);
  303         bus_write_4(sc->phy_ctrl, PHY_CSR, val);
  304 
  305         return (0);
  306 }
  307 
  308 static int
  309 awusbphy_vbus_detect(device_t dev, int *val)
  310 {
  311         struct awusbphy_softc *sc;
  312         bool active;
  313         int error;
  314 
  315         sc = device_get_softc(dev);
  316 
  317         if (sc->vbus_det_valid) {
  318                 error = gpio_pin_is_active(sc->vbus_det_pin, &active);
  319                 if (error != 0) {
  320                         device_printf(dev, "Cannot get status of id pin %d\n",
  321                             error);
  322                         return (error);
  323                 }
  324                 *val = active;
  325                 return (0);
  326         }
  327 
  328         /* TODO check vbus_power-supply. */
  329 
  330         /*
  331          * If there is no way to detect, assume present.
  332          */
  333         *val = 1;
  334         return (0);
  335 }
  336 
  337 static int
  338 awusbphy_phy_enable(struct phynode *phynode, bool enable)
  339 {
  340         device_t dev;
  341         intptr_t phy;
  342         struct awusbphy_softc *sc;
  343         regulator_t reg;
  344         int error, vbus_det;
  345 
  346         dev = phynode_get_device(phynode);
  347         phy = phynode_get_id(phynode);
  348         sc = device_get_softc(dev);
  349 
  350         if (phy < 0 || phy >= sc->phy_conf->num_phys)
  351                 return (ERANGE);
  352 
  353         /* Configure PHY */
  354         awusbphy_configure(dev, phy);
  355 
  356         /* Regulators are optional. If not found, return success. */
  357         reg = sc->reg[phy];
  358         if (reg == NULL)
  359                 return (0);
  360 
  361         if (phy == 0) {
  362                 /* If an external vbus is detected, do not enable phy 0 */
  363                 error = awusbphy_vbus_detect(dev, &vbus_det);
  364                 if (error)
  365                         goto out;
  366 
  367                 /* TODO check vbus_power-supply as well. */
  368                 if (sc->vbus_det_valid && vbus_det == 1) {
  369                         if (bootverbose)
  370                                 device_printf(dev, "External VBUS detected, "
  371                                     "not enabling the regulator\n");
  372                         return (0);
  373                 }
  374         }
  375         if (enable) {
  376                 /* Depending on the PHY we need to route OTG to OHCI/EHCI */
  377                 error = regulator_enable(reg);
  378         } else
  379                 error = regulator_disable(reg);
  380 
  381 out:
  382         if (error != 0) {
  383                 device_printf(dev,
  384                     "couldn't %s regulator for phy %jd\n",
  385                     enable ? "enable" : "disable", (intmax_t)phy);
  386                 return (error);
  387         }
  388 
  389         return (0);
  390 }
  391 
  392 static int
  393 awusbphy_get_mode(struct phynode *phynode, int *mode)
  394 {
  395         struct awusbphy_softc *sc;
  396         device_t dev;
  397 
  398         dev = phynode_get_device(phynode);
  399         sc = device_get_softc(dev);
  400 
  401         *mode = sc->mode;
  402 
  403         return (0);
  404 }
  405 
  406 static int
  407 awusbphy_set_mode(struct phynode *phynode, int mode)
  408 {
  409         device_t dev;
  410         intptr_t phy;
  411         struct awusbphy_softc *sc;
  412         uint32_t val;
  413         int error, vbus_det;
  414 
  415         dev = phynode_get_device(phynode);
  416         phy = phynode_get_id(phynode);
  417         sc = device_get_softc(dev);
  418 
  419         if (phy != 0) {
  420                 if (mode != PHY_USB_MODE_HOST)
  421                         return (EINVAL);
  422                 return (0);
  423         }
  424 
  425         if (sc->mode == mode)
  426                 return (0);
  427         if (mode == PHY_USB_MODE_OTG)   /* TODO */
  428                 return (EOPNOTSUPP);
  429 
  430         error = awusbphy_vbus_detect(dev, &vbus_det);
  431         if (error != 0)
  432                 return (error);
  433 
  434         val = bus_read_4(sc->phy_ctrl, PHY_CSR);
  435         val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
  436         val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
  437         val &= ~FORCE_VBUS_VALID;
  438         val |= (vbus_det ? FORCE_VBUS_VALID_HIGH : FORCE_VBUS_VALID_LOW) <<
  439             FORCE_VBUS_VALID_SHIFT;
  440         val &= ~FORCE_ID;
  441 
  442         switch (mode) {
  443         case PHY_USB_MODE_HOST:
  444                 val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
  445                 if (sc->phy_conf->phy0_route)
  446                         CLR4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
  447                 break;
  448         case PHY_USB_MODE_DEVICE:
  449                 val |= (FORCE_ID_HIGH << FORCE_ID_SHIFT);
  450                 if (sc->phy_conf->phy0_route)
  451                         SET4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
  452                 break;
  453         default:
  454                 return (EINVAL);
  455         }
  456 
  457         bus_write_4(sc->phy_ctrl, PHY_CSR, val);
  458         sc->mode = mode;
  459         return (0);
  460 }
  461 
  462 static int
  463 awusbphy_probe(device_t dev)
  464 {
  465         if (!ofw_bus_status_okay(dev))
  466                 return (ENXIO);
  467 
  468         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
  469                 return (ENXIO);
  470 
  471         device_set_desc(dev, "Allwinner USB PHY");
  472         return (BUS_PROBE_DEFAULT);
  473 }
  474 
  475 static int
  476 awusbphy_attach(device_t dev)
  477 {
  478         int error;
  479         struct phynode *phynode;
  480         struct phynode_init_def phy_init;
  481         struct awusbphy_softc *sc;
  482         int i;
  483 
  484         sc = device_get_softc(dev);
  485         error = awusbphy_init(dev);
  486         if (error) {
  487                 device_printf(dev, "failed to initialize USB PHY, error %d\n",
  488                     error);
  489                 return (error);
  490         }
  491 
  492         /* Create and register phys. */
  493         for (i = 0; i < sc->phy_conf->num_phys; i++) {
  494                 bzero(&phy_init, sizeof(phy_init));
  495                 phy_init.id = i;
  496                 phy_init.ofw_node = ofw_bus_get_node(dev);
  497                 phynode = phynode_create(dev, &awusbphy_phynode_class,
  498                     &phy_init);
  499                 if (phynode == NULL) {
  500                         device_printf(dev, "failed to create USB PHY\n");
  501                         return (ENXIO);
  502                 }
  503                 if (phynode_register(phynode) == NULL) {
  504                         device_printf(dev, "failed to create USB PHY\n");
  505                         return (ENXIO);
  506                 }
  507         }
  508 
  509         return (error);
  510 }
  511 
  512 static device_method_t awusbphy_methods[] = {
  513         /* Device interface */
  514         DEVMETHOD(device_probe,         awusbphy_probe),
  515         DEVMETHOD(device_attach,        awusbphy_attach),
  516 
  517         DEVMETHOD_END
  518 };
  519 
  520 static driver_t awusbphy_driver = {
  521         "awusbphy",
  522         awusbphy_methods,
  523         sizeof(struct awusbphy_softc)
  524 };
  525 
  526 /* aw_usbphy needs to come up after regulators/gpio/etc, but before ehci/ohci */
  527 EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, 0, 0,
  528     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
  529 MODULE_VERSION(awusbphy, 1);

Cache object: 6bafdaf075e7d0c5e9abeeb924b68eb8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.