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-bcm63xx-usbh.c (12384B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * BCM6328 USBH PHY Controller Driver
      4 *
      5 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
      6 * Copyright (C) 2015 Simon Arlott
      7 *
      8 * Derived from bcm963xx_4.12L.06B_consumer/kernel/linux/arch/mips/bcm963xx/setup.c:
      9 * Copyright (C) 2002 Broadcom Corporation
     10 *
     11 * Derived from OpenWrt patches:
     12 * Copyright (C) 2013 Jonas Gorski <jonas.gorski@gmail.com>
     13 * Copyright (C) 2013 Florian Fainelli <f.fainelli@gmail.com>
     14 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
     15 */
     16
     17#include <linux/clk.h>
     18#include <linux/io.h>
     19#include <linux/module.h>
     20#include <linux/phy/phy.h>
     21#include <linux/platform_device.h>
     22#include <linux/reset.h>
     23
     24/* USBH control register offsets */
     25enum usbh_regs {
     26	USBH_BRT_CONTROL1 = 0,
     27	USBH_BRT_CONTROL2,
     28	USBH_BRT_STATUS1,
     29	USBH_BRT_STATUS2,
     30	USBH_UTMI_CONTROL1,
     31#define   USBH_UC1_DEV_MODE_SEL		BIT(0)
     32	USBH_TEST_PORT_CONTROL,
     33	USBH_PLL_CONTROL1,
     34#define   USBH_PLLC_REFCLKSEL_SHIFT	0
     35#define   USBH_PLLC_REFCLKSEL_MASK	(0x3 << USBH_PLLC_REFCLKSEL_SHIFT)
     36#define   USBH_PLLC_CLKSEL_SHIFT	2
     37#define   USBH_PLLC_CLKSEL_MASK		(0x3 << USBH_PLLC_CLKSEL_MASK)
     38#define   USBH_PLLC_XTAL_PWRDWNB	BIT(4)
     39#define   USBH_PLLC_PLL_PWRDWNB		BIT(5)
     40#define   USBH_PLLC_PLL_CALEN		BIT(6)
     41#define   USBH_PLLC_PHYPLL_BYP		BIT(7)
     42#define   USBH_PLLC_PLL_RESET		BIT(8)
     43#define   USBH_PLLC_PLL_IDDQ_PWRDN	BIT(9)
     44#define   USBH_PLLC_PLL_PWRDN_DELAY	BIT(10)
     45#define   USBH_6318_PLLC_PLL_SUSPEND_EN	BIT(27)
     46#define   USBH_6318_PLLC_PHYPLL_BYP	BIT(29)
     47#define   USBH_6318_PLLC_PLL_RESET	BIT(30)
     48#define   USBH_6318_PLLC_PLL_IDDQ_PWRDN	BIT(31)
     49	USBH_SWAP_CONTROL,
     50#define   USBH_SC_OHCI_DATA_SWAP	BIT(0)
     51#define   USBH_SC_OHCI_ENDIAN_SWAP	BIT(1)
     52#define   USBH_SC_OHCI_LOGICAL_ADDR_EN	BIT(2)
     53#define   USBH_SC_EHCI_DATA_SWAP	BIT(3)
     54#define   USBH_SC_EHCI_ENDIAN_SWAP	BIT(4)
     55#define   USBH_SC_EHCI_LOGICAL_ADDR_EN	BIT(5)
     56#define   USBH_SC_USB_DEVICE_SEL	BIT(6)
     57	USBH_GENERIC_CONTROL,
     58#define   USBH_GC_PLL_SUSPEND_EN	BIT(1)
     59	USBH_FRAME_ADJUST_VALUE,
     60	USBH_SETUP,
     61#define   USBH_S_IOC			BIT(4)
     62#define   USBH_S_IPP			BIT(5)
     63	USBH_MDIO,
     64	USBH_MDIO32,
     65	USBH_USB_SIM_CONTROL,
     66#define   USBH_USC_LADDR_SEL		BIT(5)
     67
     68	__USBH_ENUM_SIZE
     69};
     70
     71struct bcm63xx_usbh_phy_variant {
     72	/* Registers */
     73	long regs[__USBH_ENUM_SIZE];
     74
     75	/* PLLC bits to set/clear for power on */
     76	u32 power_pllc_clr;
     77	u32 power_pllc_set;
     78
     79	/* Setup bits to set/clear for power on */
     80	u32 setup_clr;
     81	u32 setup_set;
     82
     83	/* Swap Control bits to set */
     84	u32 swapctl_dev_set;
     85
     86	/* Test Port Control value to set if non-zero */
     87	u32 tpc_val;
     88
     89	/* USB Sim Control bits to set */
     90	u32 usc_set;
     91
     92	/* UTMI Control 1 bits to set */
     93	u32 utmictl1_dev_set;
     94};
     95
     96struct bcm63xx_usbh_phy {
     97	void __iomem *base;
     98	struct clk *usbh_clk;
     99	struct clk *usb_ref_clk;
    100	struct reset_control *reset;
    101	const struct bcm63xx_usbh_phy_variant *variant;
    102	bool device_mode;
    103};
    104
    105static const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = {
    106	.regs = {
    107		[USBH_BRT_CONTROL1] = -1,
    108		[USBH_BRT_CONTROL2] = -1,
    109		[USBH_BRT_STATUS1] = -1,
    110		[USBH_BRT_STATUS2] = -1,
    111		[USBH_UTMI_CONTROL1] = 0x2c,
    112		[USBH_TEST_PORT_CONTROL] = 0x1c,
    113		[USBH_PLL_CONTROL1] = 0x04,
    114		[USBH_SWAP_CONTROL] = 0x0c,
    115		[USBH_GENERIC_CONTROL] = -1,
    116		[USBH_FRAME_ADJUST_VALUE] = 0x08,
    117		[USBH_SETUP] = 0x00,
    118		[USBH_MDIO] = 0x14,
    119		[USBH_MDIO32] = 0x18,
    120		[USBH_USB_SIM_CONTROL] = 0x20,
    121	},
    122	.power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN,
    123	.power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN,
    124	.setup_set = USBH_S_IOC,
    125	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
    126	.usc_set = USBH_USC_LADDR_SEL,
    127	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
    128};
    129
    130static const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = {
    131	.regs = {
    132		[USBH_BRT_CONTROL1] = 0x00,
    133		[USBH_BRT_CONTROL2] = 0x04,
    134		[USBH_BRT_STATUS1] = 0x08,
    135		[USBH_BRT_STATUS2] = 0x0c,
    136		[USBH_UTMI_CONTROL1] = 0x10,
    137		[USBH_TEST_PORT_CONTROL] = 0x14,
    138		[USBH_PLL_CONTROL1] = 0x18,
    139		[USBH_SWAP_CONTROL] = 0x1c,
    140		[USBH_GENERIC_CONTROL] = 0x20,
    141		[USBH_FRAME_ADJUST_VALUE] = 0x24,
    142		[USBH_SETUP] = 0x28,
    143		[USBH_MDIO] = 0x2c,
    144		[USBH_MDIO32] = 0x30,
    145		[USBH_USB_SIM_CONTROL] = 0x34,
    146	},
    147	.setup_set = USBH_S_IOC,
    148	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
    149	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
    150};
    151
    152static const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = {
    153	.regs = {
    154		[USBH_BRT_CONTROL1] = -1,
    155		[USBH_BRT_CONTROL2] = -1,
    156		[USBH_BRT_STATUS1] = -1,
    157		[USBH_BRT_STATUS2] = -1,
    158		[USBH_UTMI_CONTROL1] = -1,
    159		[USBH_TEST_PORT_CONTROL] = 0x24,
    160		[USBH_PLL_CONTROL1] = -1,
    161		[USBH_SWAP_CONTROL] = 0x00,
    162		[USBH_GENERIC_CONTROL] = -1,
    163		[USBH_FRAME_ADJUST_VALUE] = -1,
    164		[USBH_SETUP] = -1,
    165		[USBH_MDIO] = -1,
    166		[USBH_MDIO32] = -1,
    167		[USBH_USB_SIM_CONTROL] = -1,
    168	},
    169	/*
    170	 * The magic value comes for the original vendor BSP
    171	 * and is needed for USB to work. Datasheet does not
    172	 * help, so the magic value is used as-is.
    173	 */
    174	.tpc_val = 0x1c0020,
    175};
    176
    177static const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = {
    178	.regs = {
    179		[USBH_BRT_CONTROL1] = 0x00,
    180		[USBH_BRT_CONTROL2] = 0x04,
    181		[USBH_BRT_STATUS1] = 0x08,
    182		[USBH_BRT_STATUS2] = 0x0c,
    183		[USBH_UTMI_CONTROL1] = 0x10,
    184		[USBH_TEST_PORT_CONTROL] = 0x14,
    185		[USBH_PLL_CONTROL1] = 0x18,
    186		[USBH_SWAP_CONTROL] = 0x1c,
    187		[USBH_GENERIC_CONTROL] = -1,
    188		[USBH_FRAME_ADJUST_VALUE] = 0x24,
    189		[USBH_SETUP] = 0x28,
    190		[USBH_MDIO] = 0x2c,
    191		[USBH_MDIO32] = 0x30,
    192		[USBH_USB_SIM_CONTROL] = 0x34,
    193	},
    194	.power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
    195	.setup_set = USBH_S_IOC,
    196	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
    197	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
    198};
    199
    200static const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = {
    201	.regs = {
    202		[USBH_BRT_CONTROL1] = 0x00,
    203		[USBH_BRT_CONTROL2] = 0x04,
    204		[USBH_BRT_STATUS1] = 0x08,
    205		[USBH_BRT_STATUS2] = 0x0c,
    206		[USBH_UTMI_CONTROL1] = 0x10,
    207		[USBH_TEST_PORT_CONTROL] = 0x14,
    208		[USBH_PLL_CONTROL1] = 0x18,
    209		[USBH_SWAP_CONTROL] = 0x1c,
    210		[USBH_GENERIC_CONTROL] = 0x20,
    211		[USBH_FRAME_ADJUST_VALUE] = 0x24,
    212		[USBH_SETUP] = 0x28,
    213		[USBH_MDIO] = 0x2c,
    214		[USBH_MDIO32] = 0x30,
    215		[USBH_USB_SIM_CONTROL] = 0x34,
    216	},
    217	.power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY,
    218	.setup_clr = USBH_S_IPP,
    219	.setup_set = USBH_S_IOC,
    220	.swapctl_dev_set = USBH_SC_USB_DEVICE_SEL,
    221	.utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL,
    222};
    223
    224static inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg)
    225{
    226	return (usbh->variant->regs[reg] >= 0);
    227}
    228
    229static inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg)
    230{
    231	return __raw_readl(usbh->base + usbh->variant->regs[reg]);
    232}
    233
    234static inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg,
    235			       u32 value)
    236{
    237	__raw_writel(value, usbh->base + usbh->variant->regs[reg]);
    238}
    239
    240static int bcm63xx_usbh_phy_init(struct phy *phy)
    241{
    242	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
    243	int ret;
    244
    245	ret = clk_prepare_enable(usbh->usbh_clk);
    246	if (ret) {
    247		dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret);
    248		return ret;
    249	}
    250
    251	ret = clk_prepare_enable(usbh->usb_ref_clk);
    252	if (ret) {
    253		dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret);
    254		clk_disable_unprepare(usbh->usbh_clk);
    255		return ret;
    256	}
    257
    258	ret = reset_control_reset(usbh->reset);
    259	if (ret) {
    260		dev_err(&phy->dev, "unable to reset device: %d\n", ret);
    261		clk_disable_unprepare(usbh->usb_ref_clk);
    262		clk_disable_unprepare(usbh->usbh_clk);
    263		return ret;
    264	}
    265
    266	/* Configure to work in native CPU endian */
    267	if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) {
    268		u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL);
    269
    270		val |= USBH_SC_EHCI_DATA_SWAP;
    271		val &= ~USBH_SC_EHCI_ENDIAN_SWAP;
    272
    273		val |= USBH_SC_OHCI_DATA_SWAP;
    274		val &= ~USBH_SC_OHCI_ENDIAN_SWAP;
    275
    276		if (usbh->device_mode && usbh->variant->swapctl_dev_set)
    277			val |= usbh->variant->swapctl_dev_set;
    278
    279		usbh_writel(usbh, USBH_SWAP_CONTROL, val);
    280	}
    281
    282	if (usbh_has_reg(usbh, USBH_SETUP)) {
    283		u32 val = usbh_readl(usbh, USBH_SETUP);
    284
    285		val |= usbh->variant->setup_set;
    286		val &= ~usbh->variant->setup_clr;
    287
    288		usbh_writel(usbh, USBH_SETUP, val);
    289	}
    290
    291	if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) {
    292		u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL);
    293
    294		val |= usbh->variant->usc_set;
    295
    296		usbh_writel(usbh, USBH_USB_SIM_CONTROL, val);
    297	}
    298
    299	if (usbh->variant->tpc_val &&
    300	    usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL))
    301		usbh_writel(usbh, USBH_TEST_PORT_CONTROL,
    302			    usbh->variant->tpc_val);
    303
    304	if (usbh->device_mode &&
    305	    usbh_has_reg(usbh, USBH_UTMI_CONTROL1) &&
    306	    usbh->variant->utmictl1_dev_set) {
    307		u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1);
    308
    309		val |= usbh->variant->utmictl1_dev_set;
    310
    311		usbh_writel(usbh, USBH_UTMI_CONTROL1, val);
    312	}
    313
    314	return 0;
    315}
    316
    317static int bcm63xx_usbh_phy_power_on(struct phy *phy)
    318{
    319	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
    320
    321	if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
    322		u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
    323
    324		val |= usbh->variant->power_pllc_set;
    325		val &= ~usbh->variant->power_pllc_clr;
    326
    327		usbh_writel(usbh, USBH_PLL_CONTROL1, val);
    328	}
    329
    330	return 0;
    331}
    332
    333static int bcm63xx_usbh_phy_power_off(struct phy *phy)
    334{
    335	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
    336
    337	if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) {
    338		u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1);
    339
    340		val &= ~usbh->variant->power_pllc_set;
    341		val |= usbh->variant->power_pllc_clr;
    342
    343		usbh_writel(usbh, USBH_PLL_CONTROL1, val);
    344	}
    345
    346	return 0;
    347}
    348
    349static int bcm63xx_usbh_phy_exit(struct phy *phy)
    350{
    351	struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy);
    352
    353	clk_disable_unprepare(usbh->usbh_clk);
    354	clk_disable_unprepare(usbh->usb_ref_clk);
    355
    356	return 0;
    357}
    358
    359static const struct phy_ops bcm63xx_usbh_phy_ops = {
    360	.exit = bcm63xx_usbh_phy_exit,
    361	.init = bcm63xx_usbh_phy_init,
    362	.power_off = bcm63xx_usbh_phy_power_off,
    363	.power_on = bcm63xx_usbh_phy_power_on,
    364	.owner = THIS_MODULE,
    365};
    366
    367static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev,
    368					  struct of_phandle_args *args)
    369{
    370	struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev);
    371
    372	usbh->device_mode = !!args->args[0];
    373
    374	return of_phy_simple_xlate(dev, args);
    375}
    376
    377static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev)
    378{
    379	struct device *dev = &pdev->dev;
    380	struct bcm63xx_usbh_phy	*usbh;
    381	const struct bcm63xx_usbh_phy_variant *variant;
    382	struct phy *phy;
    383	struct phy_provider *phy_provider;
    384
    385	usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL);
    386	if (!usbh)
    387		return -ENOMEM;
    388
    389	variant = device_get_match_data(dev);
    390	if (!variant)
    391		return -EINVAL;
    392	usbh->variant = variant;
    393
    394	usbh->base = devm_platform_ioremap_resource(pdev, 0);
    395	if (IS_ERR(usbh->base))
    396		return PTR_ERR(usbh->base);
    397
    398	usbh->reset = devm_reset_control_get_exclusive(dev, NULL);
    399	if (IS_ERR(usbh->reset)) {
    400		if (PTR_ERR(usbh->reset) != -EPROBE_DEFER)
    401			dev_err(dev, "failed to get reset\n");
    402		return PTR_ERR(usbh->reset);
    403	}
    404
    405	usbh->usbh_clk = devm_clk_get_optional(dev, "usbh");
    406	if (IS_ERR(usbh->usbh_clk))
    407		return PTR_ERR(usbh->usbh_clk);
    408
    409	usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref");
    410	if (IS_ERR(usbh->usb_ref_clk))
    411		return PTR_ERR(usbh->usb_ref_clk);
    412
    413	phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops);
    414	if (IS_ERR(phy)) {
    415		dev_err(dev, "failed to create PHY\n");
    416		return PTR_ERR(phy);
    417	}
    418
    419	platform_set_drvdata(pdev, usbh);
    420	phy_set_drvdata(phy, usbh);
    421
    422	phy_provider = devm_of_phy_provider_register(dev,
    423						     bcm63xx_usbh_phy_xlate);
    424	if (IS_ERR(phy_provider)) {
    425		dev_err(dev, "failed to register PHY provider\n");
    426		return PTR_ERR(phy_provider);
    427	}
    428
    429	dev_dbg(dev, "Registered BCM63xx USB PHY driver\n");
    430
    431	return 0;
    432}
    433
    434static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = {
    435	{ .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 },
    436	{ .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 },
    437	{ .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 },
    438	{ .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 },
    439	{ .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 },
    440	{ .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 },
    441	{ /* sentinel */ }
    442};
    443MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids);
    444
    445static struct platform_driver bcm63xx_usbh_phy_driver __refdata = {
    446	.driver	= {
    447		.name = "bcm63xx-usbh-phy",
    448		.of_match_table = bcm63xx_usbh_phy_ids,
    449	},
    450	.probe	= bcm63xx_usbh_phy_probe,
    451};
    452module_platform_driver(bcm63xx_usbh_phy_driver);
    453
    454MODULE_DESCRIPTION("BCM63xx USBH PHY driver");
    455MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
    456MODULE_AUTHOR("Simon Arlott");
    457MODULE_LICENSE("GPL");