1 // SPDX-License-Identifier: GPL-2.0 2 /** 3 * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller 4 * 5 * Copyright (C) 2023 StarFive Technology Co., Ltd. 6 * 7 * Author: Minda Chen <minda.chen@starfivetech.com> 8 */ 9 10 #include <linux/bits.h> 11 #include <linux/clk.h> 12 #include <linux/module.h> 13 #include <linux/mfd/syscon.h> 14 #include <linux/kernel.h> 15 #include <linux/platform_device.h> 16 #include <linux/io.h> 17 #include <linux/of_platform.h> 18 #include <linux/reset.h> 19 #include <linux/regmap.h> 20 #include <linux/usb/otg.h> 21 #include "core.h" 22 23 #define USB_STRAP_HOST BIT(17) 24 #define USB_STRAP_DEVICE BIT(18) 25 #define USB_STRAP_MASK GENMASK(18, 16) 26 27 #define USB_SUSPENDM_HOST BIT(19) 28 #define USB_SUSPENDM_MASK BIT(19) 29 30 #define USB_MISC_CFG_MASK GENMASK(23, 20) 31 #define USB_SUSPENDM_BYPS BIT(20) 32 #define USB_PLL_EN BIT(22) 33 #define USB_REFCLK_MODE BIT(23) 34 35 struct cdns_starfive { 36 struct device *dev; 37 struct regmap *stg_syscon; 38 struct reset_control *resets; 39 struct clk_bulk_data *clks; 40 int num_clks; 41 u32 stg_usb_mode; 42 }; 43 44 static void cdns_mode_init(struct platform_device *pdev, 45 struct cdns_starfive *data) 46 { 47 enum usb_dr_mode mode; 48 49 regmap_update_bits(data->stg_syscon, data->stg_usb_mode, 50 USB_MISC_CFG_MASK, 51 USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); 52 53 /* dr mode setting */ 54 mode = usb_get_dr_mode(&pdev->dev); 55 56 switch (mode) { 57 case USB_DR_MODE_HOST: 58 regmap_update_bits(data->stg_syscon, 59 data->stg_usb_mode, 60 USB_STRAP_MASK, 61 USB_STRAP_HOST); 62 regmap_update_bits(data->stg_syscon, 63 data->stg_usb_mode, 64 USB_SUSPENDM_MASK, 65 USB_SUSPENDM_HOST); 66 break; 67 68 case USB_DR_MODE_PERIPHERAL: 69 regmap_update_bits(data->stg_syscon, data->stg_usb_mode, 70 USB_STRAP_MASK, USB_STRAP_DEVICE); 71 regmap_update_bits(data->stg_syscon, data->stg_usb_mode, 72 USB_SUSPENDM_MASK, 0); 73 break; 74 default: 75 break; 76 } 77 } 78 79 static int cdns_clk_rst_init(struct cdns_starfive *data) 80 { 81 int ret; 82 83 ret = clk_bulk_prepare_enable(data->num_clks, data->clks); 84 if (ret) 85 return dev_err_probe(data->dev, ret, 86 "failed to enable clocks\n"); 87 88 ret = reset_control_deassert(data->resets); 89 if (ret) { 90 dev_err(data->dev, "failed to reset clocks\n"); 91 goto err_clk_init; 92 } 93 94 return ret; 95 96 err_clk_init: 97 clk_bulk_disable_unprepare(data->num_clks, data->clks); 98 return ret; 99 } 100 101 static void cdns_clk_rst_deinit(struct cdns_starfive *data) 102 { 103 reset_control_assert(data->resets); 104 clk_bulk_disable_unprepare(data->num_clks, data->clks); 105 } 106 107 static int cdns_starfive_probe(struct platform_device *pdev) 108 { 109 struct device *dev = &pdev->dev; 110 struct cdns_starfive *data; 111 unsigned int args; 112 int ret; 113 114 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 115 if (!data) 116 return -ENOMEM; 117 118 data->dev = dev; 119 120 data->stg_syscon = 121 syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, 122 "starfive,stg-syscon", 1, &args); 123 124 if (IS_ERR(data->stg_syscon)) 125 return dev_err_probe(dev, PTR_ERR(data->stg_syscon), 126 "Failed to parse starfive,stg-syscon\n"); 127 128 data->stg_usb_mode = args; 129 130 data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks); 131 if (data->num_clks < 0) 132 return dev_err_probe(data->dev, -ENODEV, 133 "Failed to get clocks\n"); 134 135 data->resets = devm_reset_control_array_get_exclusive(data->dev); 136 if (IS_ERR(data->resets)) 137 return dev_err_probe(data->dev, PTR_ERR(data->resets), 138 "Failed to get resets"); 139 140 cdns_mode_init(pdev, data); 141 ret = cdns_clk_rst_init(data); 142 if (ret) 143 return ret; 144 145 ret = of_platform_populate(dev->of_node, NULL, NULL, dev); 146 if (ret) { 147 dev_err(dev, "Failed to create children\n"); 148 cdns_clk_rst_deinit(data); 149 return ret; 150 } 151 152 device_set_wakeup_capable(dev, true); 153 pm_runtime_set_active(dev); 154 pm_runtime_enable(dev); 155 platform_set_drvdata(pdev, data); 156 157 return 0; 158 } 159 160 static int cdns_starfive_remove_core(struct device *dev, void *c) 161 { 162 struct platform_device *pdev = to_platform_device(dev); 163 164 platform_device_unregister(pdev); 165 166 return 0; 167 } 168 169 static int cdns_starfive_remove(struct platform_device *pdev) 170 { 171 struct device *dev = &pdev->dev; 172 struct cdns_starfive *data = dev_get_drvdata(dev); 173 174 pm_runtime_get_sync(dev); 175 device_for_each_child(dev, NULL, cdns_starfive_remove_core); 176 177 pm_runtime_disable(dev); 178 pm_runtime_put_noidle(dev); 179 cdns_clk_rst_deinit(data); 180 platform_set_drvdata(pdev, NULL); 181 182 return 0; 183 } 184 185 #ifdef CONFIG_PM 186 static int cdns_starfive_runtime_resume(struct device *dev) 187 { 188 struct cdns_starfive *data = dev_get_drvdata(dev); 189 190 return clk_bulk_prepare_enable(data->num_clks, data->clks); 191 } 192 193 static int cdns_starfive_runtime_suspend(struct device *dev) 194 { 195 struct cdns_starfive *data = dev_get_drvdata(dev); 196 197 clk_bulk_disable_unprepare(data->num_clks, data->clks); 198 199 return 0; 200 } 201 202 #ifdef CONFIG_PM_SLEEP 203 static int cdns_starfive_resume(struct device *dev) 204 { 205 struct cdns_starfive *data = dev_get_drvdata(dev); 206 207 return cdns_clk_rst_init(data); 208 } 209 210 static int cdns_starfive_suspend(struct device *dev) 211 { 212 struct cdns_starfive *data = dev_get_drvdata(dev); 213 214 cdns_clk_rst_deinit(data); 215 216 return 0; 217 } 218 #endif 219 #endif 220 221 static const struct dev_pm_ops cdns_starfive_pm_ops = { 222 SET_RUNTIME_PM_OPS(cdns_starfive_runtime_suspend, 223 cdns_starfive_runtime_resume, NULL) 224 SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume) 225 }; 226 227 static const struct of_device_id cdns_starfive_of_match[] = { 228 { .compatible = "starfive,jh7110-usb", }, 229 { /* sentinel */ } 230 }; 231 MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); 232 233 static struct platform_driver cdns_starfive_driver = { 234 .probe = cdns_starfive_probe, 235 .remove = cdns_starfive_remove, 236 .driver = { 237 .name = "cdns3-starfive", 238 .of_match_table = cdns_starfive_of_match, 239 .pm = &cdns_starfive_pm_ops, 240 }, 241 }; 242 module_platform_driver(cdns_starfive_driver); 243 244 MODULE_ALIAS("platform:cdns3-starfive"); 245 MODULE_LICENSE("GPL v2"); 246 MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer"); 247