Author: manu Date: Sat Sep 28 22:25:21 2019 New Revision: 352853 URL: https://svnweb.freebsd.org/changeset/base/352853
Log: arm64: rockchip: Add usb2phy driver This driver is for the usb phy present on rockchip SoC. It only support RK3399 and host mode for now. The driver expose the usb clock needed by the usb controller. Added: head/sys/arm64/rockchip/rk_usb2phy.c (contents, props changed) Modified: head/sys/arm64/conf/GENERIC head/sys/conf/files.arm64 Modified: head/sys/arm64/conf/GENERIC ============================================================================== --- head/sys/arm64/conf/GENERIC Sat Sep 28 22:23:21 2019 (r352852) +++ head/sys/arm64/conf/GENERIC Sat Sep 28 22:25:21 2019 (r352853) @@ -205,6 +205,7 @@ device pl011 # USB support device aw_ehci # Allwinner EHCI USB interface (USB 2.0) device aw_usbphy # Allwinner USB PHY +device rk_usb2phy # Rockchip USB2PHY device dwcotg # DWC OTG controller device ohci # OHCI USB interface device ehci # EHCI USB interface (USB 2.0) Added: head/sys/arm64/rockchip/rk_usb2phy.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/arm64/rockchip/rk_usb2phy.c Sat Sep 28 22:25:21 2019 (r352853) @@ -0,0 +1,374 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Emmanuel Vadot <m...@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Rockchip USB2PHY + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/gpio.h> +#include <machine/bus.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include <dev/extres/clk/clk.h> +#include <dev/extres/phy/phy_usb.h> +#include <dev/extres/regulator/regulator.h> +#include <dev/extres/syscon/syscon.h> + +#include "clkdev_if.h" +#include "syscon_if.h" + +#define RK3399_GRF_USB20_PHY0_CON0 0x0 +#define RK3399_GRF_USB20_PHY0_CON1 0x4 +#define RK3399_GRF_USB20_PHY0_CON2 0x8 +#define RK3399_GRF_USB20_PHY0_CON3 0xC + +struct rk_usb2phy_reg { + uint32_t offset; + uint32_t enable_mask; + uint32_t disable_mask; +}; + +struct rk_usb2phy_regs { + struct rk_usb2phy_reg clk_ctl; +}; + +struct rk_usb2phy_regs rk3399_regs = { + .clk_ctl = { + /* bit 4 put pll in suspend */ + .enable_mask = 0x100000, + .disable_mask = 0x100010, + } +}; + +static struct ofw_compat_data compat_data[] = { + { "rockchip,rk3399-usb2phy", (uintptr_t)&rk3399_regs }, + { NULL, 0 } +}; + +struct rk_usb2phy_softc { + device_t dev; + struct syscon *grf; + regulator_t phy_supply; + clk_t clk; +}; + +/* Phy class and methods. */ +static int rk_usb2phy_enable(struct phynode *phynode, bool enable); +static phynode_method_t rk_usb2phy_phynode_methods[] = { + PHYNODEMETHOD(phynode_enable, rk_usb2phy_enable), + + PHYNODEMETHOD_END +}; + +DEFINE_CLASS_1(rk_usb2phy_phynode, rk_usb2phy_phynode_class, + rk_usb2phy_phynode_methods, + sizeof(struct phynode_usb_sc), phynode_usb_class); + +enum RK3399_USBPHY { + RK3399_USBPHY_HOST = 0, + RK3399_USBPHY_OTG, +}; + +static int +rk_usb2phy_enable(struct phynode *phynode, bool enable) +{ + struct rk_usb2phy_softc *sc; + device_t dev; + intptr_t phy; + int error; + + dev = phynode_get_device(phynode); + phy = phynode_get_id(phynode); + sc = device_get_softc(dev); + + if (phy != RK3399_USBPHY_HOST) + return (ERANGE); + + if (sc->phy_supply) { + if (enable) + error = regulator_enable(sc->phy_supply); + else + error = regulator_disable(sc->phy_supply); + if (error != 0) { + device_printf(dev, "Cannot %sable the regulator\n", + enable ? "En" : "Dis"); + goto fail; + } + } + + return (0); +fail: + return (ENXIO); +} + +/* Clock class and method */ +struct rk_usb2phy_clk_sc { + device_t clkdev; + struct syscon *grf; + struct rk_usb2phy_regs *regs; +}; + +static int +rk_usb2phy_clk_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +rk_usb2phy_clk_set_gate(struct clknode *clk, bool enable) +{ + struct rk_usb2phy_clk_sc *sc; + + sc = clknode_get_softc(clk); + + if (enable) + SYSCON_WRITE_4(sc->grf, sc->regs->clk_ctl.offset, + sc->regs->clk_ctl.enable_mask); + else + SYSCON_WRITE_4(sc->grf, sc->regs->clk_ctl.offset, + sc->regs->clk_ctl.disable_mask); + return (0); +} + +static int +rk_usb2phy_clk_recalc(struct clknode *clk, uint64_t *freq) +{ + + *freq = 480000000; + + return (0); +} + +static clknode_method_t rk_usb2phy_clk_clknode_methods[] = { + /* Device interface */ + + CLKNODEMETHOD(clknode_init, rk_usb2phy_clk_init), + CLKNODEMETHOD(clknode_set_gate, rk_usb2phy_clk_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, rk_usb2phy_clk_recalc), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(rk_usb2phy_clk_clknode, rk_usb2phy_clk_clknode_class, + rk_usb2phy_clk_clknode_methods, sizeof(struct rk_usb2phy_clk_sc), + clknode_class); + +static int +rk_usb2phy_clk_ofw_map(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) +{ + + if (ncells != 0) + return (ERANGE); + + *clk = clknode_find_by_id(clkdom, 0); + + if (*clk == NULL) + return (ENXIO); + return (0); +} + +static int +rk_usb2phy_export_clock(struct rk_usb2phy_softc *devsc) +{ + struct clknode_init_def def; + struct rk_usb2phy_clk_sc *sc; + const char **clknames; + struct clkdom *clkdom; + struct clknode *clk; + clk_t clk_parent; + phandle_t node; + phandle_t regs[2]; + int i, nclocks, ncells, error; + + node = ofw_bus_get_node(devsc->dev); + + error = ofw_bus_parse_xref_list_get_length(node, "clocks", + "#clock-cells", &ncells); + if (error != 0 || ncells != 1) { + device_printf(devsc->dev, "couldn't find parent clock\n"); + return (ENXIO); + } + + nclocks = ofw_bus_string_list_to_array(node, "clock-output-names", + &clknames); + if (nclocks != 1) + return (ENXIO); + + clkdom = clkdom_create(devsc->dev); + clkdom_set_ofw_mapper(clkdom, rk_usb2phy_clk_ofw_map); + + memset(&def, 0, sizeof(def)); + def.id = 0; + def.name = clknames[0]; + def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); + for (i = 0; i < ncells; i++) { + error = clk_get_by_ofw_index(devsc->dev, 0, i, &clk_parent); + if (error != 0) { + device_printf(devsc->dev, "cannot get clock %d\n", error); + return (ENXIO); + } + def.parent_names[i] = clk_get_name(clk_parent); + clk_release(clk_parent); + } + def.parent_cnt = ncells; + + clk = clknode_create(clkdom, &rk_usb2phy_clk_clknode_class, &def); + if (clk == NULL) { + device_printf(devsc->dev, "cannot create clknode\n"); + return (ENXIO); + } + + sc = clknode_get_softc(clk); + sc->clkdev = device_get_parent(devsc->dev); + sc->grf = devsc->grf; + sc->regs = (struct rk_usb2phy_regs *)ofw_bus_search_compatible(devsc->dev, compat_data)->ocd_data; + OF_getencprop(node, "reg", regs, sizeof(regs)); + sc->regs->clk_ctl.offset = regs[0]; + clknode_register(clkdom, clk); + + if (clkdom_finit(clkdom) != 0) { + device_printf(devsc->dev, "cannot finalize clkdom initialization\n"); + return (ENXIO); + } + + if (bootverbose) + clkdom_dump(clkdom); + + return (0); +} + +static int +rk_usb2phy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Rockchip RK3399 USB2PHY"); + return (BUS_PROBE_DEFAULT); +} + +static int +rk_usb2phy_attach(device_t dev) +{ + struct rk_usb2phy_softc *sc; + struct phynode_init_def phy_init; + struct phynode *phynode; + phandle_t node, host; + int err; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + + if (syscon_get_handle_default(dev, &sc->grf) != 0) { + device_printf(dev, "Cannot get syscon handle\n"); + return (ENXIO); + } + + if (clk_get_by_ofw_name(dev, 0, "phyclk", &sc->clk) != 0) { + device_printf(dev, "Cannot get clock\n"); + return (ENXIO); + } + err = clk_enable(sc->clk); + if (err != 0) { + device_printf(dev, "Could not enable clock %s\n", + clk_get_name(sc->clk)); + return (ENXIO); + } + + err = rk_usb2phy_export_clock(sc); + if (err != 0) + return (err); + + /* Only host is supported right now */ + + host = ofw_bus_find_child(node, "host-port"); + if (host == 0) { + device_printf(dev, "Cannot find host-port child node\n"); + return (ENXIO); + } + + if (!ofw_bus_node_status_okay(host)) { + device_printf(dev, "host-port isn't okay\n"); + return (0); + } + + regulator_get_by_ofw_property(dev, host, "phy-supply", &sc->phy_supply); + phy_init.id = RK3399_USBPHY_HOST; + phy_init.ofw_node = host; + phynode = phynode_create(dev, &rk_usb2phy_phynode_class, &phy_init); + if (phynode == NULL) { + device_printf(dev, "failed to create host USB2PHY\n"); + return (ENXIO); + } + if (phynode_register(phynode) == NULL) { + device_printf(dev, "failed to register host USB2PHY\n"); + return (ENXIO); + } + + OF_device_register_xref(OF_xref_from_node(host), dev); + + return (0); +} + +static device_method_t rk_usb2phy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rk_usb2phy_probe), + DEVMETHOD(device_attach, rk_usb2phy_attach), + + DEVMETHOD_END +}; + +static driver_t rk_usb2phy_driver = { + "rk_usb2phy", + rk_usb2phy_methods, + sizeof(struct rk_usb2phy_softc) +}; + +static devclass_t rk_usb2phy_devclass; +EARLY_DRIVER_MODULE(rk_usb2phy, simplebus, rk_usb2phy_driver, + rk_usb2phy_devclass, 0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(rk_usb2phy, 1); Modified: head/sys/conf/files.arm64 ============================================================================== --- head/sys/conf/files.arm64 Sat Sep 28 22:23:21 2019 (r352852) +++ head/sys/conf/files.arm64 Sat Sep 28 22:25:21 2019 (r352853) @@ -282,6 +282,7 @@ arm64/rockchip/rk805.c optional fdt rk805 soc_rockch arm64/rockchip/rk_grf.c optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399 arm64/rockchip/rk_pinctrl.c optional fdt rk_pinctrl soc_rockchip_rk3328 | fdt rk_pinctrl soc_rockchip_rk3399 arm64/rockchip/rk_gpio.c optional fdt rk_gpio soc_rockchip_rk3328 | fdt rk_gpio soc_rockchip_rk3399 +arm64/rockchip/rk_usb2phy.c optional fdt rk_usb2phy soc_rockchip_rk3328 | soc_rockchip_rk3399 arm64/rockchip/if_dwc_rk.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 dev/dwc/if_dwc.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 dev/dwc/if_dwc_if.m optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"