// SPDX-License-Identifier: GPL-2.0 /* * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller * * Copyright (C) 2024 StarFive Technology Co., Ltd. * * Author: Minda Chen */ #include #include #include #include #include #include #include #include #include #include #include "core.h" #define USB_STRAP_HOST BIT(17) #define USB_STRAP_DEVICE BIT(18) #define USB_STRAP_MASK GENMASK(18, 16) #define USB_SUSPENDM_HOST BIT(19) #define USB_SUSPENDM_MASK BIT(19) #define USB_MISC_CFG_MASK GENMASK(23, 20) #define USB_SUSPENDM_BYPS BIT(20) #define USB_PLL_EN BIT(22) #define USB_REFCLK_MODE BIT(23) struct cdns_starfive { struct udevice *dev; struct regmap *stg_syscon; struct reset_ctl_bulk resets; struct clk_bulk clks; u32 stg_usb_mode; enum usb_dr_mode mode; }; static void cdns_mode_init(struct cdns_starfive *data, enum usb_dr_mode mode) { unsigned int strap, suspendm; regmap_update_bits(data->stg_syscon, data->stg_usb_mode, USB_MISC_CFG_MASK, USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); switch (mode) { case USB_DR_MODE_HOST: strap = USB_STRAP_HOST; suspendm = USB_SUSPENDM_HOST; break; case USB_DR_MODE_PERIPHERAL: strap = USB_STRAP_DEVICE; suspendm = 0; break; default: return; } regmap_update_bits(data->stg_syscon, data->stg_usb_mode, USB_SUSPENDM_MASK | USB_STRAP_MASK, strap | suspendm); } static void cdns_clk_rst_deinit(struct cdns_starfive *data) { reset_assert_bulk(&data->resets); clk_disable_bulk(&data->clks); } static int cdns_clk_rst_init(struct cdns_starfive *data) { int ret; ret = clk_get_bulk(data->dev, &data->clks); if (ret) return ret; ret = reset_get_bulk(data->dev, &data->resets); if (ret) goto err_clk; ret = clk_enable_bulk(&data->clks); if (ret) { dev_err(data->dev, "clk enable failed: %d\n", ret); goto err_en_clk; } ret = reset_deassert_bulk(&data->resets); if (ret) { dev_err(data->dev, "reset deassert failed: %d\n", ret); goto err_reset; } return 0; err_reset: clk_disable_bulk(&data->clks); err_en_clk: reset_release_bulk(&data->resets); err_clk: clk_release_bulk(&data->clks); return ret; } static int cdns_starfive_get_syscon(struct cdns_starfive *data) { struct ofnode_phandle_args phandle; int ret; ret = dev_read_phandle_with_args(data->dev, "starfive,stg-syscon", NULL, 1, 0, &phandle); if (ret < 0) { dev_err(data->dev, "Can't get stg cfg phandle: %d\n", ret); return ret; } data->stg_syscon = syscon_node_to_regmap(phandle.node); if (IS_ERR(data->stg_syscon)) { dev_err(data->dev, "fail to get regmap: %d\n", (int)PTR_ERR(data->stg_syscon)); return PTR_ERR(data->stg_syscon); } data->stg_usb_mode = phandle.args[0]; return 0; } static int cdns_starfive_probe(struct udevice *dev) { struct cdns_starfive *data = dev_get_plat(dev); enum usb_dr_mode dr_mode; int ret; data->dev = dev; ret = cdns_starfive_get_syscon(data); if (ret) return ret; dr_mode = usb_get_dr_mode(dev_ofnode(dev)); data->mode = dr_mode; ret = cdns_clk_rst_init(data); if (ret) { dev_err(data->dev, "clk reset failed: %d\n", ret); return ret; } cdns_mode_init(data, dr_mode); return 0; } static int cdns_starfive_remove(struct udevice *dev) { struct cdns_starfive *data = dev_get_plat(dev); cdns_clk_rst_deinit(data); return 0; } static const struct udevice_id cdns_starfive_of_match[] = { { .compatible = "starfive,jh7110-usb", }, {}, }; U_BOOT_DRIVER(cdns_starfive) = { .name = "cdns-starfive", .id = UCLASS_NOP, .of_match = cdns_starfive_of_match, .bind = cdns3_bind, .probe = cdns_starfive_probe, .remove = cdns_starfive_remove, .plat_auto = sizeof(struct cdns_starfive), .flags = DM_FLAG_OS_PREPARE, };