17a58744fSEmmanuel Vadot /*-
27a58744fSEmmanuel Vadot * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org>
37a58744fSEmmanuel Vadot * Copyright (c) 2016 The FreeBSD Foundation
47a58744fSEmmanuel Vadot * All rights reserved.
57a58744fSEmmanuel Vadot *
67a58744fSEmmanuel Vadot * This software was developed by Andrew Turner under
77a58744fSEmmanuel Vadot * sponsorship from the FreeBSD Foundation.
87a58744fSEmmanuel Vadot *
97a58744fSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without
107a58744fSEmmanuel Vadot * modification, are permitted provided that the following conditions
117a58744fSEmmanuel Vadot * are met:
127a58744fSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright
137a58744fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer.
147a58744fSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright
157a58744fSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the
167a58744fSEmmanuel Vadot * documentation and/or other materials provided with the distribution.
177a58744fSEmmanuel Vadot *
187a58744fSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
197a58744fSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
207a58744fSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
217a58744fSEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
227a58744fSEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
237a58744fSEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
247a58744fSEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
257a58744fSEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
267a58744fSEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
277a58744fSEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
287a58744fSEmmanuel Vadot * SUCH DAMAGE.
297a58744fSEmmanuel Vadot */
307a58744fSEmmanuel Vadot
317a58744fSEmmanuel Vadot #include "opt_bus.h"
327a58744fSEmmanuel Vadot
337a58744fSEmmanuel Vadot #include <sys/param.h>
347a58744fSEmmanuel Vadot #include <sys/systm.h>
357a58744fSEmmanuel Vadot #include <sys/bus.h>
367a58744fSEmmanuel Vadot #include <sys/condvar.h>
377a58744fSEmmanuel Vadot #include <sys/kernel.h>
387a58744fSEmmanuel Vadot #include <sys/module.h>
397a58744fSEmmanuel Vadot
407a58744fSEmmanuel Vadot #include <dev/usb/usb.h>
417a58744fSEmmanuel Vadot #include <dev/usb/usbdi.h>
427a58744fSEmmanuel Vadot
437a58744fSEmmanuel Vadot #include <dev/usb/usb_core.h>
447a58744fSEmmanuel Vadot #include <dev/usb/usb_busdma.h>
457a58744fSEmmanuel Vadot #include <dev/usb/usb_process.h>
467a58744fSEmmanuel Vadot
477a58744fSEmmanuel Vadot #include <dev/usb/usb_controller.h>
487a58744fSEmmanuel Vadot #include <dev/usb/usb_bus.h>
497a58744fSEmmanuel Vadot #include <dev/usb/controller/ehci.h>
507a58744fSEmmanuel Vadot
517a58744fSEmmanuel Vadot #include <dev/fdt/fdt_common.h>
527a58744fSEmmanuel Vadot #include <dev/ofw/openfirm.h>
537a58744fSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
547a58744fSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
557a58744fSEmmanuel Vadot
56be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
571f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
58*950a6087SEmmanuel Vadot #include <dev/phy/phy.h>
59*950a6087SEmmanuel Vadot #include <dev/phy/phy_usb.h>
607a58744fSEmmanuel Vadot
617a58744fSEmmanuel Vadot #include "generic_ehci.h"
627a58744fSEmmanuel Vadot
637a58744fSEmmanuel Vadot struct clk_list {
647a58744fSEmmanuel Vadot TAILQ_ENTRY(clk_list) next;
657a58744fSEmmanuel Vadot clk_t clk;
667a58744fSEmmanuel Vadot };
677a58744fSEmmanuel Vadot
687a58744fSEmmanuel Vadot struct hwrst_list {
697a58744fSEmmanuel Vadot TAILQ_ENTRY(hwrst_list) next;
707a58744fSEmmanuel Vadot hwreset_t rst;
717a58744fSEmmanuel Vadot };
727a58744fSEmmanuel Vadot
737a58744fSEmmanuel Vadot struct phy_list {
747a58744fSEmmanuel Vadot TAILQ_ENTRY(phy_list) next;
757a58744fSEmmanuel Vadot phy_t phy;
767a58744fSEmmanuel Vadot };
777a58744fSEmmanuel Vadot
787a58744fSEmmanuel Vadot struct generic_ehci_fdt_softc {
797a58744fSEmmanuel Vadot ehci_softc_t ehci_sc;
807a58744fSEmmanuel Vadot
817a58744fSEmmanuel Vadot TAILQ_HEAD(, clk_list) clk_list;
827a58744fSEmmanuel Vadot TAILQ_HEAD(, hwrst_list) rst_list;
837a58744fSEmmanuel Vadot TAILQ_HEAD(, phy_list) phy_list;
847a58744fSEmmanuel Vadot };
857a58744fSEmmanuel Vadot
867a58744fSEmmanuel Vadot static device_probe_t generic_ehci_fdt_probe;
877a58744fSEmmanuel Vadot static device_attach_t generic_ehci_fdt_attach;
887a58744fSEmmanuel Vadot static device_detach_t generic_ehci_fdt_detach;
897a58744fSEmmanuel Vadot
907a58744fSEmmanuel Vadot static int
generic_ehci_fdt_probe(device_t self)917a58744fSEmmanuel Vadot generic_ehci_fdt_probe(device_t self)
927a58744fSEmmanuel Vadot {
937a58744fSEmmanuel Vadot
947a58744fSEmmanuel Vadot if (!ofw_bus_status_okay(self))
957a58744fSEmmanuel Vadot return (ENXIO);
967a58744fSEmmanuel Vadot
977a58744fSEmmanuel Vadot if (!ofw_bus_is_compatible(self, "generic-ehci"))
987a58744fSEmmanuel Vadot return (ENXIO);
997a58744fSEmmanuel Vadot
1007a58744fSEmmanuel Vadot device_set_desc(self, "Generic EHCI Controller");
1017a58744fSEmmanuel Vadot return (BUS_PROBE_DEFAULT);
1027a58744fSEmmanuel Vadot }
1037a58744fSEmmanuel Vadot
1047a58744fSEmmanuel Vadot static int
generic_ehci_fdt_attach(device_t dev)1057a58744fSEmmanuel Vadot generic_ehci_fdt_attach(device_t dev)
1067a58744fSEmmanuel Vadot {
107306e46ebSEmmanuel Vadot int err;
1087a58744fSEmmanuel Vadot struct generic_ehci_fdt_softc *sc;
1097a58744fSEmmanuel Vadot struct clk_list *clkp;
1107a58744fSEmmanuel Vadot clk_t clk;
1117a58744fSEmmanuel Vadot struct hwrst_list *rstp;
1127a58744fSEmmanuel Vadot hwreset_t rst;
1137a58744fSEmmanuel Vadot struct phy_list *phyp;
1147a58744fSEmmanuel Vadot phy_t phy;
115306e46ebSEmmanuel Vadot int off;
1167a58744fSEmmanuel Vadot
1177a58744fSEmmanuel Vadot sc = device_get_softc(dev);
1187a58744fSEmmanuel Vadot
1197a58744fSEmmanuel Vadot TAILQ_INIT(&sc->clk_list);
1207a58744fSEmmanuel Vadot /* Enable clock */
1217a58744fSEmmanuel Vadot for (off = 0; clk_get_by_ofw_index(dev, 0, off, &clk) == 0; off++) {
1227a58744fSEmmanuel Vadot err = clk_enable(clk);
1237a58744fSEmmanuel Vadot if (err != 0) {
1247a58744fSEmmanuel Vadot device_printf(dev, "Could not enable clock %s\n",
1257a58744fSEmmanuel Vadot clk_get_name(clk));
1267a58744fSEmmanuel Vadot goto error;
1277a58744fSEmmanuel Vadot }
1287a58744fSEmmanuel Vadot clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO);
1297a58744fSEmmanuel Vadot clkp->clk = clk;
1307a58744fSEmmanuel Vadot TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next);
1317a58744fSEmmanuel Vadot }
1327a58744fSEmmanuel Vadot
1337a58744fSEmmanuel Vadot /* De-assert reset */
1347a58744fSEmmanuel Vadot TAILQ_INIT(&sc->rst_list);
1357a58744fSEmmanuel Vadot for (off = 0; hwreset_get_by_ofw_idx(dev, 0, off, &rst) == 0; off++) {
1367a58744fSEmmanuel Vadot err = hwreset_deassert(rst);
1377a58744fSEmmanuel Vadot if (err != 0) {
1387a58744fSEmmanuel Vadot device_printf(dev, "Could not de-assert reset\n");
1397a58744fSEmmanuel Vadot goto error;
1407a58744fSEmmanuel Vadot }
1417a58744fSEmmanuel Vadot rstp = malloc(sizeof(*rstp), M_DEVBUF, M_WAITOK | M_ZERO);
1427a58744fSEmmanuel Vadot rstp->rst = rst;
1437a58744fSEmmanuel Vadot TAILQ_INSERT_TAIL(&sc->rst_list, rstp, next);
1447a58744fSEmmanuel Vadot }
1457a58744fSEmmanuel Vadot
1467a58744fSEmmanuel Vadot /* Enable USB PHY */
1477a58744fSEmmanuel Vadot TAILQ_INIT(&sc->phy_list);
1487a58744fSEmmanuel Vadot for (off = 0; phy_get_by_ofw_idx(dev, 0, off, &phy) == 0; off++) {
1497a58744fSEmmanuel Vadot err = phy_usb_set_mode(phy, PHY_USB_MODE_HOST);
1507a58744fSEmmanuel Vadot if (err != 0) {
1517a58744fSEmmanuel Vadot device_printf(dev, "Could not set phy to host mode\n");
1527a58744fSEmmanuel Vadot goto error;
1537a58744fSEmmanuel Vadot }
1547a58744fSEmmanuel Vadot err = phy_enable(phy);
1557a58744fSEmmanuel Vadot if (err != 0) {
1567a58744fSEmmanuel Vadot device_printf(dev, "Could not enable phy\n");
1577a58744fSEmmanuel Vadot goto error;
1587a58744fSEmmanuel Vadot }
1597a58744fSEmmanuel Vadot phyp = malloc(sizeof(*phyp), M_DEVBUF, M_WAITOK | M_ZERO);
1607a58744fSEmmanuel Vadot phyp->phy = phy;
1617a58744fSEmmanuel Vadot TAILQ_INSERT_TAIL(&sc->phy_list, phyp, next);
1627a58744fSEmmanuel Vadot }
1637a58744fSEmmanuel Vadot
1647a58744fSEmmanuel Vadot err = generic_ehci_attach(dev);
1657a58744fSEmmanuel Vadot if (err != 0)
1667a58744fSEmmanuel Vadot goto error;
1677a58744fSEmmanuel Vadot
1687a58744fSEmmanuel Vadot return (0);
1697a58744fSEmmanuel Vadot
1707a58744fSEmmanuel Vadot error:
1717a58744fSEmmanuel Vadot generic_ehci_fdt_detach(dev);
1727a58744fSEmmanuel Vadot return (err);
1737a58744fSEmmanuel Vadot }
1747a58744fSEmmanuel Vadot
1757a58744fSEmmanuel Vadot static int
generic_ehci_fdt_detach(device_t dev)1767a58744fSEmmanuel Vadot generic_ehci_fdt_detach(device_t dev)
1777a58744fSEmmanuel Vadot {
1787a58744fSEmmanuel Vadot struct generic_ehci_fdt_softc *sc;
1797a58744fSEmmanuel Vadot struct clk_list *clk, *clk_tmp;
1807a58744fSEmmanuel Vadot struct hwrst_list *rst, *rst_tmp;
1817a58744fSEmmanuel Vadot struct phy_list *phy, *phy_tmp;
1827a58744fSEmmanuel Vadot int err;
1837a58744fSEmmanuel Vadot
1847a58744fSEmmanuel Vadot err = generic_ehci_detach(dev);
1857a58744fSEmmanuel Vadot if (err != 0)
1867a58744fSEmmanuel Vadot return (err);
1877a58744fSEmmanuel Vadot
1887a58744fSEmmanuel Vadot sc = device_get_softc(dev);
1897a58744fSEmmanuel Vadot
1907a58744fSEmmanuel Vadot /* Disable clock */
1917a58744fSEmmanuel Vadot TAILQ_FOREACH_SAFE(clk, &sc->clk_list, next, clk_tmp) {
1927a58744fSEmmanuel Vadot err = clk_disable(clk->clk);
1937a58744fSEmmanuel Vadot if (err != 0)
1947a58744fSEmmanuel Vadot device_printf(dev, "Could not disable clock %s\n",
1957a58744fSEmmanuel Vadot clk_get_name(clk->clk));
1967a58744fSEmmanuel Vadot err = clk_release(clk->clk);
1977a58744fSEmmanuel Vadot if (err != 0)
1987a58744fSEmmanuel Vadot device_printf(dev, "Could not release clock %s\n",
1997a58744fSEmmanuel Vadot clk_get_name(clk->clk));
2007a58744fSEmmanuel Vadot TAILQ_REMOVE(&sc->clk_list, clk, next);
2017a58744fSEmmanuel Vadot free(clk, M_DEVBUF);
2027a58744fSEmmanuel Vadot }
2037a58744fSEmmanuel Vadot
2047a58744fSEmmanuel Vadot /* Assert reset */
2057a58744fSEmmanuel Vadot TAILQ_FOREACH_SAFE(rst, &sc->rst_list, next, rst_tmp) {
2067a58744fSEmmanuel Vadot hwreset_assert(rst->rst);
2077a58744fSEmmanuel Vadot hwreset_release(rst->rst);
2087a58744fSEmmanuel Vadot TAILQ_REMOVE(&sc->rst_list, rst, next);
2097a58744fSEmmanuel Vadot free(rst, M_DEVBUF);
2107a58744fSEmmanuel Vadot }
2117a58744fSEmmanuel Vadot
2127a58744fSEmmanuel Vadot /* Disable phys */
2137a58744fSEmmanuel Vadot TAILQ_FOREACH_SAFE(phy, &sc->phy_list, next, phy_tmp) {
2147a58744fSEmmanuel Vadot err = phy_disable(phy->phy);
2157a58744fSEmmanuel Vadot if (err != 0)
2167a58744fSEmmanuel Vadot device_printf(dev, "Could not disable phy\n");
2177a58744fSEmmanuel Vadot phy_release(phy->phy);
2187a58744fSEmmanuel Vadot TAILQ_REMOVE(&sc->phy_list, phy, next);
2197a58744fSEmmanuel Vadot free(phy, M_DEVBUF);
2207a58744fSEmmanuel Vadot }
2217a58744fSEmmanuel Vadot
2227a58744fSEmmanuel Vadot return (0);
2237a58744fSEmmanuel Vadot }
2247a58744fSEmmanuel Vadot
2257a58744fSEmmanuel Vadot static device_method_t ehci_fdt_methods[] = {
2267a58744fSEmmanuel Vadot /* Device interface */
2277a58744fSEmmanuel Vadot DEVMETHOD(device_probe, generic_ehci_fdt_probe),
2287a58744fSEmmanuel Vadot DEVMETHOD(device_attach, generic_ehci_fdt_attach),
2297a58744fSEmmanuel Vadot DEVMETHOD(device_detach, generic_ehci_fdt_detach),
2307a58744fSEmmanuel Vadot
2317a58744fSEmmanuel Vadot DEVMETHOD_END
2327a58744fSEmmanuel Vadot };
2337a58744fSEmmanuel Vadot
2347a58744fSEmmanuel Vadot DEFINE_CLASS_1(ehci, ehci_fdt_driver, ehci_fdt_methods,
2357a58744fSEmmanuel Vadot sizeof(ehci_softc_t), generic_ehci_driver);
2367a58744fSEmmanuel Vadot
237bc9372d7SJohn Baldwin DRIVER_MODULE(generic_ehci, simplebus, ehci_fdt_driver, 0, 0);
238f943f61cSEmmanuel Vadot MODULE_DEPEND(generic_ehci, usb, 1, 1, 1);
239