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

phy-omap-usb2.c (13083B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs.
      4 *
      5 * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com
      6 * Author: Kishon Vijay Abraham I <kishon@ti.com>
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/delay.h>
     11#include <linux/err.h>
     12#include <linux/io.h>
     13#include <linux/mfd/syscon.h>
     14#include <linux/module.h>
     15#include <linux/of.h>
     16#include <linux/of_platform.h>
     17#include <linux/phy/omap_control_phy.h>
     18#include <linux/phy/omap_usb.h>
     19#include <linux/phy/phy.h>
     20#include <linux/platform_device.h>
     21#include <linux/pm_runtime.h>
     22#include <linux/regmap.h>
     23#include <linux/slab.h>
     24#include <linux/sys_soc.h>
     25#include <linux/usb/phy_companion.h>
     26
     27#define USB2PHY_ANA_CONFIG1		0x4c
     28#define USB2PHY_DISCON_BYP_LATCH	BIT(31)
     29
     30#define USB2PHY_CHRG_DET			0x14
     31#define USB2PHY_CHRG_DET_USE_CHG_DET_REG	BIT(29)
     32#define USB2PHY_CHRG_DET_DIS_CHG_DET		BIT(28)
     33
     34/* SoC Specific USB2_OTG register definitions */
     35#define AM654_USB2_OTG_PD		BIT(8)
     36#define AM654_USB2_VBUS_DET_EN		BIT(5)
     37#define AM654_USB2_VBUSVALID_DET_EN	BIT(4)
     38
     39#define OMAP_DEV_PHY_PD		BIT(0)
     40#define OMAP_USB2_PHY_PD	BIT(28)
     41
     42#define AM437X_USB2_PHY_PD		BIT(0)
     43#define AM437X_USB2_OTG_PD		BIT(1)
     44#define AM437X_USB2_OTGVDET_EN		BIT(19)
     45#define AM437X_USB2_OTGSESSEND_EN	BIT(20)
     46
     47/* Driver Flags */
     48#define OMAP_USB2_HAS_START_SRP			BIT(0)
     49#define OMAP_USB2_HAS_SET_VBUS			BIT(1)
     50#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT	BIT(2)
     51#define OMAP_USB2_DISABLE_CHRG_DET		BIT(3)
     52
     53struct omap_usb {
     54	struct usb_phy		phy;
     55	struct phy_companion	*comparator;
     56	void __iomem		*pll_ctrl_base;
     57	void __iomem		*phy_base;
     58	struct device		*dev;
     59	struct device		*control_dev;
     60	struct clk		*wkupclk;
     61	struct clk		*optclk;
     62	u8			flags;
     63	struct regmap		*syscon_phy_power; /* ctrl. reg. acces */
     64	unsigned int		power_reg; /* power reg. index within syscon */
     65	u32			mask;
     66	u32			power_on;
     67	u32			power_off;
     68};
     69
     70#define	phy_to_omapusb(x)	container_of((x), struct omap_usb, phy)
     71
     72struct usb_phy_data {
     73	const char *label;
     74	u8 flags;
     75	u32 mask;
     76	u32 power_on;
     77	u32 power_off;
     78};
     79
     80static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset)
     81{
     82	return __raw_readl(addr + offset);
     83}
     84
     85static inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
     86				   u32 data)
     87{
     88	__raw_writel(data, addr + offset);
     89}
     90
     91/**
     92 * omap_usb2_set_comparator() - links the comparator present in the system with this phy
     93 *
     94 * @comparator:  the companion phy(comparator) for this phy
     95 *
     96 * The phy companion driver should call this API passing the phy_companion
     97 * filled with set_vbus and start_srp to be used by usb phy.
     98 *
     99 * For use by phy companion driver
    100 */
    101int omap_usb2_set_comparator(struct phy_companion *comparator)
    102{
    103	struct omap_usb	*phy;
    104	struct usb_phy	*x = usb_get_phy(USB_PHY_TYPE_USB2);
    105
    106	if (IS_ERR(x))
    107		return -ENODEV;
    108
    109	phy = phy_to_omapusb(x);
    110	phy->comparator = comparator;
    111	return 0;
    112}
    113EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
    114
    115static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
    116{
    117	struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
    118
    119	if (!phy->comparator)
    120		return -ENODEV;
    121
    122	return phy->comparator->set_vbus(phy->comparator, enabled);
    123}
    124
    125static int omap_usb_start_srp(struct usb_otg *otg)
    126{
    127	struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
    128
    129	if (!phy->comparator)
    130		return -ENODEV;
    131
    132	return phy->comparator->start_srp(phy->comparator);
    133}
    134
    135static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
    136{
    137	otg->host = host;
    138	if (!host)
    139		otg->state = OTG_STATE_UNDEFINED;
    140
    141	return 0;
    142}
    143
    144static int omap_usb_set_peripheral(struct usb_otg *otg,
    145				   struct usb_gadget *gadget)
    146{
    147	otg->gadget = gadget;
    148	if (!gadget)
    149		otg->state = OTG_STATE_UNDEFINED;
    150
    151	return 0;
    152}
    153
    154static int omap_usb_phy_power(struct omap_usb *phy, int on)
    155{
    156	u32 val;
    157	int ret;
    158
    159	if (!phy->syscon_phy_power) {
    160		omap_control_phy_power(phy->control_dev, on);
    161		return 0;
    162	}
    163
    164	if (on)
    165		val = phy->power_on;
    166	else
    167		val = phy->power_off;
    168
    169	ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
    170				 phy->mask, val);
    171	return ret;
    172}
    173
    174static int omap_usb_power_off(struct phy *x)
    175{
    176	struct omap_usb *phy = phy_get_drvdata(x);
    177
    178	return omap_usb_phy_power(phy, false);
    179}
    180
    181static int omap_usb_power_on(struct phy *x)
    182{
    183	struct omap_usb *phy = phy_get_drvdata(x);
    184
    185	return omap_usb_phy_power(phy, true);
    186}
    187
    188static int omap_usb2_disable_clocks(struct omap_usb *phy)
    189{
    190	clk_disable_unprepare(phy->wkupclk);
    191	if (!IS_ERR(phy->optclk))
    192		clk_disable_unprepare(phy->optclk);
    193
    194	return 0;
    195}
    196
    197static int omap_usb2_enable_clocks(struct omap_usb *phy)
    198{
    199	int ret;
    200
    201	ret = clk_prepare_enable(phy->wkupclk);
    202	if (ret < 0) {
    203		dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
    204		goto err0;
    205	}
    206
    207	if (!IS_ERR(phy->optclk)) {
    208		ret = clk_prepare_enable(phy->optclk);
    209		if (ret < 0) {
    210			dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
    211			goto err1;
    212		}
    213	}
    214
    215	return 0;
    216
    217err1:
    218	clk_disable_unprepare(phy->wkupclk);
    219
    220err0:
    221	return ret;
    222}
    223
    224static int omap_usb_init(struct phy *x)
    225{
    226	struct omap_usb *phy = phy_get_drvdata(x);
    227	u32 val;
    228
    229	omap_usb2_enable_clocks(phy);
    230
    231	if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
    232		/*
    233		 *
    234		 * Reduce the sensitivity of internal PHY by enabling the
    235		 * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
    236		 * resolves issues with certain devices which can otherwise
    237		 * be prone to false disconnects.
    238		 *
    239		 */
    240		val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
    241		val |= USB2PHY_DISCON_BYP_LATCH;
    242		omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
    243	}
    244
    245	if (phy->flags & OMAP_USB2_DISABLE_CHRG_DET) {
    246		val = omap_usb_readl(phy->phy_base, USB2PHY_CHRG_DET);
    247		val |= USB2PHY_CHRG_DET_USE_CHG_DET_REG |
    248		       USB2PHY_CHRG_DET_DIS_CHG_DET;
    249		omap_usb_writel(phy->phy_base, USB2PHY_CHRG_DET, val);
    250	}
    251
    252	return 0;
    253}
    254
    255static int omap_usb_exit(struct phy *x)
    256{
    257	struct omap_usb *phy = phy_get_drvdata(x);
    258
    259	return omap_usb2_disable_clocks(phy);
    260}
    261
    262static const struct phy_ops ops = {
    263	.init		= omap_usb_init,
    264	.exit		= omap_usb_exit,
    265	.power_on	= omap_usb_power_on,
    266	.power_off	= omap_usb_power_off,
    267	.owner		= THIS_MODULE,
    268};
    269
    270static const struct usb_phy_data omap_usb2_data = {
    271	.label = "omap_usb2",
    272	.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
    273	.mask = OMAP_DEV_PHY_PD,
    274	.power_off = OMAP_DEV_PHY_PD,
    275};
    276
    277static const struct usb_phy_data omap5_usb2_data = {
    278	.label = "omap5_usb2",
    279	.flags = 0,
    280	.mask = OMAP_DEV_PHY_PD,
    281	.power_off = OMAP_DEV_PHY_PD,
    282};
    283
    284static const struct usb_phy_data dra7x_usb2_data = {
    285	.label = "dra7x_usb2",
    286	.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
    287	.mask = OMAP_DEV_PHY_PD,
    288	.power_off = OMAP_DEV_PHY_PD,
    289};
    290
    291static const struct usb_phy_data dra7x_usb2_phy2_data = {
    292	.label = "dra7x_usb2_phy2",
    293	.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
    294	.mask = OMAP_USB2_PHY_PD,
    295	.power_off = OMAP_USB2_PHY_PD,
    296};
    297
    298static const struct usb_phy_data am437x_usb2_data = {
    299	.label = "am437x_usb2",
    300	.flags =  0,
    301	.mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
    302		AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
    303	.power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
    304	.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
    305};
    306
    307static const struct usb_phy_data am654_usb2_data = {
    308	.label = "am654_usb2",
    309	.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
    310	.mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
    311		AM654_USB2_VBUSVALID_DET_EN,
    312	.power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
    313	.power_off = AM654_USB2_OTG_PD,
    314};
    315
    316static const struct of_device_id omap_usb2_id_table[] = {
    317	{
    318		.compatible = "ti,omap-usb2",
    319		.data = &omap_usb2_data,
    320	},
    321	{
    322		.compatible = "ti,omap5-usb2",
    323		.data = &omap5_usb2_data,
    324	},
    325	{
    326		.compatible = "ti,dra7x-usb2",
    327		.data = &dra7x_usb2_data,
    328	},
    329	{
    330		.compatible = "ti,dra7x-usb2-phy2",
    331		.data = &dra7x_usb2_phy2_data,
    332	},
    333	{
    334		.compatible = "ti,am437x-usb2",
    335		.data = &am437x_usb2_data,
    336	},
    337	{
    338		.compatible = "ti,am654-usb2",
    339		.data = &am654_usb2_data,
    340	},
    341	{},
    342};
    343MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
    344
    345static void omap_usb2_init_errata(struct omap_usb *phy)
    346{
    347	static const struct soc_device_attribute am65x_sr10_soc_devices[] = {
    348		{ .family = "AM65X", .revision = "SR1.0" },
    349		{ /* sentinel */ }
    350	};
    351
    352	/*
    353	 * Errata i2075: USB2PHY: USB2PHY Charger Detect is Enabled by
    354	 * Default Without VBUS Presence.
    355	 *
    356	 * AM654x SR1.0 has a silicon bug due to which D+ is pulled high after
    357	 * POR, which could cause enumeration failure with some USB hubs.
    358	 * Disabling the USB2_PHY Charger Detect function will put D+
    359	 * into the normal state.
    360	 */
    361	if (soc_device_match(am65x_sr10_soc_devices))
    362		phy->flags |= OMAP_USB2_DISABLE_CHRG_DET;
    363}
    364
    365static int omap_usb2_probe(struct platform_device *pdev)
    366{
    367	struct omap_usb	*phy;
    368	struct phy *generic_phy;
    369	struct phy_provider *phy_provider;
    370	struct usb_otg *otg;
    371	struct device_node *node = pdev->dev.of_node;
    372	struct device_node *control_node;
    373	struct platform_device *control_pdev;
    374	const struct of_device_id *of_id;
    375	struct usb_phy_data *phy_data;
    376
    377	of_id = of_match_device(omap_usb2_id_table, &pdev->dev);
    378
    379	if (!of_id)
    380		return -EINVAL;
    381
    382	phy_data = (struct usb_phy_data *)of_id->data;
    383
    384	phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
    385	if (!phy)
    386		return -ENOMEM;
    387
    388	otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
    389	if (!otg)
    390		return -ENOMEM;
    391
    392	phy->dev		= &pdev->dev;
    393
    394	phy->phy.dev		= phy->dev;
    395	phy->phy.label		= phy_data->label;
    396	phy->phy.otg		= otg;
    397	phy->phy.type		= USB_PHY_TYPE_USB2;
    398	phy->mask		= phy_data->mask;
    399	phy->power_on		= phy_data->power_on;
    400	phy->power_off		= phy_data->power_off;
    401	phy->flags		= phy_data->flags;
    402
    403	omap_usb2_init_errata(phy);
    404
    405	phy->phy_base = devm_platform_ioremap_resource(pdev, 0);
    406	if (IS_ERR(phy->phy_base))
    407		return PTR_ERR(phy->phy_base);
    408
    409	phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
    410								"syscon-phy-power");
    411	if (IS_ERR(phy->syscon_phy_power)) {
    412		dev_dbg(&pdev->dev,
    413			"can't get syscon-phy-power, using control device\n");
    414		phy->syscon_phy_power = NULL;
    415
    416		control_node = of_parse_phandle(node, "ctrl-module", 0);
    417		if (!control_node) {
    418			dev_err(&pdev->dev,
    419				"Failed to get control device phandle\n");
    420			return -EINVAL;
    421		}
    422
    423		control_pdev = of_find_device_by_node(control_node);
    424		if (!control_pdev) {
    425			dev_err(&pdev->dev, "Failed to get control device\n");
    426			return -EINVAL;
    427		}
    428		phy->control_dev = &control_pdev->dev;
    429	} else {
    430		if (of_property_read_u32_index(node,
    431					       "syscon-phy-power", 1,
    432					       &phy->power_reg)) {
    433			dev_err(&pdev->dev,
    434				"couldn't get power reg. offset\n");
    435			return -EINVAL;
    436		}
    437	}
    438
    439	phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
    440	if (IS_ERR(phy->wkupclk)) {
    441		if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
    442			return -EPROBE_DEFER;
    443
    444		dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n",
    445			 PTR_ERR(phy->wkupclk));
    446		phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
    447
    448		if (IS_ERR(phy->wkupclk)) {
    449			if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
    450				dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
    451			return PTR_ERR(phy->wkupclk);
    452		}
    453
    454		dev_warn(&pdev->dev,
    455			 "found usb_phy_cm_clk32k, please fix DTS\n");
    456	}
    457
    458	phy->optclk = devm_clk_get(phy->dev, "refclk");
    459	if (IS_ERR(phy->optclk)) {
    460		if (PTR_ERR(phy->optclk) == -EPROBE_DEFER)
    461			return -EPROBE_DEFER;
    462
    463		dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
    464		phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
    465
    466		if (IS_ERR(phy->optclk)) {
    467			if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) {
    468				dev_dbg(&pdev->dev,
    469					"unable to get usb_otg_ss_refclk960m\n");
    470			}
    471		} else {
    472			dev_warn(&pdev->dev,
    473				 "found usb_otg_ss_refclk960m, please fix DTS\n");
    474		}
    475	}
    476
    477	otg->set_host = omap_usb_set_host;
    478	otg->set_peripheral = omap_usb_set_peripheral;
    479	if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
    480		otg->set_vbus = omap_usb_set_vbus;
    481	if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
    482		otg->start_srp = omap_usb_start_srp;
    483	otg->usb_phy = &phy->phy;
    484
    485	platform_set_drvdata(pdev, phy);
    486	pm_runtime_enable(phy->dev);
    487
    488	generic_phy = devm_phy_create(phy->dev, NULL, &ops);
    489	if (IS_ERR(generic_phy)) {
    490		pm_runtime_disable(phy->dev);
    491		return PTR_ERR(generic_phy);
    492	}
    493
    494	phy_set_drvdata(generic_phy, phy);
    495	omap_usb_power_off(generic_phy);
    496
    497	phy_provider = devm_of_phy_provider_register(phy->dev,
    498						     of_phy_simple_xlate);
    499	if (IS_ERR(phy_provider)) {
    500		pm_runtime_disable(phy->dev);
    501		return PTR_ERR(phy_provider);
    502	}
    503
    504	usb_add_phy_dev(&phy->phy);
    505
    506	return 0;
    507}
    508
    509static int omap_usb2_remove(struct platform_device *pdev)
    510{
    511	struct omap_usb	*phy = platform_get_drvdata(pdev);
    512
    513	usb_remove_phy(&phy->phy);
    514	pm_runtime_disable(phy->dev);
    515
    516	return 0;
    517}
    518
    519static struct platform_driver omap_usb2_driver = {
    520	.probe		= omap_usb2_probe,
    521	.remove		= omap_usb2_remove,
    522	.driver		= {
    523		.name	= "omap-usb2",
    524		.of_match_table = omap_usb2_id_table,
    525	},
    526};
    527
    528module_platform_driver(omap_usb2_driver);
    529
    530MODULE_ALIAS("platform:omap_usb2");
    531MODULE_AUTHOR("Texas Instruments Inc.");
    532MODULE_DESCRIPTION("OMAP USB2 phy driver");
    533MODULE_LICENSE("GPL v2");