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-imx8mp.c (10903B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * dwc3-imx8mp.c - NXP imx8mp Specific Glue layer
      4 *
      5 * Copyright (c) 2020 NXP.
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/interrupt.h>
     10#include <linux/io.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/of_platform.h>
     14#include <linux/platform_device.h>
     15#include <linux/pm_runtime.h>
     16
     17#include "core.h"
     18
     19/* USB wakeup registers */
     20#define USB_WAKEUP_CTRL			0x00
     21
     22/* Global wakeup interrupt enable, also used to clear interrupt */
     23#define USB_WAKEUP_EN			BIT(31)
     24/* Wakeup from connect or disconnect, only for superspeed */
     25#define USB_WAKEUP_SS_CONN		BIT(5)
     26/* 0 select vbus_valid, 1 select sessvld */
     27#define USB_WAKEUP_VBUS_SRC_SESS_VAL	BIT(4)
     28/* Enable signal for wake up from u3 state */
     29#define USB_WAKEUP_U3_EN		BIT(3)
     30/* Enable signal for wake up from id change */
     31#define USB_WAKEUP_ID_EN		BIT(2)
     32/* Enable signal for wake up from vbus change */
     33#define	USB_WAKEUP_VBUS_EN		BIT(1)
     34/* Enable signal for wake up from dp/dm change */
     35#define USB_WAKEUP_DPDM_EN		BIT(0)
     36
     37#define USB_WAKEUP_EN_MASK		GENMASK(5, 0)
     38
     39/* USB glue registers */
     40#define USB_CTRL0		0x00
     41#define USB_CTRL1		0x04
     42
     43#define USB_CTRL0_PORTPWR_EN	BIT(12) /* 1 - PPC enabled (default) */
     44#define USB_CTRL0_USB3_FIXED	BIT(22) /* 1 - USB3 permanent attached */
     45#define USB_CTRL0_USB2_FIXED	BIT(23) /* 1 - USB2 permanent attached */
     46
     47#define USB_CTRL1_OC_POLARITY	BIT(16) /* 0 - HIGH / 1 - LOW */
     48#define USB_CTRL1_PWR_POLARITY	BIT(17) /* 0 - HIGH / 1 - LOW */
     49
     50struct dwc3_imx8mp {
     51	struct device			*dev;
     52	struct platform_device		*dwc3;
     53	void __iomem			*hsio_blk_base;
     54	void __iomem			*glue_base;
     55	struct clk			*hsio_clk;
     56	struct clk			*suspend_clk;
     57	int				irq;
     58	bool				pm_suspended;
     59	bool				wakeup_pending;
     60};
     61
     62static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx)
     63{
     64	struct device *dev = dwc3_imx->dev;
     65	u32 value;
     66
     67	if (!dwc3_imx->glue_base)
     68		return;
     69
     70	value = readl(dwc3_imx->glue_base + USB_CTRL0);
     71
     72	if (device_property_read_bool(dev, "fsl,permanently-attached"))
     73		value |= (USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
     74	else
     75		value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED);
     76
     77	if (device_property_read_bool(dev, "fsl,disable-port-power-control"))
     78		value &= ~(USB_CTRL0_PORTPWR_EN);
     79	else
     80		value |= USB_CTRL0_PORTPWR_EN;
     81
     82	writel(value, dwc3_imx->glue_base + USB_CTRL0);
     83
     84	value = readl(dwc3_imx->glue_base + USB_CTRL1);
     85	if (device_property_read_bool(dev, "fsl,over-current-active-low"))
     86		value |= USB_CTRL1_OC_POLARITY;
     87	else
     88		value &= ~USB_CTRL1_OC_POLARITY;
     89
     90	if (device_property_read_bool(dev, "fsl,power-active-low"))
     91		value |= USB_CTRL1_PWR_POLARITY;
     92	else
     93		value &= ~USB_CTRL1_PWR_POLARITY;
     94
     95	writel(value, dwc3_imx->glue_base + USB_CTRL1);
     96}
     97
     98static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx)
     99{
    100	struct dwc3	*dwc3 = platform_get_drvdata(dwc3_imx->dwc3);
    101	u32		val;
    102
    103	if (!dwc3)
    104		return;
    105
    106	val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
    107
    108	if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci)
    109		val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN |
    110		       USB_WAKEUP_U3_EN | USB_WAKEUP_DPDM_EN;
    111	else if (dwc3->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
    112		val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN |
    113		       USB_WAKEUP_VBUS_SRC_SESS_VAL;
    114
    115	writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
    116}
    117
    118static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx)
    119{
    120	u32 val;
    121
    122	val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
    123	val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK);
    124	writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL);
    125}
    126
    127static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx)
    128{
    129	struct dwc3_imx8mp	*dwc3_imx = _dwc3_imx;
    130	struct dwc3		*dwc = platform_get_drvdata(dwc3_imx->dwc3);
    131
    132	if (!dwc3_imx->pm_suspended)
    133		return IRQ_HANDLED;
    134
    135	disable_irq_nosync(dwc3_imx->irq);
    136	dwc3_imx->wakeup_pending = true;
    137
    138	if ((dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc->xhci)
    139		pm_runtime_resume(&dwc->xhci->dev);
    140	else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE)
    141		pm_runtime_get(dwc->dev);
    142
    143	return IRQ_HANDLED;
    144}
    145
    146static int dwc3_imx8mp_probe(struct platform_device *pdev)
    147{
    148	struct device		*dev = &pdev->dev;
    149	struct device_node	*dwc3_np, *node = dev->of_node;
    150	struct dwc3_imx8mp	*dwc3_imx;
    151	struct resource		*res;
    152	int			err, irq;
    153
    154	if (!node) {
    155		dev_err(dev, "device node not found\n");
    156		return -EINVAL;
    157	}
    158
    159	dwc3_imx = devm_kzalloc(dev, sizeof(*dwc3_imx), GFP_KERNEL);
    160	if (!dwc3_imx)
    161		return -ENOMEM;
    162
    163	platform_set_drvdata(pdev, dwc3_imx);
    164
    165	dwc3_imx->dev = dev;
    166
    167	dwc3_imx->hsio_blk_base = devm_platform_ioremap_resource(pdev, 0);
    168	if (IS_ERR(dwc3_imx->hsio_blk_base))
    169		return PTR_ERR(dwc3_imx->hsio_blk_base);
    170
    171	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    172	if (!res) {
    173		dev_warn(dev, "Base address for glue layer missing. Continuing without, some features are missing though.");
    174	} else {
    175		dwc3_imx->glue_base = devm_ioremap_resource(dev, res);
    176		if (IS_ERR(dwc3_imx->glue_base))
    177			return PTR_ERR(dwc3_imx->glue_base);
    178	}
    179
    180	dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio");
    181	if (IS_ERR(dwc3_imx->hsio_clk)) {
    182		err = PTR_ERR(dwc3_imx->hsio_clk);
    183		dev_err(dev, "Failed to get hsio clk, err=%d\n", err);
    184		return err;
    185	}
    186
    187	err = clk_prepare_enable(dwc3_imx->hsio_clk);
    188	if (err) {
    189		dev_err(dev, "Failed to enable hsio clk, err=%d\n", err);
    190		return err;
    191	}
    192
    193	dwc3_imx->suspend_clk = devm_clk_get(dev, "suspend");
    194	if (IS_ERR(dwc3_imx->suspend_clk)) {
    195		err = PTR_ERR(dwc3_imx->suspend_clk);
    196		dev_err(dev, "Failed to get suspend clk, err=%d\n", err);
    197		goto disable_hsio_clk;
    198	}
    199
    200	err = clk_prepare_enable(dwc3_imx->suspend_clk);
    201	if (err) {
    202		dev_err(dev, "Failed to enable suspend clk, err=%d\n", err);
    203		goto disable_hsio_clk;
    204	}
    205
    206	irq = platform_get_irq(pdev, 0);
    207	if (irq < 0) {
    208		err = irq;
    209		goto disable_clks;
    210	}
    211	dwc3_imx->irq = irq;
    212
    213	imx8mp_configure_glue(dwc3_imx);
    214
    215	pm_runtime_set_active(dev);
    216	pm_runtime_enable(dev);
    217	err = pm_runtime_get_sync(dev);
    218	if (err < 0)
    219		goto disable_rpm;
    220
    221	dwc3_np = of_get_compatible_child(node, "snps,dwc3");
    222	if (!dwc3_np) {
    223		err = -ENODEV;
    224		dev_err(dev, "failed to find dwc3 core child\n");
    225		goto disable_rpm;
    226	}
    227
    228	err = of_platform_populate(node, NULL, NULL, dev);
    229	if (err) {
    230		dev_err(&pdev->dev, "failed to create dwc3 core\n");
    231		goto err_node_put;
    232	}
    233
    234	dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np);
    235	if (!dwc3_imx->dwc3) {
    236		dev_err(dev, "failed to get dwc3 platform device\n");
    237		err = -ENODEV;
    238		goto depopulate;
    239	}
    240	of_node_put(dwc3_np);
    241
    242	err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt,
    243					IRQF_ONESHOT, dev_name(dev), dwc3_imx);
    244	if (err) {
    245		dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err);
    246		goto depopulate;
    247	}
    248
    249	device_set_wakeup_capable(dev, true);
    250	pm_runtime_put(dev);
    251
    252	return 0;
    253
    254depopulate:
    255	of_platform_depopulate(dev);
    256err_node_put:
    257	of_node_put(dwc3_np);
    258disable_rpm:
    259	pm_runtime_disable(dev);
    260	pm_runtime_put_noidle(dev);
    261disable_clks:
    262	clk_disable_unprepare(dwc3_imx->suspend_clk);
    263disable_hsio_clk:
    264	clk_disable_unprepare(dwc3_imx->hsio_clk);
    265
    266	return err;
    267}
    268
    269static int dwc3_imx8mp_remove(struct platform_device *pdev)
    270{
    271	struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev);
    272	struct device *dev = &pdev->dev;
    273
    274	pm_runtime_get_sync(dev);
    275	of_platform_depopulate(dev);
    276
    277	clk_disable_unprepare(dwc3_imx->suspend_clk);
    278	clk_disable_unprepare(dwc3_imx->hsio_clk);
    279
    280	pm_runtime_disable(dev);
    281	pm_runtime_put_noidle(dev);
    282	platform_set_drvdata(pdev, NULL);
    283
    284	return 0;
    285}
    286
    287static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx,
    288					      pm_message_t msg)
    289{
    290	if (dwc3_imx->pm_suspended)
    291		return 0;
    292
    293	/* Wakeup enable */
    294	if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc3_imx->dev))
    295		dwc3_imx8mp_wakeup_enable(dwc3_imx);
    296
    297	dwc3_imx->pm_suspended = true;
    298
    299	return 0;
    300}
    301
    302static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx,
    303					     pm_message_t msg)
    304{
    305	struct dwc3	*dwc = platform_get_drvdata(dwc3_imx->dwc3);
    306	int ret = 0;
    307
    308	if (!dwc3_imx->pm_suspended)
    309		return 0;
    310
    311	/* Wakeup disable */
    312	dwc3_imx8mp_wakeup_disable(dwc3_imx);
    313	dwc3_imx->pm_suspended = false;
    314
    315	/* Upon power loss any previous configuration is lost, restore it */
    316	imx8mp_configure_glue(dwc3_imx);
    317
    318	if (dwc3_imx->wakeup_pending) {
    319		dwc3_imx->wakeup_pending = false;
    320		if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) {
    321			pm_runtime_mark_last_busy(dwc->dev);
    322			pm_runtime_put_autosuspend(dwc->dev);
    323		} else {
    324			/*
    325			 * Add wait for xhci switch from suspend
    326			 * clock to normal clock to detect connection.
    327			 */
    328			usleep_range(9000, 10000);
    329		}
    330		enable_irq(dwc3_imx->irq);
    331	}
    332
    333	return ret;
    334}
    335
    336static int __maybe_unused dwc3_imx8mp_pm_suspend(struct device *dev)
    337{
    338	struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
    339	int ret;
    340
    341	ret = dwc3_imx8mp_suspend(dwc3_imx, PMSG_SUSPEND);
    342
    343	if (device_may_wakeup(dwc3_imx->dev))
    344		enable_irq_wake(dwc3_imx->irq);
    345	else
    346		clk_disable_unprepare(dwc3_imx->suspend_clk);
    347
    348	clk_disable_unprepare(dwc3_imx->hsio_clk);
    349	dev_dbg(dev, "dwc3 imx8mp pm suspend.\n");
    350
    351	return ret;
    352}
    353
    354static int __maybe_unused dwc3_imx8mp_pm_resume(struct device *dev)
    355{
    356	struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
    357	int ret;
    358
    359	if (device_may_wakeup(dwc3_imx->dev)) {
    360		disable_irq_wake(dwc3_imx->irq);
    361	} else {
    362		ret = clk_prepare_enable(dwc3_imx->suspend_clk);
    363		if (ret)
    364			return ret;
    365	}
    366
    367	ret = clk_prepare_enable(dwc3_imx->hsio_clk);
    368	if (ret)
    369		return ret;
    370
    371	ret = dwc3_imx8mp_resume(dwc3_imx, PMSG_RESUME);
    372
    373	pm_runtime_disable(dev);
    374	pm_runtime_set_active(dev);
    375	pm_runtime_enable(dev);
    376
    377	dev_dbg(dev, "dwc3 imx8mp pm resume.\n");
    378
    379	return ret;
    380}
    381
    382static int __maybe_unused dwc3_imx8mp_runtime_suspend(struct device *dev)
    383{
    384	struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
    385
    386	dev_dbg(dev, "dwc3 imx8mp runtime suspend.\n");
    387
    388	return dwc3_imx8mp_suspend(dwc3_imx, PMSG_AUTO_SUSPEND);
    389}
    390
    391static int __maybe_unused dwc3_imx8mp_runtime_resume(struct device *dev)
    392{
    393	struct dwc3_imx8mp *dwc3_imx = dev_get_drvdata(dev);
    394
    395	dev_dbg(dev, "dwc3 imx8mp runtime resume.\n");
    396
    397	return dwc3_imx8mp_resume(dwc3_imx, PMSG_AUTO_RESUME);
    398}
    399
    400static const struct dev_pm_ops dwc3_imx8mp_dev_pm_ops = {
    401	SET_SYSTEM_SLEEP_PM_OPS(dwc3_imx8mp_pm_suspend, dwc3_imx8mp_pm_resume)
    402	SET_RUNTIME_PM_OPS(dwc3_imx8mp_runtime_suspend,
    403			   dwc3_imx8mp_runtime_resume, NULL)
    404};
    405
    406static const struct of_device_id dwc3_imx8mp_of_match[] = {
    407	{ .compatible = "fsl,imx8mp-dwc3", },
    408	{},
    409};
    410MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match);
    411
    412static struct platform_driver dwc3_imx8mp_driver = {
    413	.probe		= dwc3_imx8mp_probe,
    414	.remove		= dwc3_imx8mp_remove,
    415	.driver		= {
    416		.name	= "imx8mp-dwc3",
    417		.pm	= &dwc3_imx8mp_dev_pm_ops,
    418		.of_match_table	= dwc3_imx8mp_of_match,
    419	},
    420};
    421
    422module_platform_driver(dwc3_imx8mp_driver);
    423
    424MODULE_ALIAS("platform:imx8mp-dwc3");
    425MODULE_AUTHOR("jun.li@nxp.com");
    426MODULE_LICENSE("GPL v2");
    427MODULE_DESCRIPTION("DesignWare USB3 imx8mp Glue Layer");