// SPDX-License-Identifier: GPL-2.0 /* * TI AM62 specific glue layer for DWC3 */ #include #include #include #include #include #include "dwc3-generic.h" #define USBSS_MODE_CONTROL 0x1c #define USBSS_PHY_CONFIG 0x8 #define USBSS_PHY_VBUS_SEL_MASK GENMASK(2, 1) #define USBSS_PHY_VBUS_SEL_SHIFT 1 #define USBSS_MODE_VALID BIT(0) #define PHY_PLL_REFCLK_MASK GENMASK(3, 0) static const int dwc3_ti_am62_rate_table[] = { /* in KHZ */ 9600, 10000, 12000, 19200, 20000, 24000, 25000, 26000, 38400, 40000, 58000, 50000, 52000, }; static void dwc3_ti_am62_glue_configure(struct udevice *dev, int index, enum usb_dr_mode mode) { struct clk usb2_refclk; int rate_code, i, ret; unsigned long rate; u32 reg; void *usbss; bool vbus_divider; struct regmap *syscon; struct ofnode_phandle_args args; usbss = dev_remap_addr_index(dev, 0); if (IS_ERR(usbss)) { dev_err(dev, "can't map IOMEM resource\n"); return; } ret = clk_get_by_name(dev, "ref", &usb2_refclk); if (ret) { dev_err(dev, "can't get usb2_refclk\n"); return; } /* Calculate the rate code */ rate = clk_get_rate(&usb2_refclk); rate /= 1000; /* To KHz */ for (i = 0; i < ARRAY_SIZE(dwc3_ti_am62_rate_table); i++) { if (dwc3_ti_am62_rate_table[i] == rate) break; } if (i == ARRAY_SIZE(dwc3_ti_am62_rate_table)) { dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate); return; } rate_code = i; /* Read the syscon property */ syscon = syscon_regmap_lookup_by_phandle(dev, "ti,syscon-phy-pll-refclk"); if (IS_ERR(syscon)) { dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n"); return; } ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "ti,syscon-phy-pll-refclk", NULL, 1, 0, &args); if (ret) return; /* Program PHY PLL refclk by reading syscon property */ ret = regmap_update_bits(syscon, args.args[0], PHY_PLL_REFCLK_MASK, rate_code); if (ret) { dev_err(dev, "failed to set phy pll reference clock rate\n"); return; } /* VBUS divider select */ reg = readl(usbss + USBSS_PHY_CONFIG); vbus_divider = dev_read_bool(dev, "ti,vbus-divider"); if (vbus_divider) reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; writel(reg, usbss + USBSS_PHY_CONFIG); /* Set mode valid */ reg = readl(usbss + USBSS_MODE_CONTROL); reg |= USBSS_MODE_VALID; writel(reg, usbss + USBSS_MODE_CONTROL); } struct dwc3_glue_ops ti_am62_ops = { .glue_configure = dwc3_ti_am62_glue_configure, }; static const struct udevice_id dwc3_am62_match[] = { { .compatible = "ti,am62-usb", .data = (ulong)&ti_am62_ops }, { /* sentinel */ } }; U_BOOT_DRIVER(dwc3_am62_wrapper) = { .name = "dwc3-am62", .id = UCLASS_SIMPLE_BUS, .of_match = dwc3_am62_match, .bind = dwc3_glue_bind, .probe = dwc3_glue_probe, .remove = dwc3_glue_remove, .plat_auto = sizeof(struct dwc3_glue_data), };