162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cdns3-imx.c - NXP i.MX specific Glue layer for Cadence USB Controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bits.h> 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/of_platform.h> 1762306a36Sopenharmony_ci#include <linux/iopoll.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include "core.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define USB3_CORE_CTRL1 0x00 2262306a36Sopenharmony_ci#define USB3_CORE_CTRL2 0x04 2362306a36Sopenharmony_ci#define USB3_INT_REG 0x08 2462306a36Sopenharmony_ci#define USB3_CORE_STATUS 0x0c 2562306a36Sopenharmony_ci#define XHCI_DEBUG_LINK_ST 0x10 2662306a36Sopenharmony_ci#define XHCI_DEBUG_BUS 0x14 2762306a36Sopenharmony_ci#define USB3_SSPHY_CTRL1 0x40 2862306a36Sopenharmony_ci#define USB3_SSPHY_CTRL2 0x44 2962306a36Sopenharmony_ci#define USB3_SSPHY_STATUS 0x4c 3062306a36Sopenharmony_ci#define USB2_PHY_CTRL1 0x50 3162306a36Sopenharmony_ci#define USB2_PHY_CTRL2 0x54 3262306a36Sopenharmony_ci#define USB2_PHY_STATUS 0x5c 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Register bits definition */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* USB3_CORE_CTRL1 */ 3762306a36Sopenharmony_ci#define SW_RESET_MASK GENMASK(31, 26) 3862306a36Sopenharmony_ci#define PWR_SW_RESET BIT(31) 3962306a36Sopenharmony_ci#define APB_SW_RESET BIT(30) 4062306a36Sopenharmony_ci#define AXI_SW_RESET BIT(29) 4162306a36Sopenharmony_ci#define RW_SW_RESET BIT(28) 4262306a36Sopenharmony_ci#define PHY_SW_RESET BIT(27) 4362306a36Sopenharmony_ci#define PHYAHB_SW_RESET BIT(26) 4462306a36Sopenharmony_ci#define ALL_SW_RESET (PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \ 4562306a36Sopenharmony_ci RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET) 4662306a36Sopenharmony_ci#define OC_DISABLE BIT(9) 4762306a36Sopenharmony_ci#define MDCTRL_CLK_SEL BIT(7) 4862306a36Sopenharmony_ci#define MODE_STRAP_MASK (0x7) 4962306a36Sopenharmony_ci#define DEV_MODE (1 << 2) 5062306a36Sopenharmony_ci#define HOST_MODE (1 << 1) 5162306a36Sopenharmony_ci#define OTG_MODE (1 << 0) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* USB3_INT_REG */ 5462306a36Sopenharmony_ci#define CLK_125_REQ BIT(29) 5562306a36Sopenharmony_ci#define LPM_CLK_REQ BIT(28) 5662306a36Sopenharmony_ci#define DEVU3_WAEKUP_EN BIT(14) 5762306a36Sopenharmony_ci#define OTG_WAKEUP_EN BIT(12) 5862306a36Sopenharmony_ci#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */ 5962306a36Sopenharmony_ci#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* USB3_CORE_STATUS */ 6262306a36Sopenharmony_ci#define MDCTRL_CLK_STATUS BIT(15) 6362306a36Sopenharmony_ci#define DEV_POWER_ON_READY BIT(13) 6462306a36Sopenharmony_ci#define HOST_POWER_ON_READY BIT(12) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* USB3_SSPHY_STATUS */ 6762306a36Sopenharmony_ci#define CLK_VALID_MASK (0x3f << 26) 6862306a36Sopenharmony_ci#define CLK_VALID_COMPARE_BITS (0xf << 28) 6962306a36Sopenharmony_ci#define PHY_REFCLK_REQ (1 << 0) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* OTG registers definition */ 7262306a36Sopenharmony_ci#define OTGSTS 0x4 7362306a36Sopenharmony_ci/* OTGSTS */ 7462306a36Sopenharmony_ci#define OTG_NRDY BIT(11) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* xHCI registers definition */ 7762306a36Sopenharmony_ci#define XECP_PM_PMCSR 0x8018 7862306a36Sopenharmony_ci#define XECP_AUX_CTRL_REG1 0x8120 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* Register bits definition */ 8162306a36Sopenharmony_ci/* XECP_AUX_CTRL_REG1 */ 8262306a36Sopenharmony_ci#define CFG_RXDET_P3_EN BIT(15) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* XECP_PM_PMCSR */ 8562306a36Sopenharmony_ci#define PS_MASK GENMASK(1, 0) 8662306a36Sopenharmony_ci#define PS_D0 0 8762306a36Sopenharmony_ci#define PS_D1 1 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct cdns_imx { 9062306a36Sopenharmony_ci struct device *dev; 9162306a36Sopenharmony_ci void __iomem *noncore; 9262306a36Sopenharmony_ci struct clk_bulk_data *clks; 9362306a36Sopenharmony_ci int num_clks; 9462306a36Sopenharmony_ci struct platform_device *cdns3_pdev; 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic inline u32 cdns_imx_readl(struct cdns_imx *data, u32 offset) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return readl(data->noncore + offset); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci writel(value, data->noncore + offset); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic const struct clk_bulk_data imx_cdns3_core_clks[] = { 10862306a36Sopenharmony_ci { .id = "lpm" }, 10962306a36Sopenharmony_ci { .id = "bus" }, 11062306a36Sopenharmony_ci { .id = "aclk" }, 11162306a36Sopenharmony_ci { .id = "ipg" }, 11262306a36Sopenharmony_ci { .id = "core" }, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int cdns_imx_noncore_init(struct cdns_imx *data) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci u32 value; 11862306a36Sopenharmony_ci int ret; 11962306a36Sopenharmony_ci struct device *dev = data->dev; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci cdns_imx_writel(data, USB3_SSPHY_STATUS, CLK_VALID_MASK); 12262306a36Sopenharmony_ci udelay(1); 12362306a36Sopenharmony_ci ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value, 12462306a36Sopenharmony_ci (value & CLK_VALID_COMPARE_BITS) == CLK_VALID_COMPARE_BITS, 12562306a36Sopenharmony_ci 10, 100000); 12662306a36Sopenharmony_ci if (ret) { 12762306a36Sopenharmony_ci dev_err(dev, "wait clkvld timeout\n"); 12862306a36Sopenharmony_ci return ret; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_CTRL1); 13262306a36Sopenharmony_ci value |= ALL_SW_RESET; 13362306a36Sopenharmony_ci cdns_imx_writel(data, USB3_CORE_CTRL1, value); 13462306a36Sopenharmony_ci udelay(1); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_CTRL1); 13762306a36Sopenharmony_ci value = (value & ~MODE_STRAP_MASK) | OTG_MODE | OC_DISABLE; 13862306a36Sopenharmony_ci cdns_imx_writel(data, USB3_CORE_CTRL1, value); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_INT_REG); 14162306a36Sopenharmony_ci value |= HOST_INT1_EN | DEV_INT_EN; 14262306a36Sopenharmony_ci cdns_imx_writel(data, USB3_INT_REG, value); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_CTRL1); 14562306a36Sopenharmony_ci value &= ~ALL_SW_RESET; 14662306a36Sopenharmony_ci cdns_imx_writel(data, USB3_CORE_CTRL1, value); 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int cdns_imx_platform_suspend(struct device *dev, 15162306a36Sopenharmony_ci bool suspend, bool wakeup); 15262306a36Sopenharmony_cistatic struct cdns3_platform_data cdns_imx_pdata = { 15362306a36Sopenharmony_ci .platform_suspend = cdns_imx_platform_suspend, 15462306a36Sopenharmony_ci .quirks = CDNS3_DEFAULT_PM_RUNTIME_ALLOW, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct of_dev_auxdata cdns_imx_auxdata[] = { 15862306a36Sopenharmony_ci { 15962306a36Sopenharmony_ci .compatible = "cdns,usb3", 16062306a36Sopenharmony_ci .platform_data = &cdns_imx_pdata, 16162306a36Sopenharmony_ci }, 16262306a36Sopenharmony_ci {}, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int cdns_imx_probe(struct platform_device *pdev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 16862306a36Sopenharmony_ci struct device_node *node = dev->of_node; 16962306a36Sopenharmony_ci struct cdns_imx *data; 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (!node) 17362306a36Sopenharmony_ci return -ENODEV; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 17662306a36Sopenharmony_ci if (!data) 17762306a36Sopenharmony_ci return -ENOMEM; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 18062306a36Sopenharmony_ci data->dev = dev; 18162306a36Sopenharmony_ci data->noncore = devm_platform_ioremap_resource(pdev, 0); 18262306a36Sopenharmony_ci if (IS_ERR(data->noncore)) { 18362306a36Sopenharmony_ci dev_err(dev, "can't map IOMEM resource\n"); 18462306a36Sopenharmony_ci return PTR_ERR(data->noncore); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci data->num_clks = ARRAY_SIZE(imx_cdns3_core_clks); 18862306a36Sopenharmony_ci data->clks = devm_kmemdup(dev, imx_cdns3_core_clks, 18962306a36Sopenharmony_ci sizeof(imx_cdns3_core_clks), GFP_KERNEL); 19062306a36Sopenharmony_ci if (!data->clks) 19162306a36Sopenharmony_ci return -ENOMEM; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = devm_clk_bulk_get(dev, data->num_clks, data->clks); 19462306a36Sopenharmony_ci if (ret) 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(data->num_clks, data->clks); 19862306a36Sopenharmony_ci if (ret) 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = cdns_imx_noncore_init(data); 20262306a36Sopenharmony_ci if (ret) 20362306a36Sopenharmony_ci goto err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = of_platform_populate(node, NULL, cdns_imx_auxdata, dev); 20662306a36Sopenharmony_ci if (ret) { 20762306a36Sopenharmony_ci dev_err(dev, "failed to create children: %d\n", ret); 20862306a36Sopenharmony_ci goto err; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci device_set_wakeup_capable(dev, true); 21262306a36Sopenharmony_ci pm_runtime_set_active(dev); 21362306a36Sopenharmony_ci pm_runtime_enable(dev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_cierr: 21762306a36Sopenharmony_ci clk_bulk_disable_unprepare(data->num_clks, data->clks); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void cdns_imx_remove(struct platform_device *pdev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 22462306a36Sopenharmony_ci struct cdns_imx *data = dev_get_drvdata(dev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci pm_runtime_get_sync(dev); 22762306a36Sopenharmony_ci of_platform_depopulate(dev); 22862306a36Sopenharmony_ci clk_bulk_disable_unprepare(data->num_clks, data->clks); 22962306a36Sopenharmony_ci pm_runtime_disable(dev); 23062306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 23162306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#ifdef CONFIG_PM 23562306a36Sopenharmony_cistatic void cdns3_set_wakeup(struct cdns_imx *data, bool enable) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci u32 value; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_INT_REG); 24062306a36Sopenharmony_ci if (enable) 24162306a36Sopenharmony_ci value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN; 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci cdns_imx_writel(data, USB3_INT_REG, value); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int cdns_imx_platform_suspend(struct device *dev, 24962306a36Sopenharmony_ci bool suspend, bool wakeup) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct cdns *cdns = dev_get_drvdata(dev); 25262306a36Sopenharmony_ci struct device *parent = dev->parent; 25362306a36Sopenharmony_ci struct cdns_imx *data = dev_get_drvdata(parent); 25462306a36Sopenharmony_ci void __iomem *otg_regs = (void __iomem *)(cdns->otg_regs); 25562306a36Sopenharmony_ci void __iomem *xhci_regs = cdns->xhci_regs; 25662306a36Sopenharmony_ci u32 value; 25762306a36Sopenharmony_ci int ret = 0; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (cdns->role != USB_ROLE_HOST) 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (suspend) { 26362306a36Sopenharmony_ci /* SW request low power when all usb ports allow to it ??? */ 26462306a36Sopenharmony_ci value = readl(xhci_regs + XECP_PM_PMCSR); 26562306a36Sopenharmony_ci value &= ~PS_MASK; 26662306a36Sopenharmony_ci value |= PS_D1; 26762306a36Sopenharmony_ci writel(value, xhci_regs + XECP_PM_PMCSR); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* mdctrl_clk_sel */ 27062306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_CTRL1); 27162306a36Sopenharmony_ci value |= MDCTRL_CLK_SEL; 27262306a36Sopenharmony_ci cdns_imx_writel(data, USB3_CORE_CTRL1, value); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* wait for mdctrl_clk_status */ 27562306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_STATUS); 27662306a36Sopenharmony_ci ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, 27762306a36Sopenharmony_ci (value & MDCTRL_CLK_STATUS) == MDCTRL_CLK_STATUS, 27862306a36Sopenharmony_ci 10, 100000); 27962306a36Sopenharmony_ci if (ret) 28062306a36Sopenharmony_ci dev_warn(parent, "wait mdctrl_clk_status timeout\n"); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* wait lpm_clk_req to be 0 */ 28362306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_INT_REG); 28462306a36Sopenharmony_ci ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, 28562306a36Sopenharmony_ci (value & LPM_CLK_REQ) != LPM_CLK_REQ, 28662306a36Sopenharmony_ci 10, 100000); 28762306a36Sopenharmony_ci if (ret) 28862306a36Sopenharmony_ci dev_warn(parent, "wait lpm_clk_req timeout\n"); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* wait phy_refclk_req to be 0 */ 29162306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_SSPHY_STATUS); 29262306a36Sopenharmony_ci ret = readl_poll_timeout(data->noncore + USB3_SSPHY_STATUS, value, 29362306a36Sopenharmony_ci (value & PHY_REFCLK_REQ) != PHY_REFCLK_REQ, 29462306a36Sopenharmony_ci 10, 100000); 29562306a36Sopenharmony_ci if (ret) 29662306a36Sopenharmony_ci dev_warn(parent, "wait phy_refclk_req timeout\n"); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci cdns3_set_wakeup(data, wakeup); 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci cdns3_set_wakeup(data, false); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* SW request D0 */ 30362306a36Sopenharmony_ci value = readl(xhci_regs + XECP_PM_PMCSR); 30462306a36Sopenharmony_ci value &= ~PS_MASK; 30562306a36Sopenharmony_ci value |= PS_D0; 30662306a36Sopenharmony_ci writel(value, xhci_regs + XECP_PM_PMCSR); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* clr CFG_RXDET_P3_EN */ 30962306a36Sopenharmony_ci value = readl(xhci_regs + XECP_AUX_CTRL_REG1); 31062306a36Sopenharmony_ci value &= ~CFG_RXDET_P3_EN; 31162306a36Sopenharmony_ci writel(value, xhci_regs + XECP_AUX_CTRL_REG1); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* clear mdctrl_clk_sel */ 31462306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_CTRL1); 31562306a36Sopenharmony_ci value &= ~MDCTRL_CLK_SEL; 31662306a36Sopenharmony_ci cdns_imx_writel(data, USB3_CORE_CTRL1, value); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* wait CLK_125_REQ to be 1 */ 31962306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_INT_REG); 32062306a36Sopenharmony_ci ret = readl_poll_timeout(data->noncore + USB3_INT_REG, value, 32162306a36Sopenharmony_ci (value & CLK_125_REQ) == CLK_125_REQ, 32262306a36Sopenharmony_ci 10, 100000); 32362306a36Sopenharmony_ci if (ret) 32462306a36Sopenharmony_ci dev_warn(parent, "wait CLK_125_REQ timeout\n"); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* wait for mdctrl_clk_status is cleared */ 32762306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_STATUS); 32862306a36Sopenharmony_ci ret = readl_poll_timeout(data->noncore + USB3_CORE_STATUS, value, 32962306a36Sopenharmony_ci (value & MDCTRL_CLK_STATUS) != MDCTRL_CLK_STATUS, 33062306a36Sopenharmony_ci 10, 100000); 33162306a36Sopenharmony_ci if (ret) 33262306a36Sopenharmony_ci dev_warn(parent, "wait mdctrl_clk_status cleared timeout\n"); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Wait until OTG_NRDY is 0 */ 33562306a36Sopenharmony_ci value = readl(otg_regs + OTGSTS); 33662306a36Sopenharmony_ci ret = readl_poll_timeout(otg_regs + OTGSTS, value, 33762306a36Sopenharmony_ci (value & OTG_NRDY) != OTG_NRDY, 33862306a36Sopenharmony_ci 10, 100000); 33962306a36Sopenharmony_ci if (ret) 34062306a36Sopenharmony_ci dev_warn(parent, "wait OTG ready timeout\n"); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int cdns_imx_resume(struct device *dev) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct cdns_imx *data = dev_get_drvdata(dev); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return clk_bulk_prepare_enable(data->num_clks, data->clks); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int cdns_imx_suspend(struct device *dev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct cdns_imx *data = dev_get_drvdata(dev); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci clk_bulk_disable_unprepare(data->num_clks, data->clks); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/* Indicate if the controller was power lost before */ 36562306a36Sopenharmony_cistatic inline bool cdns_imx_is_power_lost(struct cdns_imx *data) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci u32 value; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci value = cdns_imx_readl(data, USB3_CORE_CTRL1); 37062306a36Sopenharmony_ci if ((value & SW_RESET_MASK) == ALL_SW_RESET) 37162306a36Sopenharmony_ci return true; 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci return false; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int __maybe_unused cdns_imx_system_suspend(struct device *dev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci pm_runtime_put_sync(dev); 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int __maybe_unused cdns_imx_system_resume(struct device *dev) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct cdns_imx *data = dev_get_drvdata(dev); 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 38862306a36Sopenharmony_ci if (ret < 0) { 38962306a36Sopenharmony_ci dev_err(dev, "Could not get runtime PM.\n"); 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (cdns_imx_is_power_lost(data)) { 39462306a36Sopenharmony_ci dev_dbg(dev, "resume from power lost\n"); 39562306a36Sopenharmony_ci ret = cdns_imx_noncore_init(data); 39662306a36Sopenharmony_ci if (ret) 39762306a36Sopenharmony_ci cdns_imx_suspend(dev); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#else 40462306a36Sopenharmony_cistatic int cdns_imx_platform_suspend(struct device *dev, 40562306a36Sopenharmony_ci bool suspend, bool wakeup) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci#endif /* CONFIG_PM */ 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic const struct dev_pm_ops cdns_imx_pm_ops = { 41362306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) 41462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_system_suspend, cdns_imx_system_resume) 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic const struct of_device_id cdns_imx_of_match[] = { 41862306a36Sopenharmony_ci { .compatible = "fsl,imx8qm-usb3", }, 41962306a36Sopenharmony_ci {}, 42062306a36Sopenharmony_ci}; 42162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cdns_imx_of_match); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic struct platform_driver cdns_imx_driver = { 42462306a36Sopenharmony_ci .probe = cdns_imx_probe, 42562306a36Sopenharmony_ci .remove_new = cdns_imx_remove, 42662306a36Sopenharmony_ci .driver = { 42762306a36Sopenharmony_ci .name = "cdns3-imx", 42862306a36Sopenharmony_ci .of_match_table = cdns_imx_of_match, 42962306a36Sopenharmony_ci .pm = &cdns_imx_pm_ops, 43062306a36Sopenharmony_ci }, 43162306a36Sopenharmony_ci}; 43262306a36Sopenharmony_cimodule_platform_driver(cdns_imx_driver); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ciMODULE_ALIAS("platform:cdns3-imx"); 43562306a36Sopenharmony_ciMODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>"); 43662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 43762306a36Sopenharmony_ciMODULE_DESCRIPTION("Cadence USB3 i.MX Glue Layer"); 438