11b4bd023SJared McNeill /*-
21b4bd023SJared McNeill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
31b4bd023SJared McNeill *
41b4bd023SJared McNeill * Redistribution and use in source and binary forms, with or without
51b4bd023SJared McNeill * modification, are permitted provided that the following conditions
61b4bd023SJared McNeill * are met:
71b4bd023SJared McNeill * 1. Redistributions of source code must retain the above copyright
81b4bd023SJared McNeill * notice, this list of conditions and the following disclaimer.
91b4bd023SJared McNeill * 2. Redistributions in binary form must reproduce the above copyright
101b4bd023SJared McNeill * notice, this list of conditions and the following disclaimer in the
111b4bd023SJared McNeill * documentation and/or other materials provided with the distribution.
121b4bd023SJared McNeill *
131b4bd023SJared McNeill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
141b4bd023SJared McNeill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
151b4bd023SJared McNeill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
161b4bd023SJared McNeill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
171b4bd023SJared McNeill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
181b4bd023SJared McNeill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
191b4bd023SJared McNeill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
201b4bd023SJared McNeill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
211b4bd023SJared McNeill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
221b4bd023SJared McNeill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
231b4bd023SJared McNeill * SUCH DAMAGE.
241b4bd023SJared McNeill */
251b4bd023SJared McNeill
261b4bd023SJared McNeill /*
271b4bd023SJared McNeill * Allwinner USB PHY
281b4bd023SJared McNeill */
291b4bd023SJared McNeill
301b4bd023SJared McNeill #include <sys/param.h>
311b4bd023SJared McNeill #include <sys/systm.h>
321b4bd023SJared McNeill #include <sys/bus.h>
331b4bd023SJared McNeill #include <sys/rman.h>
341b4bd023SJared McNeill #include <sys/kernel.h>
351b4bd023SJared McNeill #include <sys/module.h>
361b4bd023SJared McNeill #include <sys/gpio.h>
3763dc81d8SJared McNeill #include <machine/bus.h>
381b4bd023SJared McNeill
391b4bd023SJared McNeill #include <dev/ofw/ofw_bus.h>
401b4bd023SJared McNeill #include <dev/ofw/ofw_bus_subr.h>
41627c360fSJared McNeill #include <dev/gpio/gpiobusvar.h>
421b4bd023SJared McNeill
43be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
441f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
45b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h>
46950a6087SEmmanuel Vadot #include <dev/phy/phy_usb.h>
471b4bd023SJared McNeill
48f8759facSMichal Meloun #include "phynode_if.h"
49627c360fSJared McNeill
5063dc81d8SJared McNeill enum awusbphy_type {
5163dc81d8SJared McNeill AWUSBPHY_TYPE_A10 = 1,
5263dc81d8SJared McNeill AWUSBPHY_TYPE_A13,
5363dc81d8SJared McNeill AWUSBPHY_TYPE_A20,
5463dc81d8SJared McNeill AWUSBPHY_TYPE_A31,
5563dc81d8SJared McNeill AWUSBPHY_TYPE_H3,
565b48129eSKyle Evans AWUSBPHY_TYPE_A64,
57126700faSEmmanuel Vadot AWUSBPHY_TYPE_A83T,
58126700faSEmmanuel Vadot AWUSBPHY_TYPE_H6,
59*9eb30ef4SMitchell Horne AWUSBPHY_TYPE_D1,
6063dc81d8SJared McNeill };
611b4bd023SJared McNeill
6236dcd6a4SEmmanuel Vadot struct aw_usbphy_conf {
6336dcd6a4SEmmanuel Vadot int num_phys;
6436dcd6a4SEmmanuel Vadot enum awusbphy_type phy_type;
6536dcd6a4SEmmanuel Vadot bool pmu_unk1;
6636dcd6a4SEmmanuel Vadot bool phy0_route;
6736dcd6a4SEmmanuel Vadot };
6836dcd6a4SEmmanuel Vadot
6936dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a10_usbphy_conf = {
7036dcd6a4SEmmanuel Vadot .num_phys = 3,
7136dcd6a4SEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_A10,
7236dcd6a4SEmmanuel Vadot .pmu_unk1 = false,
7336dcd6a4SEmmanuel Vadot .phy0_route = false,
7436dcd6a4SEmmanuel Vadot };
7536dcd6a4SEmmanuel Vadot
7636dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a13_usbphy_conf = {
7736dcd6a4SEmmanuel Vadot .num_phys = 2,
7836dcd6a4SEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_A13,
7936dcd6a4SEmmanuel Vadot .pmu_unk1 = false,
8036dcd6a4SEmmanuel Vadot .phy0_route = false,
8136dcd6a4SEmmanuel Vadot };
8236dcd6a4SEmmanuel Vadot
8336dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a20_usbphy_conf = {
8436dcd6a4SEmmanuel Vadot .num_phys = 3,
8536dcd6a4SEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_A20,
8636dcd6a4SEmmanuel Vadot .pmu_unk1 = false,
8736dcd6a4SEmmanuel Vadot .phy0_route = false,
8836dcd6a4SEmmanuel Vadot };
8936dcd6a4SEmmanuel Vadot
9036dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a31_usbphy_conf = {
9136dcd6a4SEmmanuel Vadot .num_phys = 3,
9236dcd6a4SEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_A31,
9336dcd6a4SEmmanuel Vadot .pmu_unk1 = false,
9436dcd6a4SEmmanuel Vadot .phy0_route = false,
9536dcd6a4SEmmanuel Vadot };
9636dcd6a4SEmmanuel Vadot
9736dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf h3_usbphy_conf = {
9836dcd6a4SEmmanuel Vadot .num_phys = 4,
9936dcd6a4SEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_H3,
10036dcd6a4SEmmanuel Vadot .pmu_unk1 = true,
101aea49d9fSAndriy Gapon .phy0_route = true,
10236dcd6a4SEmmanuel Vadot };
10336dcd6a4SEmmanuel Vadot
10436dcd6a4SEmmanuel Vadot static const struct aw_usbphy_conf a64_usbphy_conf = {
10536dcd6a4SEmmanuel Vadot .num_phys = 2,
10636dcd6a4SEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_A64,
10736dcd6a4SEmmanuel Vadot .pmu_unk1 = true,
10836dcd6a4SEmmanuel Vadot .phy0_route = true,
10936dcd6a4SEmmanuel Vadot };
11036dcd6a4SEmmanuel Vadot
1115b48129eSKyle Evans static const struct aw_usbphy_conf a83t_usbphy_conf = {
1125b48129eSKyle Evans .num_phys = 3,
1135b48129eSKyle Evans .phy_type = AWUSBPHY_TYPE_A83T,
1145b48129eSKyle Evans .pmu_unk1 = false,
1155b48129eSKyle Evans .phy0_route = false,
1165b48129eSKyle Evans };
1175b48129eSKyle Evans
118126700faSEmmanuel Vadot static const struct aw_usbphy_conf h6_usbphy_conf = {
119126700faSEmmanuel Vadot .num_phys = 4,
120126700faSEmmanuel Vadot .phy_type = AWUSBPHY_TYPE_H6,
121126700faSEmmanuel Vadot .pmu_unk1 = false,
122126700faSEmmanuel Vadot .phy0_route = true,
123126700faSEmmanuel Vadot };
124126700faSEmmanuel Vadot
125*9eb30ef4SMitchell Horne static const struct aw_usbphy_conf d1_usbphy_conf = {
126*9eb30ef4SMitchell Horne .num_phys = 2,
127*9eb30ef4SMitchell Horne .phy_type = AWUSBPHY_TYPE_D1,
128*9eb30ef4SMitchell Horne .pmu_unk1 = true,
129*9eb30ef4SMitchell Horne .phy0_route = true,
130*9eb30ef4SMitchell Horne };
131*9eb30ef4SMitchell Horne
1321b4bd023SJared McNeill static struct ofw_compat_data compat_data[] = {
13336dcd6a4SEmmanuel Vadot { "allwinner,sun4i-a10-usb-phy", (uintptr_t)&a10_usbphy_conf },
13436dcd6a4SEmmanuel Vadot { "allwinner,sun5i-a13-usb-phy", (uintptr_t)&a13_usbphy_conf },
13536dcd6a4SEmmanuel Vadot { "allwinner,sun6i-a31-usb-phy", (uintptr_t)&a31_usbphy_conf },
13636dcd6a4SEmmanuel Vadot { "allwinner,sun7i-a20-usb-phy", (uintptr_t)&a20_usbphy_conf },
13736dcd6a4SEmmanuel Vadot { "allwinner,sun8i-h3-usb-phy", (uintptr_t)&h3_usbphy_conf },
13836dcd6a4SEmmanuel Vadot { "allwinner,sun50i-a64-usb-phy", (uintptr_t)&a64_usbphy_conf },
1395b48129eSKyle Evans { "allwinner,sun8i-a83t-usb-phy", (uintptr_t)&a83t_usbphy_conf },
140126700faSEmmanuel Vadot { "allwinner,sun50i-h6-usb-phy", (uintptr_t)&h6_usbphy_conf },
141*9eb30ef4SMitchell Horne { "allwinner,sun20i-d1-usb-phy", (uintptr_t)&d1_usbphy_conf },
1421b4bd023SJared McNeill { NULL, 0 }
1431b4bd023SJared McNeill };
1441b4bd023SJared McNeill
145627c360fSJared McNeill struct awusbphy_softc {
14636dcd6a4SEmmanuel Vadot struct resource * phy_ctrl;
14736dcd6a4SEmmanuel Vadot struct resource ** pmu;
14836dcd6a4SEmmanuel Vadot regulator_t * reg;
149627c360fSJared McNeill gpio_pin_t id_det_pin;
150627c360fSJared McNeill int id_det_valid;
151627c360fSJared McNeill gpio_pin_t vbus_det_pin;
152627c360fSJared McNeill int vbus_det_valid;
15336dcd6a4SEmmanuel Vadot struct aw_usbphy_conf *phy_conf;
15484ebe16aSEmmanuel Vadot int mode;
155627c360fSJared McNeill };
156627c360fSJared McNeill
157f8759facSMichal Meloun /* Phy class and methods. */
158f8759facSMichal Meloun static int awusbphy_phy_enable(struct phynode *phy, bool enable);
15984ebe16aSEmmanuel Vadot static int awusbphy_get_mode(struct phynode *phy, int *mode);
16084ebe16aSEmmanuel Vadot static int awusbphy_set_mode(struct phynode *phy, int mode);
16184ebe16aSEmmanuel Vadot static phynode_usb_method_t awusbphy_phynode_methods[] = {
162f8759facSMichal Meloun PHYNODEMETHOD(phynode_enable, awusbphy_phy_enable),
16384ebe16aSEmmanuel Vadot PHYNODEMETHOD(phynode_usb_get_mode, awusbphy_get_mode),
16484ebe16aSEmmanuel Vadot PHYNODEMETHOD(phynode_usb_set_mode, awusbphy_set_mode),
165f8759facSMichal Meloun
166f8759facSMichal Meloun PHYNODEMETHOD_END
167f8759facSMichal Meloun };
168f8759facSMichal Meloun DEFINE_CLASS_1(awusbphy_phynode, awusbphy_phynode_class, awusbphy_phynode_methods,
16984ebe16aSEmmanuel Vadot sizeof(struct phynode_usb_sc), phynode_usb_class);
170f8759facSMichal Meloun
17136dcd6a4SEmmanuel Vadot #define RD4(res, o) bus_read_4(res, (o))
17236dcd6a4SEmmanuel Vadot #define WR4(res, o, v) bus_write_4(res, (o), (v))
17336dcd6a4SEmmanuel Vadot #define CLR4(res, o, m) WR4(res, o, RD4(res, o) & ~(m))
17436dcd6a4SEmmanuel Vadot #define SET4(res, o, m) WR4(res, o, RD4(res, o) | (m))
17563dc81d8SJared McNeill
176d9a65ba8SAndriy Gapon #define PHY_CSR 0x00
177d9a65ba8SAndriy Gapon #define ID_PULLUP_EN (1 << 17)
178d9a65ba8SAndriy Gapon #define DPDM_PULLUP_EN (1 << 16)
179d9a65ba8SAndriy Gapon #define FORCE_ID (0x3 << 14)
180d9a65ba8SAndriy Gapon #define FORCE_ID_SHIFT 14
181d9a65ba8SAndriy Gapon #define FORCE_ID_LOW 2
182aea49d9fSAndriy Gapon #define FORCE_ID_HIGH 3
183d9a65ba8SAndriy Gapon #define FORCE_VBUS_VALID (0x3 << 12)
184d9a65ba8SAndriy Gapon #define FORCE_VBUS_VALID_SHIFT 12
185aea49d9fSAndriy Gapon #define FORCE_VBUS_VALID_LOW 2
186d9a65ba8SAndriy Gapon #define FORCE_VBUS_VALID_HIGH 3
187d9a65ba8SAndriy Gapon #define VBUS_CHANGE_DET (1 << 6)
188d9a65ba8SAndriy Gapon #define ID_CHANGE_DET (1 << 5)
189d9a65ba8SAndriy Gapon #define DPDM_CHANGE_DET (1 << 4)
19063dc81d8SJared McNeill #define OTG_PHY_CFG 0x20
19163dc81d8SJared McNeill #define OTG_PHY_ROUTE_OTG (1 << 0)
19263dc81d8SJared McNeill #define PMU_IRQ_ENABLE 0x00
19363dc81d8SJared McNeill #define PMU_AHB_INCR8 (1 << 10)
19463dc81d8SJared McNeill #define PMU_AHB_INCR4 (1 << 9)
19563dc81d8SJared McNeill #define PMU_AHB_INCRX_ALIGN (1 << 8)
19663dc81d8SJared McNeill #define PMU_ULPI_BYPASS (1 << 0)
19763dc81d8SJared McNeill #define PMU_UNK_H3 0x10
19863dc81d8SJared McNeill #define PMU_UNK_H3_CLR 0x2
19963dc81d8SJared McNeill
20063dc81d8SJared McNeill static void
awusbphy_configure(device_t dev,int phyno)20163dc81d8SJared McNeill awusbphy_configure(device_t dev, int phyno)
20263dc81d8SJared McNeill {
20363dc81d8SJared McNeill struct awusbphy_softc *sc;
20463dc81d8SJared McNeill
20563dc81d8SJared McNeill sc = device_get_softc(dev);
20663dc81d8SJared McNeill
20736dcd6a4SEmmanuel Vadot if (sc->pmu[phyno] == NULL)
20863dc81d8SJared McNeill return;
20963dc81d8SJared McNeill
21036dcd6a4SEmmanuel Vadot if (sc->phy_conf->pmu_unk1 == true)
2111eca1d26SEmmanuel Vadot CLR4(sc->pmu[phyno], PMU_UNK_H3, PMU_UNK_H3_CLR);
21263dc81d8SJared McNeill
21336dcd6a4SEmmanuel Vadot SET4(sc->pmu[phyno], PMU_IRQ_ENABLE, PMU_ULPI_BYPASS |
21463dc81d8SJared McNeill PMU_AHB_INCR8 | PMU_AHB_INCR4 | PMU_AHB_INCRX_ALIGN);
21563dc81d8SJared McNeill }
21663dc81d8SJared McNeill
2171b4bd023SJared McNeill static int
awusbphy_init(device_t dev)2181b4bd023SJared McNeill awusbphy_init(device_t dev)
2191b4bd023SJared McNeill {
220627c360fSJared McNeill struct awusbphy_softc *sc;
221627c360fSJared McNeill phandle_t node;
2221b4bd023SJared McNeill char pname[20];
223d9a65ba8SAndriy Gapon uint32_t val;
22436dcd6a4SEmmanuel Vadot int error, off, rid;
2256a05f063SJared McNeill regulator_t reg;
2266a05f063SJared McNeill hwreset_t rst;
2276a05f063SJared McNeill clk_t clk;
2281b4bd023SJared McNeill
229627c360fSJared McNeill sc = device_get_softc(dev);
230627c360fSJared McNeill node = ofw_bus_get_node(dev);
231627c360fSJared McNeill
23236dcd6a4SEmmanuel Vadot sc->phy_conf = (struct aw_usbphy_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
23336dcd6a4SEmmanuel Vadot
23436dcd6a4SEmmanuel Vadot /* Get phy_ctrl region */
23536dcd6a4SEmmanuel Vadot if (ofw_bus_find_string_index(node, "reg-names", "phy_ctrl", &rid) != 0) {
23636dcd6a4SEmmanuel Vadot device_printf(dev, "Cannot locate phy control resource\n");
23736dcd6a4SEmmanuel Vadot return (ENXIO);
23836dcd6a4SEmmanuel Vadot }
23936dcd6a4SEmmanuel Vadot sc->phy_ctrl = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
24036dcd6a4SEmmanuel Vadot RF_ACTIVE);
24136dcd6a4SEmmanuel Vadot if (sc->phy_ctrl == NULL) {
24236dcd6a4SEmmanuel Vadot device_printf(dev, "Cannot allocate resource\n");
24336dcd6a4SEmmanuel Vadot return (ENXIO);
24436dcd6a4SEmmanuel Vadot }
24563dc81d8SJared McNeill
2466a05f063SJared McNeill /* Enable clocks */
247dac93553SMichal Meloun for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
2486a05f063SJared McNeill error = clk_enable(clk);
2496a05f063SJared McNeill if (error != 0) {
2506a05f063SJared McNeill device_printf(dev, "couldn't enable clock %s\n",
2516a05f063SJared McNeill clk_get_name(clk));
2526a05f063SJared McNeill return (error);
2536a05f063SJared McNeill }
2546a05f063SJared McNeill }
2551b4bd023SJared McNeill
2566a05f063SJared McNeill /* De-assert resets */
257dac93553SMichal Meloun for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
2586a05f063SJared McNeill error = hwreset_deassert(rst);
2596a05f063SJared McNeill if (error != 0) {
2606a05f063SJared McNeill device_printf(dev, "couldn't de-assert reset %d\n",
2616a05f063SJared McNeill off);
2626a05f063SJared McNeill return (error);
2636a05f063SJared McNeill }
2646a05f063SJared McNeill }
2656a05f063SJared McNeill
266627c360fSJared McNeill /* Get GPIOs */
267627c360fSJared McNeill error = gpio_pin_get_by_ofw_property(dev, node, "usb0_id_det-gpios",
268627c360fSJared McNeill &sc->id_det_pin);
269627c360fSJared McNeill if (error == 0)
270627c360fSJared McNeill sc->id_det_valid = 1;
271627c360fSJared McNeill error = gpio_pin_get_by_ofw_property(dev, node, "usb0_vbus_det-gpios",
272627c360fSJared McNeill &sc->vbus_det_pin);
273627c360fSJared McNeill if (error == 0)
274627c360fSJared McNeill sc->vbus_det_valid = 1;
275627c360fSJared McNeill
27636dcd6a4SEmmanuel Vadot sc->reg = malloc(sizeof(*(sc->reg)) * sc->phy_conf->num_phys, M_DEVBUF,
27736dcd6a4SEmmanuel Vadot M_WAITOK | M_ZERO);
27836dcd6a4SEmmanuel Vadot sc->pmu = malloc(sizeof(*(sc->pmu)) * sc->phy_conf->num_phys, M_DEVBUF,
27936dcd6a4SEmmanuel Vadot M_WAITOK | M_ZERO);
28036dcd6a4SEmmanuel Vadot /* Get regulators */
28136dcd6a4SEmmanuel Vadot for (off = 0; off < sc->phy_conf->num_phys; off++) {
28236dcd6a4SEmmanuel Vadot snprintf(pname, sizeof(pname), "usb%d_vbus-supply", off);
28336dcd6a4SEmmanuel Vadot if (regulator_get_by_ofw_property(dev, 0, pname, ®) == 0)
28436dcd6a4SEmmanuel Vadot sc->reg[off] = reg;
28536dcd6a4SEmmanuel Vadot
28636dcd6a4SEmmanuel Vadot snprintf(pname, sizeof(pname), "pmu%d", off);
28736dcd6a4SEmmanuel Vadot if (ofw_bus_find_string_index(node, "reg-names",
28836dcd6a4SEmmanuel Vadot pname, &rid) != 0)
28936dcd6a4SEmmanuel Vadot continue;
29036dcd6a4SEmmanuel Vadot
29136dcd6a4SEmmanuel Vadot sc->pmu[off] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
29236dcd6a4SEmmanuel Vadot RF_ACTIVE);
29336dcd6a4SEmmanuel Vadot if (sc->pmu[off] == NULL) {
29436dcd6a4SEmmanuel Vadot device_printf(dev, "Cannot allocate resource\n");
29536dcd6a4SEmmanuel Vadot return (ENXIO);
29636dcd6a4SEmmanuel Vadot }
29736dcd6a4SEmmanuel Vadot }
29863dc81d8SJared McNeill
299d9a65ba8SAndriy Gapon /* Enable OTG PHY for host mode */
300d9a65ba8SAndriy Gapon val = bus_read_4(sc->phy_ctrl, PHY_CSR);
301d9a65ba8SAndriy Gapon val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
302d9a65ba8SAndriy Gapon val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
303d9a65ba8SAndriy Gapon val &= ~FORCE_ID;
304d9a65ba8SAndriy Gapon val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
305d9a65ba8SAndriy Gapon val &= ~FORCE_VBUS_VALID;
306d9a65ba8SAndriy Gapon val |= (FORCE_VBUS_VALID_HIGH << FORCE_VBUS_VALID_SHIFT);
307d9a65ba8SAndriy Gapon bus_write_4(sc->phy_ctrl, PHY_CSR, val);
308d9a65ba8SAndriy Gapon
309627c360fSJared McNeill return (0);
310627c360fSJared McNeill }
311627c360fSJared McNeill
312627c360fSJared McNeill static int
awusbphy_vbus_detect(device_t dev,int * val)313627c360fSJared McNeill awusbphy_vbus_detect(device_t dev, int *val)
314627c360fSJared McNeill {
315627c360fSJared McNeill struct awusbphy_softc *sc;
316627c360fSJared McNeill bool active;
317627c360fSJared McNeill int error;
318627c360fSJared McNeill
319627c360fSJared McNeill sc = device_get_softc(dev);
320627c360fSJared McNeill
321627c360fSJared McNeill if (sc->vbus_det_valid) {
322627c360fSJared McNeill error = gpio_pin_is_active(sc->vbus_det_pin, &active);
323de355beaSEmmanuel Vadot if (error != 0) {
324de355beaSEmmanuel Vadot device_printf(dev, "Cannot get status of id pin %d\n",
325de355beaSEmmanuel Vadot error);
326627c360fSJared McNeill return (error);
327de355beaSEmmanuel Vadot }
328627c360fSJared McNeill *val = active;
329627c360fSJared McNeill return (0);
330627c360fSJared McNeill }
331627c360fSJared McNeill
332aea49d9fSAndriy Gapon /* TODO check vbus_power-supply. */
333aea49d9fSAndriy Gapon
334aea49d9fSAndriy Gapon /*
335aea49d9fSAndriy Gapon * If there is no way to detect, assume present.
336aea49d9fSAndriy Gapon */
337aea49d9fSAndriy Gapon *val = 1;
338627c360fSJared McNeill return (0);
339627c360fSJared McNeill }
340627c360fSJared McNeill
341627c360fSJared McNeill static int
awusbphy_phy_enable(struct phynode * phynode,bool enable)342f8759facSMichal Meloun awusbphy_phy_enable(struct phynode *phynode, bool enable)
343627c360fSJared McNeill {
344f8759facSMichal Meloun device_t dev;
345f8759facSMichal Meloun intptr_t phy;
346627c360fSJared McNeill struct awusbphy_softc *sc;
347627c360fSJared McNeill regulator_t reg;
348627c360fSJared McNeill int error, vbus_det;
349627c360fSJared McNeill
350f8759facSMichal Meloun dev = phynode_get_device(phynode);
351f8759facSMichal Meloun phy = phynode_get_id(phynode);
352627c360fSJared McNeill sc = device_get_softc(dev);
353627c360fSJared McNeill
35436dcd6a4SEmmanuel Vadot if (phy < 0 || phy >= sc->phy_conf->num_phys)
35536dcd6a4SEmmanuel Vadot return (ERANGE);
35636dcd6a4SEmmanuel Vadot
35763dc81d8SJared McNeill /* Configure PHY */
35863dc81d8SJared McNeill awusbphy_configure(dev, phy);
35963dc81d8SJared McNeill
360627c360fSJared McNeill /* Regulators are optional. If not found, return success. */
361627c360fSJared McNeill reg = sc->reg[phy];
362627c360fSJared McNeill if (reg == NULL)
363627c360fSJared McNeill return (0);
364627c360fSJared McNeill
365627c360fSJared McNeill if (phy == 0) {
36684ebe16aSEmmanuel Vadot /* If an external vbus is detected, do not enable phy 0 */
367627c360fSJared McNeill error = awusbphy_vbus_detect(dev, &vbus_det);
368de355beaSEmmanuel Vadot if (error)
369de355beaSEmmanuel Vadot goto out;
370de355beaSEmmanuel Vadot
371aea49d9fSAndriy Gapon /* TODO check vbus_power-supply as well. */
372aea49d9fSAndriy Gapon if (sc->vbus_det_valid && vbus_det == 1) {
37384ebe16aSEmmanuel Vadot if (bootverbose)
374aea49d9fSAndriy Gapon device_printf(dev, "External VBUS detected, "
375aea49d9fSAndriy Gapon "not enabling the regulator\n");
376627c360fSJared McNeill return (0);
37784ebe16aSEmmanuel Vadot }
37884ebe16aSEmmanuel Vadot }
37984ebe16aSEmmanuel Vadot if (enable) {
38084ebe16aSEmmanuel Vadot /* Depending on the PHY we need to route OTG to OHCI/EHCI */
381627c360fSJared McNeill error = regulator_enable(reg);
382627c360fSJared McNeill } else
383627c360fSJared McNeill error = regulator_disable(reg);
384de355beaSEmmanuel Vadot
385de355beaSEmmanuel Vadot out:
386627c360fSJared McNeill if (error != 0) {
3877dd62272SJared McNeill device_printf(dev,
3887dd62272SJared McNeill "couldn't %s regulator for phy %jd\n",
3897dd62272SJared McNeill enable ? "enable" : "disable", (intmax_t)phy);
390627c360fSJared McNeill return (error);
3916a05f063SJared McNeill }
3921b4bd023SJared McNeill
3931b4bd023SJared McNeill return (0);
3941b4bd023SJared McNeill }
3951b4bd023SJared McNeill
3961b4bd023SJared McNeill static int
awusbphy_get_mode(struct phynode * phynode,int * mode)39784ebe16aSEmmanuel Vadot awusbphy_get_mode(struct phynode *phynode, int *mode)
39884ebe16aSEmmanuel Vadot {
39984ebe16aSEmmanuel Vadot struct awusbphy_softc *sc;
40084ebe16aSEmmanuel Vadot device_t dev;
40184ebe16aSEmmanuel Vadot
40284ebe16aSEmmanuel Vadot dev = phynode_get_device(phynode);
40384ebe16aSEmmanuel Vadot sc = device_get_softc(dev);
40484ebe16aSEmmanuel Vadot
40584ebe16aSEmmanuel Vadot *mode = sc->mode;
40684ebe16aSEmmanuel Vadot
40784ebe16aSEmmanuel Vadot return (0);
40884ebe16aSEmmanuel Vadot }
40984ebe16aSEmmanuel Vadot
41084ebe16aSEmmanuel Vadot static int
awusbphy_set_mode(struct phynode * phynode,int mode)41184ebe16aSEmmanuel Vadot awusbphy_set_mode(struct phynode *phynode, int mode)
41284ebe16aSEmmanuel Vadot {
41384ebe16aSEmmanuel Vadot device_t dev;
41484ebe16aSEmmanuel Vadot intptr_t phy;
41584ebe16aSEmmanuel Vadot struct awusbphy_softc *sc;
41684ebe16aSEmmanuel Vadot uint32_t val;
41784ebe16aSEmmanuel Vadot int error, vbus_det;
41884ebe16aSEmmanuel Vadot
41984ebe16aSEmmanuel Vadot dev = phynode_get_device(phynode);
42084ebe16aSEmmanuel Vadot phy = phynode_get_id(phynode);
42184ebe16aSEmmanuel Vadot sc = device_get_softc(dev);
42284ebe16aSEmmanuel Vadot
423198e1debSEmmanuel Vadot if (phy != 0) {
424198e1debSEmmanuel Vadot if (mode != PHY_USB_MODE_HOST)
42584ebe16aSEmmanuel Vadot return (EINVAL);
426198e1debSEmmanuel Vadot return (0);
427198e1debSEmmanuel Vadot }
42884ebe16aSEmmanuel Vadot
429aea49d9fSAndriy Gapon if (sc->mode == mode)
430aea49d9fSAndriy Gapon return (0);
431aea49d9fSAndriy Gapon if (mode == PHY_USB_MODE_OTG) /* TODO */
432aea49d9fSAndriy Gapon return (EOPNOTSUPP);
433aea49d9fSAndriy Gapon
434aea49d9fSAndriy Gapon error = awusbphy_vbus_detect(dev, &vbus_det);
435aea49d9fSAndriy Gapon if (error != 0)
436aea49d9fSAndriy Gapon return (error);
437aea49d9fSAndriy Gapon
43884ebe16aSEmmanuel Vadot val = bus_read_4(sc->phy_ctrl, PHY_CSR);
43984ebe16aSEmmanuel Vadot val &= ~(VBUS_CHANGE_DET | ID_CHANGE_DET | DPDM_CHANGE_DET);
44084ebe16aSEmmanuel Vadot val |= (ID_PULLUP_EN | DPDM_PULLUP_EN);
44184ebe16aSEmmanuel Vadot val &= ~FORCE_VBUS_VALID;
442aea49d9fSAndriy Gapon val |= (vbus_det ? FORCE_VBUS_VALID_HIGH : FORCE_VBUS_VALID_LOW) <<
443aea49d9fSAndriy Gapon FORCE_VBUS_VALID_SHIFT;
444aea49d9fSAndriy Gapon val &= ~FORCE_ID;
445aea49d9fSAndriy Gapon
446aea49d9fSAndriy Gapon switch (mode) {
447aea49d9fSAndriy Gapon case PHY_USB_MODE_HOST:
448aea49d9fSAndriy Gapon val |= (FORCE_ID_LOW << FORCE_ID_SHIFT);
449aea49d9fSAndriy Gapon if (sc->phy_conf->phy0_route)
450aea49d9fSAndriy Gapon CLR4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
451aea49d9fSAndriy Gapon break;
452aea49d9fSAndriy Gapon case PHY_USB_MODE_DEVICE:
453aea49d9fSAndriy Gapon val |= (FORCE_ID_HIGH << FORCE_ID_SHIFT);
454aea49d9fSAndriy Gapon if (sc->phy_conf->phy0_route)
455aea49d9fSAndriy Gapon SET4(sc->phy_ctrl, OTG_PHY_CFG, OTG_PHY_ROUTE_OTG);
456aea49d9fSAndriy Gapon break;
457aea49d9fSAndriy Gapon default:
458aea49d9fSAndriy Gapon return (EINVAL);
459aea49d9fSAndriy Gapon }
460aea49d9fSAndriy Gapon
46184ebe16aSEmmanuel Vadot bus_write_4(sc->phy_ctrl, PHY_CSR, val);
46284ebe16aSEmmanuel Vadot sc->mode = mode;
46384ebe16aSEmmanuel Vadot return (0);
46484ebe16aSEmmanuel Vadot }
46584ebe16aSEmmanuel Vadot
46684ebe16aSEmmanuel Vadot static int
awusbphy_probe(device_t dev)4671b4bd023SJared McNeill awusbphy_probe(device_t dev)
4681b4bd023SJared McNeill {
4691b4bd023SJared McNeill if (!ofw_bus_status_okay(dev))
4701b4bd023SJared McNeill return (ENXIO);
4711b4bd023SJared McNeill
4721b4bd023SJared McNeill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
4731b4bd023SJared McNeill return (ENXIO);
4741b4bd023SJared McNeill
4751b4bd023SJared McNeill device_set_desc(dev, "Allwinner USB PHY");
4761b4bd023SJared McNeill return (BUS_PROBE_DEFAULT);
4771b4bd023SJared McNeill }
4781b4bd023SJared McNeill
4791b4bd023SJared McNeill static int
awusbphy_attach(device_t dev)4801b4bd023SJared McNeill awusbphy_attach(device_t dev)
4811b4bd023SJared McNeill {
4821b4bd023SJared McNeill int error;
483f8759facSMichal Meloun struct phynode *phynode;
484f8759facSMichal Meloun struct phynode_init_def phy_init;
485f8759facSMichal Meloun struct awusbphy_softc *sc;
486f8759facSMichal Meloun int i;
4871b4bd023SJared McNeill
488f8759facSMichal Meloun sc = device_get_softc(dev);
4891b4bd023SJared McNeill error = awusbphy_init(dev);
490627c360fSJared McNeill if (error) {
4911b4bd023SJared McNeill device_printf(dev, "failed to initialize USB PHY, error %d\n",
4921b4bd023SJared McNeill error);
493627c360fSJared McNeill return (error);
494627c360fSJared McNeill }
495627c360fSJared McNeill
496f8759facSMichal Meloun /* Create and register phys. */
497f8759facSMichal Meloun for (i = 0; i < sc->phy_conf->num_phys; i++) {
498f8759facSMichal Meloun bzero(&phy_init, sizeof(phy_init));
499f8759facSMichal Meloun phy_init.id = i;
500f8759facSMichal Meloun phy_init.ofw_node = ofw_bus_get_node(dev);
501f8759facSMichal Meloun phynode = phynode_create(dev, &awusbphy_phynode_class,
502f8759facSMichal Meloun &phy_init);
503f8759facSMichal Meloun if (phynode == NULL) {
504f8759facSMichal Meloun device_printf(dev, "failed to create USB PHY\n");
505f8759facSMichal Meloun return (ENXIO);
506f8759facSMichal Meloun }
507f8759facSMichal Meloun if (phynode_register(phynode) == NULL) {
508f8759facSMichal Meloun device_printf(dev, "failed to create USB PHY\n");
509f8759facSMichal Meloun return (ENXIO);
510f8759facSMichal Meloun }
511f8759facSMichal Meloun }
5121b4bd023SJared McNeill
5131b4bd023SJared McNeill return (error);
5141b4bd023SJared McNeill }
5151b4bd023SJared McNeill
5161b4bd023SJared McNeill static device_method_t awusbphy_methods[] = {
5171b4bd023SJared McNeill /* Device interface */
5181b4bd023SJared McNeill DEVMETHOD(device_probe, awusbphy_probe),
5191b4bd023SJared McNeill DEVMETHOD(device_attach, awusbphy_attach),
5201b4bd023SJared McNeill
5211b4bd023SJared McNeill DEVMETHOD_END
5221b4bd023SJared McNeill };
5231b4bd023SJared McNeill
5241b4bd023SJared McNeill static driver_t awusbphy_driver = {
5251b4bd023SJared McNeill "awusbphy",
5261b4bd023SJared McNeill awusbphy_methods,
527627c360fSJared McNeill sizeof(struct awusbphy_softc)
5281b4bd023SJared McNeill };
5291b4bd023SJared McNeill
5309f3b3133SKyle Evans /* aw_usbphy needs to come up after regulators/gpio/etc, but before ehci/ohci */
5317e1e2ba1SJohn Baldwin EARLY_DRIVER_MODULE(awusbphy, simplebus, awusbphy_driver, 0, 0,
5327e1e2ba1SJohn Baldwin BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
5331b4bd023SJared McNeill MODULE_VERSION(awusbphy, 1);
534