cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dwc3-am62.c (8344B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * dwc3-am62.c - TI specific Glue layer for AM62 DWC3 USB Controller
      4 *
      5 * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com
      6 */
      7
      8#include <linux/init.h>
      9#include <linux/kernel.h>
     10#include <linux/module.h>
     11#include <linux/platform_device.h>
     12#include <linux/mfd/syscon.h>
     13#include <linux/of.h>
     14#include <linux/of_device.h>
     15#include <linux/pm_runtime.h>
     16#include <linux/clk.h>
     17#include <linux/regmap.h>
     18#include <linux/pinctrl/consumer.h>
     19
     20/* USB WRAPPER register offsets */
     21#define USBSS_PID			0x0
     22#define USBSS_OVERCURRENT_CTRL		0x4
     23#define USBSS_PHY_CONFIG		0x8
     24#define USBSS_PHY_TEST			0xc
     25#define USBSS_CORE_STAT			0x14
     26#define USBSS_HOST_VBUS_CTRL		0x18
     27#define USBSS_MODE_CONTROL		0x1c
     28#define USBSS_WAKEUP_CONFIG		0x30
     29#define USBSS_WAKEUP_STAT		0x34
     30#define USBSS_OVERRIDE_CONFIG		0x38
     31#define USBSS_IRQ_MISC_STATUS_RAW	0x430
     32#define USBSS_IRQ_MISC_STATUS		0x434
     33#define USBSS_IRQ_MISC_ENABLE_SET	0x438
     34#define USBSS_IRQ_MISC_ENABLE_CLR	0x43c
     35#define USBSS_IRQ_MISC_EOI		0x440
     36#define USBSS_INTR_TEST			0x490
     37#define USBSS_VBUS_FILTER		0x614
     38#define USBSS_VBUS_STAT			0x618
     39#define USBSS_DEBUG_CFG			0x708
     40#define USBSS_DEBUG_DATA		0x70c
     41#define USBSS_HOST_HUB_CTRL		0x714
     42
     43/* PHY CONFIG register bits */
     44#define USBSS_PHY_VBUS_SEL_MASK		GENMASK(2, 1)
     45#define USBSS_PHY_VBUS_SEL_SHIFT	1
     46#define USBSS_PHY_LANE_REVERSE		BIT(0)
     47
     48/* MODE CONTROL register bits */
     49#define USBSS_MODE_VALID	BIT(0)
     50
     51/* WAKEUP CONFIG register bits */
     52#define USBSS_WAKEUP_CFG_OVERCURRENT_EN	BIT(3)
     53#define USBSS_WAKEUP_CFG_LINESTATE_EN	BIT(2)
     54#define USBSS_WAKEUP_CFG_SESSVALID_EN	BIT(1)
     55#define USBSS_WAKEUP_CFG_VBUSVALID_EN	BIT(0)
     56
     57/* WAKEUP STAT register bits */
     58#define USBSS_WAKEUP_STAT_OVERCURRENT	BIT(4)
     59#define USBSS_WAKEUP_STAT_LINESTATE	BIT(3)
     60#define USBSS_WAKEUP_STAT_SESSVALID	BIT(2)
     61#define USBSS_WAKEUP_STAT_VBUSVALID	BIT(1)
     62#define USBSS_WAKEUP_STAT_CLR		BIT(0)
     63
     64/* IRQ_MISC_STATUS_RAW register bits */
     65#define USBSS_IRQ_MISC_RAW_VBUSVALID	BIT(22)
     66#define USBSS_IRQ_MISC_RAW_SESSVALID	BIT(20)
     67
     68/* IRQ_MISC_STATUS register bits */
     69#define USBSS_IRQ_MISC_VBUSVALID	BIT(22)
     70#define USBSS_IRQ_MISC_SESSVALID	BIT(20)
     71
     72/* IRQ_MISC_ENABLE_SET register bits */
     73#define USBSS_IRQ_MISC_ENABLE_SET_VBUSVALID	BIT(22)
     74#define USBSS_IRQ_MISC_ENABLE_SET_SESSVALID	BIT(20)
     75
     76/* IRQ_MISC_ENABLE_CLR register bits */
     77#define USBSS_IRQ_MISC_ENABLE_CLR_VBUSVALID	BIT(22)
     78#define USBSS_IRQ_MISC_ENABLE_CLR_SESSVALID	BIT(20)
     79
     80/* IRQ_MISC_EOI register bits */
     81#define USBSS_IRQ_MISC_EOI_VECTOR	BIT(0)
     82
     83/* VBUS_STAT register bits */
     84#define USBSS_VBUS_STAT_SESSVALID	BIT(2)
     85#define USBSS_VBUS_STAT_VBUSVALID	BIT(0)
     86
     87/* Mask for PHY PLL REFCLK */
     88#define PHY_PLL_REFCLK_MASK	GENMASK(3, 0)
     89
     90#define DWC3_AM62_AUTOSUSPEND_DELAY	100
     91
     92struct dwc3_data {
     93	struct device *dev;
     94	void __iomem *usbss;
     95	struct clk *usb2_refclk;
     96	int rate_code;
     97	struct regmap *syscon;
     98	unsigned int offset;
     99	unsigned int vbus_divider;
    100};
    101
    102static const int dwc3_ti_rate_table[] = {	/* in KHZ */
    103	9600,
    104	10000,
    105	12000,
    106	19200,
    107	20000,
    108	24000,
    109	25000,
    110	26000,
    111	38400,
    112	40000,
    113	58000,
    114	50000,
    115	52000,
    116};
    117
    118static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
    119{
    120	return readl((data->usbss) + offset);
    121}
    122
    123static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
    124{
    125	writel(value, (data->usbss) + offset);
    126}
    127
    128static int phy_syscon_pll_refclk(struct dwc3_data *data)
    129{
    130	struct device *dev = data->dev;
    131	struct device_node *node = dev->of_node;
    132	struct of_phandle_args args;
    133	struct regmap *syscon;
    134	int ret;
    135
    136	syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
    137	if (IS_ERR(syscon)) {
    138		dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
    139		return PTR_ERR(syscon);
    140	}
    141
    142	data->syscon = syscon;
    143
    144	ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
    145					       0, &args);
    146	if (ret)
    147		return ret;
    148
    149	data->offset = args.args[0];
    150
    151	ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
    152	if (ret) {
    153		dev_err(dev, "failed to set phy pll reference clock rate\n");
    154		return ret;
    155	}
    156
    157	return 0;
    158}
    159
    160static int dwc3_ti_probe(struct platform_device *pdev)
    161{
    162	struct device *dev = &pdev->dev;
    163	struct device_node *node = pdev->dev.of_node;
    164	struct dwc3_data *data;
    165	int i, ret;
    166	unsigned long rate;
    167	u32 reg;
    168
    169	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    170	if (!data)
    171		return -ENOMEM;
    172
    173	data->dev = dev;
    174	platform_set_drvdata(pdev, data);
    175
    176	data->usbss = devm_platform_ioremap_resource(pdev, 0);
    177	if (IS_ERR(data->usbss)) {
    178		dev_err(dev, "can't map IOMEM resource\n");
    179		return PTR_ERR(data->usbss);
    180	}
    181
    182	data->usb2_refclk = devm_clk_get(dev, "ref");
    183	if (IS_ERR(data->usb2_refclk)) {
    184		dev_err(dev, "can't get usb2_refclk\n");
    185		return PTR_ERR(data->usb2_refclk);
    186	}
    187
    188	/* Calculate the rate code */
    189	rate = clk_get_rate(data->usb2_refclk);
    190	rate /= 1000;	// To KHz
    191	for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
    192		if (dwc3_ti_rate_table[i] == rate)
    193			break;
    194	}
    195
    196	if (i == ARRAY_SIZE(dwc3_ti_rate_table)) {
    197		dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
    198		ret = -EINVAL;
    199		goto err_clk_disable;
    200	}
    201
    202	data->rate_code = i;
    203
    204	/* Read the syscon property and set the rate code */
    205	ret = phy_syscon_pll_refclk(data);
    206	if (ret)
    207		goto err_clk_disable;
    208
    209	/* VBUS divider select */
    210	data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
    211	reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG);
    212	if (data->vbus_divider)
    213		reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
    214
    215	dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
    216
    217	pm_runtime_set_active(dev);
    218	pm_runtime_enable(dev);
    219	/*
    220	 * Don't ignore its dependencies with its children
    221	 */
    222	pm_suspend_ignore_children(dev, false);
    223	clk_prepare_enable(data->usb2_refclk);
    224	pm_runtime_get_noresume(dev);
    225
    226	ret = of_platform_populate(node, NULL, NULL, dev);
    227	if (ret) {
    228		dev_err(dev, "failed to create dwc3 core: %d\n", ret);
    229		goto err_pm_disable;
    230	}
    231
    232	/* Set mode valid bit to indicate role is valid */
    233	reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
    234	reg |= USBSS_MODE_VALID;
    235	dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
    236
    237	/* Setting up autosuspend */
    238	pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
    239	pm_runtime_use_autosuspend(dev);
    240	pm_runtime_mark_last_busy(dev);
    241	pm_runtime_put_autosuspend(dev);
    242	return 0;
    243
    244err_pm_disable:
    245	clk_disable_unprepare(data->usb2_refclk);
    246	pm_runtime_disable(dev);
    247	pm_runtime_set_suspended(dev);
    248err_clk_disable:
    249	clk_put(data->usb2_refclk);
    250	return ret;
    251}
    252
    253static int dwc3_ti_remove_core(struct device *dev, void *c)
    254{
    255	struct platform_device *pdev = to_platform_device(dev);
    256
    257	platform_device_unregister(pdev);
    258	return 0;
    259}
    260
    261static int dwc3_ti_remove(struct platform_device *pdev)
    262{
    263	struct device *dev = &pdev->dev;
    264	struct dwc3_data *data = platform_get_drvdata(pdev);
    265	u32 reg;
    266
    267	device_for_each_child(dev, NULL, dwc3_ti_remove_core);
    268
    269	/* Clear mode valid bit */
    270	reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
    271	reg &= ~USBSS_MODE_VALID;
    272	dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
    273
    274	pm_runtime_put_sync(dev);
    275	clk_disable_unprepare(data->usb2_refclk);
    276	pm_runtime_disable(dev);
    277	pm_runtime_set_suspended(dev);
    278
    279	clk_put(data->usb2_refclk);
    280	platform_set_drvdata(pdev, NULL);
    281	return 0;
    282}
    283
    284#ifdef CONFIG_PM
    285static int dwc3_ti_suspend_common(struct device *dev)
    286{
    287	struct dwc3_data *data = dev_get_drvdata(dev);
    288
    289	clk_disable_unprepare(data->usb2_refclk);
    290
    291	return 0;
    292}
    293
    294static int dwc3_ti_resume_common(struct device *dev)
    295{
    296	struct dwc3_data *data = dev_get_drvdata(dev);
    297
    298	clk_prepare_enable(data->usb2_refclk);
    299
    300	return 0;
    301}
    302
    303static UNIVERSAL_DEV_PM_OPS(dwc3_ti_pm_ops, dwc3_ti_suspend_common,
    304			    dwc3_ti_resume_common, NULL);
    305
    306#define DEV_PM_OPS	(&dwc3_ti_pm_ops)
    307#else
    308#define DEV_PM_OPS	NULL
    309#endif /* CONFIG_PM */
    310
    311static const struct of_device_id dwc3_ti_of_match[] = {
    312	{ .compatible = "ti,am62-usb"},
    313	{},
    314};
    315MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
    316
    317static struct platform_driver dwc3_ti_driver = {
    318	.probe		= dwc3_ti_probe,
    319	.remove		= dwc3_ti_remove,
    320	.driver		= {
    321		.name	= "dwc3-am62",
    322		.pm	= DEV_PM_OPS,
    323		.of_match_table = dwc3_ti_of_match,
    324	},
    325};
    326
    327module_platform_driver(dwc3_ti_driver);
    328
    329MODULE_ALIAS("platform:dwc3-am62");
    330MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>");
    331MODULE_LICENSE("GPL");
    332MODULE_DESCRIPTION("DesignWare USB3 TI Glue Layer");