// SPDX-License-Identifier: GPL-2.0
/*
* cdns3 - ti . c - TI specific Glue layer for Cadence USB Controller
*
* Copyright ( C ) 2019 Texas Instruments Incorporated - https : //www.ti.com
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include "core.h"
/* USB Wrapper register offsets */
#define USBSS_PID 0 x0
#define USBSS_W1 0 x4
#define USBSS_STATIC_CONFIG 0 x8
#define USBSS_PHY_TEST 0 xc
#define USBSS_DEBUG_CTRL 0 x10
#define USBSS_DEBUG_INFO 0 x14
#define USBSS_DEBUG_LINK_STATE 0 x18
#define USBSS_DEVICE_CTRL 0 x1c
/* Wrapper 1 register bits */
#define USBSS_W1_PWRUP_RST BIT(0 )
#define USBSS_W1_OVERCURRENT_SEL BIT(8 )
#define USBSS_W1_MODESTRAP_SEL BIT(9 )
#define USBSS_W1_OVERCURRENT BIT(16 )
#define USBSS_W1_MODESTRAP_MASK GENMASK(18 , 17 )
#define USBSS_W1_MODESTRAP_SHIFT 17
#define USBSS_W1_USB2_ONLY BIT(19 )
/* Static config register bits */
#define USBSS1_STATIC_PLL_REF_SEL_MASK GENMASK(8 , 5 )
#define USBSS1_STATIC_PLL_REF_SEL_SHIFT 5
#define USBSS1_STATIC_LOOPBACK_MODE_MASK GENMASK(4 , 3 )
#define USBSS1_STATIC_LOOPBACK_MODE_SHIFT 3
#define USBSS1_STATIC_VBUS_SEL_MASK GENMASK(2 , 1 )
#define USBSS1_STATIC_VBUS_SEL_SHIFT 1
#define USBSS1_STATIC_LANE_REVERSE BIT(0 )
/* Modestrap modes */
enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE,
USBSS_MODESTRAP_MODE_HOST,
USBSS_MODESTRAP_MODE_PERIPHERAL};
struct cdns_ti {
struct device *dev;
void __iomem *usbss;
unsigned usb2_only:1 ;
unsigned vbus_divider:1 ;
struct clk *usb2_refclk;
struct clk *lpm_clk;
int usb2_refclk_rate_code;
};
static const int cdns_ti_rate_table[] = { /* in KHZ */
9600 ,
10000 ,
12000 ,
19200 ,
20000 ,
24000 ,
25000 ,
26000 ,
38400 ,
40000 ,
58000 ,
50000 ,
52000 ,
};
static inline u32 cdns_ti_readl(struct cdns_ti *data, u32 offset)
{
return readl(data->usbss + offset);
}
static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
{
writel(value, data->usbss + offset);
}
static struct cdns3_platform_data cdns_ti_pdata = {
.quirks = CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE, /* Errata i2409 */
};
static const struct of_dev_auxdata cdns_ti_auxdata[] = {
{
.compatible = "cdns,usb3" ,
.platform_data = &cdns_ti_pdata,
},
{},
};
static void cdns_ti_reset_and_init_hw(struct cdns_ti *data)
{
u32 reg;
/* assert RESET */
reg = cdns_ti_readl(data, USBSS_W1);
reg &= ~USBSS_W1_PWRUP_RST;
cdns_ti_writel(data, USBSS_W1, reg);
/* set static config */
reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
reg |= data->usb2_refclk_rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
if (data->vbus_divider)
reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
/* set USB2_ONLY mode if requested */
reg = cdns_ti_readl(data, USBSS_W1);
if (data->usb2_only)
reg |= USBSS_W1_USB2_ONLY;
/* set default modestrap */
reg |= USBSS_W1_MODESTRAP_SEL;
reg &= ~USBSS_W1_MODESTRAP_MASK;
reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
cdns_ti_writel(data, USBSS_W1, reg);
/* de-assert RESET */
reg |= USBSS_W1_PWRUP_RST;
cdns_ti_writel(data, USBSS_W1, reg);
}
static int cdns_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct cdns_ti *data;
unsigned long rate;
int error, i;
data = devm_kzalloc(dev, sizeof (*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
platform_set_drvdata(pdev, data);
data->dev = dev;
data->usbss = devm_platform_ioremap_resource(pdev, 0 );
if (IS_ERR(data->usbss)) {
dev_err(dev, "can't map IOMEM resource\n" );
return PTR_ERR(data->usbss);
}
data->usb2_refclk = devm_clk_get(dev, "ref" );
if (IS_ERR(data->usb2_refclk)) {
dev_err(dev, "can't get usb2_refclk\n" );
return PTR_ERR(data->usb2_refclk);
}
data->lpm_clk = devm_clk_get(dev, "lpm" );
if (IS_ERR(data->lpm_clk)) {
dev_err(dev, "can't get lpm_clk\n" );
return PTR_ERR(data->lpm_clk);
}
rate = clk_get_rate(data->usb2_refclk);
rate /= 1000 ; /* To KHz */
for (i = 0 ; i < ARRAY_SIZE(cdns_ti_rate_table); i++) {
if (cdns_ti_rate_table[i] == rate)
break ;
}
if (i == ARRAY_SIZE(cdns_ti_rate_table)) {
dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n" , rate);
return -EINVAL;
}
data->usb2_refclk_rate_code = i;
data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider" );
data->usb2_only = device_property_read_bool(dev, "ti,usb2-only" );
/*
* The call below to pm_runtime_get_sync ( ) MIGHT reset hardware , if it
* detects it as uninitialised . We want to enforce a reset at probe ,
* and so do it manually here . This means the first runtime_resume ( )
* will be a no - op .
*/
cdns_ti_reset_and_init_hw(data);
pm_runtime_enable(dev);
error = pm_runtime_get_sync(dev);
if (error < 0 ) {
dev_err(dev, "pm_runtime_get_sync failed: %d\n" , error);
goto err;
}
error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
if (error) {
dev_err(dev, "failed to create children: %d\n" , error);
goto err;
}
return 0 ;
err:
pm_runtime_put_sync(data->dev);
pm_runtime_disable(data->dev);
return error;
}
static int cdns_ti_remove_core(struct device *dev, void *c)
{
struct platform_device *pdev = to_platform_device(dev);
platform_device_unregister(pdev);
return 0 ;
}
static void cdns_ti_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
device_for_each_child(dev, NULL, cdns_ti_remove_core);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
platform_set_drvdata(pdev, NULL);
}
static int cdns_ti_runtime_resume(struct device *dev)
{
const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL;
struct cdns_ti *data = dev_get_drvdata(dev);
u32 w1;
w1 = cdns_ti_readl(data, USBSS_W1);
if ((w1 & mask) != mask)
cdns_ti_reset_and_init_hw(data);
return 0 ;
}
static const struct dev_pm_ops cdns_ti_pm_ops = {
RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static const struct of_device_id cdns_ti_of_match[] = {
{ .compatible = "ti,j721e-usb" , },
{ .compatible = "ti,am64-usb" , },
{},
};
MODULE_DEVICE_TABLE(of, cdns_ti_of_match);
static struct platform_driver cdns_ti_driver = {
.probe = cdns_ti_probe,
.remove = cdns_ti_remove,
.driver = {
.name = "cdns3-ti" ,
.of_match_table = cdns_ti_of_match,
.pm = pm_ptr(&cdns_ti_pm_ops),
},
};
module_platform_driver(cdns_ti_driver);
MODULE_ALIAS("platform:cdns3-ti" );
MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>" );
MODULE_LICENSE("GPL v2" );
MODULE_DESCRIPTION("Cadence USB3 TI Glue Layer" );
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland