1 /*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 /* 30 * Allwinner USB PHY 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/bus.h> 39 #include <sys/rman.h> 40 #include <sys/kernel.h> 41 #include <sys/module.h> 42 #include <sys/gpio.h> 43 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include "gpio_if.h" 48 49 #define USBPHY_NUMOFF 3 50 #define GPIO_POLARITY(flags) (((flags) & 1) ? GPIO_PIN_LOW : GPIO_PIN_HIGH) 51 52 static struct ofw_compat_data compat_data[] = { 53 { "allwinner,sun4i-a10-usb-phy", 1 }, 54 { "allwinner,sun5i-a13-usb-phy", 1 }, 55 { "allwinner,sun6i-a31-usb-phy", 1 }, 56 { "allwinner,sun7i-a20-usb-phy", 1 }, 57 { NULL, 0 } 58 }; 59 60 static int 61 awusbphy_gpio_set(device_t dev, phandle_t node, const char *pname) 62 { 63 pcell_t gpio_prop[4]; 64 phandle_t gpio_node; 65 device_t gpio_dev; 66 uint32_t pin, flags; 67 ssize_t len; 68 int val; 69 70 len = OF_getencprop(node, pname, gpio_prop, sizeof(gpio_prop)); 71 if (len == -1) 72 return (0); 73 74 if (len != sizeof(gpio_prop)) { 75 device_printf(dev, "property %s length was %d, expected %d\n", 76 pname, len, sizeof(gpio_prop)); 77 return (ENXIO); 78 } 79 80 gpio_node = OF_node_from_xref(gpio_prop[0]); 81 gpio_dev = OF_device_from_xref(gpio_prop[0]); 82 if (gpio_dev == NULL) { 83 device_printf(dev, "failed to get the GPIO device for %s\n", 84 pname); 85 return (ENOENT); 86 } 87 88 if (GPIO_MAP_GPIOS(gpio_dev, node, gpio_node, 89 sizeof(gpio_prop) / sizeof(gpio_prop[0]) - 1, gpio_prop + 1, 90 &pin, &flags) != 0) { 91 device_printf(dev, "failed to map the GPIO pin for %s\n", 92 pname); 93 return (ENXIO); 94 } 95 96 val = GPIO_POLARITY(flags); 97 98 GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT); 99 GPIO_PIN_SET(gpio_dev, pin, val); 100 101 return (0); 102 } 103 104 static int 105 awusbphy_supply_set(device_t dev, const char *pname) 106 { 107 phandle_t node, reg_node; 108 pcell_t reg_xref; 109 110 node = ofw_bus_get_node(dev); 111 112 if (OF_getencprop(node, pname, ®_xref, sizeof(reg_xref)) == -1) 113 return (0); 114 115 reg_node = OF_node_from_xref(reg_xref); 116 117 return (awusbphy_gpio_set(dev, reg_node, "gpio")); 118 } 119 120 static int 121 awusbphy_init(device_t dev) 122 { 123 char pname[20]; 124 phandle_t node; 125 int error, off; 126 127 node = ofw_bus_get_node(dev); 128 129 for (off = 0; off < USBPHY_NUMOFF; off++) { 130 snprintf(pname, sizeof(pname), "usb%d_id_det-gpio", off); 131 error = awusbphy_gpio_set(dev, node, pname); 132 if (error) 133 return (error); 134 135 snprintf(pname, sizeof(pname), "usb%d_vbus_det-gpio", off); 136 error = awusbphy_gpio_set(dev, node, pname); 137 if (error) 138 return (error); 139 140 snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off); 141 error = awusbphy_supply_set(dev, pname); 142 if (error) 143 return (error); 144 } 145 146 return (0); 147 } 148 149 static int 150 awusbphy_probe(device_t dev) 151 { 152 if (!ofw_bus_status_okay(dev)) 153 return (ENXIO); 154 155 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 156 return (ENXIO); 157 158 device_set_desc(dev, "Allwinner USB PHY"); 159 return (BUS_PROBE_DEFAULT); 160 } 161 162 static int 163 awusbphy_attach(device_t dev) 164 { 165 int error; 166 167 error = awusbphy_init(dev); 168 if (error) 169 device_printf(dev, "failed to initialize USB PHY, error %d\n", 170 error); 171 172 return (error); 173 } 174 175 static device_method_t awusbphy_methods[] = { 176 /* Device interface */ 177 DEVMETHOD(device_probe, awusbphy_probe), 178 DEVMETHOD(device_attach, awusbphy_attach), 179 180 DEVMETHOD_END 181 }; 182 183 static driver_t awusbphy_driver = { 184 "awusbphy", 185 awusbphy_methods, 186 0, 187 }; 188 189 static devclass_t awusbphy_devclass; 190 191 DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, awusbphy_devclass, 0, 0); 192 MODULE_VERSION(awusbphy, 1); 193