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