diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-10-20 00:10:27 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-10-20 00:10:27 -0300 |
commit | d0b2f91bede3bd5e3d24dd6803e56eee959c1797 (patch) | |
tree | 7fee4ab0509879c373c4f2cbd5b8a5be5b4041ee /drivers/phy | |
parent | e914f8eb445e8f74b00303c19c2ffceaedd16a05 (diff) |
Linux-libre 4.8.2-gnupck-4.8.2-gnu
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 19 | ||||
-rw-r--r-- | drivers/phy/Makefile | 3 | ||||
-rw-r--r-- | drivers/phy/phy-bcm-ns2-pcie.c | 115 | ||||
-rw-r--r-- | drivers/phy/phy-brcm-sata.c | 83 | ||||
-rw-r--r-- | drivers/phy/phy-core.c | 15 | ||||
-rw-r--r-- | drivers/phy/phy-da8xx-usb.c | 245 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-14nm.c | 1 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-20nm.c | 1 | ||||
-rw-r--r-- | drivers/phy/phy-rockchip-emmc.c | 241 | ||||
-rw-r--r-- | drivers/phy/phy-rockchip-usb.c | 23 | ||||
-rw-r--r-- | drivers/phy/phy-sun4i-usb.c | 113 | ||||
-rw-r--r-- | drivers/phy/phy-sun9i-usb.c | 4 | ||||
-rw-r--r-- | drivers/phy/phy-xgene.c | 4 |
13 files changed, 770 insertions, 97 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b869b9883..19bff3a10 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -44,6 +44,16 @@ config ARMADA375_USBCLUSTER_PHY depends on OF && HAS_IOMEM select GENERIC_PHY +config PHY_DA8XX_USB + tristate "TI DA8xx USB PHY Driver" + depends on ARCH_DAVINCI_DA8XX + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the USB PHY on DA8xx SoCs. + + This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. + config PHY_DM816X_USB tristate "TI dm816x USB PHY driver" depends on ARCH_OMAP2PLUS @@ -176,6 +186,7 @@ config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS depends on USB_SUPPORT + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y' select GENERIC_PHY select USB_PHY help @@ -434,4 +445,12 @@ config PHY_CYGNUS_PCIE source "drivers/phy/tegra/Kconfig" +config PHY_NS2_PCIE + tristate "Broadcom Northstar2 PCIe PHY driver" + depends on OF && MDIO_BUS_MUX_BCM_IPROC + select GENERIC_PHY + default ARCH_BCM_IPROC + help + Enable this to support the Broadcom Northstar2 PCIe PHY. + If unsure, say N. endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 9c3e73cca..90ae19879 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_GENERIC_PHY) += phy-core.o obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o +obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o @@ -53,5 +54,5 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o - obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o diff --git a/drivers/phy/phy-bcm-ns2-pcie.c b/drivers/phy/phy-bcm-ns2-pcie.c new file mode 100644 index 000000000..9513f7ab1 --- /dev/null +++ b/drivers/phy/phy-bcm-ns2-pcie.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_mdio.h> +#include <linux/mdio.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> + +struct ns2_pci_phy { + struct mdio_device *mdiodev; + struct phy *phy; +}; + +#define BLK_ADDR_REG_OFFSET 0x1f +#define PLL_AFE1_100MHZ_BLK 0x2100 +#define PLL_CLK_AMP_OFFSET 0x03 +#define PLL_CLK_AMP_2P05V 0x2b18 + +static int ns2_pci_phy_init(struct phy *p) +{ + struct ns2_pci_phy *phy = phy_get_drvdata(p); + int rc; + + /* select the AFE 100MHz block page */ + rc = mdiobus_write(phy->mdiodev->bus, phy->mdiodev->addr, + BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); + if (rc) + goto err; + + /* set the 100 MHz reference clock amplitude to 2.05 v */ + rc = mdiobus_write(phy->mdiodev->bus, phy->mdiodev->addr, + PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); + if (rc) + goto err; + + return 0; + +err: + dev_err(&phy->mdiodev->dev, "Error %d writing to phy\n", rc); + return rc; +} + +static struct phy_ops ns2_pci_phy_ops = { + .init = ns2_pci_phy_init, +}; + +static int ns2_pci_phy_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct phy_provider *provider; + struct ns2_pci_phy *p; + struct phy *phy; + + phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create Phy\n"); + return PTR_ERR(phy); + } + + p = devm_kmalloc(dev, sizeof(struct ns2_pci_phy), + GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->mdiodev = mdiodev; + dev_set_drvdata(dev, p); + + p->phy = phy; + phy_set_drvdata(phy, p); + + provider = devm_of_phy_provider_register(&phy->dev, + of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register Phy provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "%s PHY registered\n", dev_name(dev)); + + return 0; +} + +static const struct of_device_id ns2_pci_phy_of_match[] = { + { .compatible = "brcm,ns2-pcie-phy", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ns2_pci_phy_of_match); + +static struct mdio_driver ns2_pci_phy_driver = { + .mdiodrv = { + .driver = { + .name = "phy-bcm-ns2-pci", + .of_match_table = ns2_pci_phy_of_match, + }, + }, + .probe = ns2_pci_phy_probe, +}; +mdio_module_driver(ns2_pci_phy_driver); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("Broadcom Northstar2 PCI Phy driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:phy-bcm-ns2-pci"); diff --git a/drivers/phy/phy-brcm-sata.c b/drivers/phy/phy-brcm-sata.c index 6c4c5cb79..8ffc44afd 100644 --- a/drivers/phy/phy-brcm-sata.c +++ b/drivers/phy/phy-brcm-sata.c @@ -45,6 +45,7 @@ enum brcm_sata_phy_version { BRCM_SATA_PHY_STB_28NM, BRCM_SATA_PHY_STB_40NM, BRCM_SATA_PHY_IPROC_NS2, + BRCM_SATA_PHY_IPROC_NSP, }; struct brcm_sata_port { @@ -73,6 +74,13 @@ enum sata_phy_regs { PLL_REG_BANK_0 = 0x050, PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, + PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), + PLLCONTROL_0_FREQ_MONITOR = BIT(12), + PLLCONTROL_0_SEQ_START = BIT(15), + PLL_CAP_CONTROL = 0x85, + PLL_ACTRL2 = 0x8b, + PLL_ACTRL2_SELDIV_MASK = 0x1f, + PLL_ACTRL2_SELDIV_SHIFT = 9, PLL1_REG_BANK = 0x060, PLL1_ACTRL2 = 0x82, @@ -80,6 +88,7 @@ enum sata_phy_regs { PLL1_ACTRL4 = 0x84, OOB_REG_BANK = 0x150, + OOB1_REG_BANK = 0x160, OOB_CTRL1 = 0x80, OOB_CTRL1_BURST_MAX_MASK = 0xf, OOB_CTRL1_BURST_MAX_SHIFT = 12, @@ -271,6 +280,73 @@ static int brcm_ns2_sata_init(struct brcm_sata_port *port) return 0; } +static int brcm_nsp_sata_init(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + struct device *dev = port->phy_priv->dev; + void __iomem *base = priv->phy_base; + unsigned int oob_bank; + unsigned int val, try; + + /* Configure OOB control */ + if (port->portnum == 0) + oob_bank = OOB_REG_BANK; + else if (port->portnum == 1) + oob_bank = OOB1_REG_BANK; + else + return -EINVAL; + + val = 0x0; + val |= (0x0f << OOB_CTRL1_BURST_MAX_SHIFT); + val |= (0x06 << OOB_CTRL1_BURST_MIN_SHIFT); + val |= (0x0f << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); + val |= (0x06 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, oob_bank, OOB_CTRL1, 0x0, val); + + val = 0x0; + val |= (0x2e << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); + val |= (0x02 << OOB_CTRL2_BURST_CNT_SHIFT); + val |= (0x16 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, oob_bank, OOB_CTRL2, 0x0, val); + + + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_ACTRL2, + ~(PLL_ACTRL2_SELDIV_MASK << PLL_ACTRL2_SELDIV_SHIFT), + 0x0c << PLL_ACTRL2_SELDIV_SHIFT); + + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CONTROL, + 0xff0, 0x4f0); + + val = PLLCONTROL_0_FREQ_DET_RESTART | PLLCONTROL_0_FREQ_MONITOR; + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, + ~val, val); + val = PLLCONTROL_0_SEQ_START; + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, + ~val, 0); + mdelay(10); + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, + ~val, val); + + /* Wait for pll_seq_done bit */ + try = 50; + while (try--) { + val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, + BLOCK0_XGXSSTATUS); + if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) + break; + msleep(20); + } + if (!try) { + /* PLL did not lock; give up */ + dev_err(dev, "port%d PLL did not lock\n", port->portnum); + return -ETIMEDOUT; + } + + dev_dbg(dev, "port%d initialized\n", port->portnum); + + return 0; +} + static int brcm_sata_phy_init(struct phy *phy) { int rc; @@ -284,11 +360,14 @@ static int brcm_sata_phy_init(struct phy *phy) case BRCM_SATA_PHY_IPROC_NS2: rc = brcm_ns2_sata_init(port); break; + case BRCM_SATA_PHY_IPROC_NSP: + rc = brcm_nsp_sata_init(port); + break; default: rc = -ENODEV; }; - return 0; + return rc; } static const struct phy_ops phy_ops = { @@ -303,6 +382,8 @@ static const struct of_device_id brcm_sata_phy_of_match[] = { .data = (void *)BRCM_SATA_PHY_STB_40NM }, { .compatible = "brcm,iproc-ns2-sata-phy", .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, + { .compatible = "brcm,iproc-nsp-sata-phy", + .data = (void *)BRCM_SATA_PHY_IPROC_NSP }, {}, }; MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index b72e9a3b6..8eca906b6 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -342,6 +342,21 @@ int phy_power_off(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_power_off); +int phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + int ret; + + if (!phy || !phy->ops->set_mode) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->set_mode(phy, mode); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_set_mode); + /** * _of_phy_get() - lookup and obtain a reference to a phy by phandle * @np: device_node for which to get the phy diff --git a/drivers/phy/phy-da8xx-usb.c b/drivers/phy/phy-da8xx-usb.c new file mode 100644 index 000000000..b2e59b617 --- /dev/null +++ b/drivers/phy/phy-da8xx-usb.c @@ -0,0 +1,245 @@ +/* + * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver + * + * Copyright (C) 2016 David Lechner <david@lechnology.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/mfd/da8xx-cfgchip.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +struct da8xx_usb_phy { + struct phy_provider *phy_provider; + struct phy *usb11_phy; + struct phy *usb20_phy; + struct clk *usb11_clk; + struct clk *usb20_clk; + struct regmap *regmap; +}; + +static int da8xx_usb11_phy_power_on(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(d_phy->usb11_clk); + if (ret) + return ret; + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, + CFGCHIP2_USB1SUSPENDM); + + return 0; +} + +static int da8xx_usb11_phy_power_off(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0); + + clk_disable_unprepare(d_phy->usb11_clk); + + return 0; +} + +static const struct phy_ops da8xx_usb11_phy_ops = { + .power_on = da8xx_usb11_phy_power_on, + .power_off = da8xx_usb11_phy_power_off, + .owner = THIS_MODULE, +}; + +static int da8xx_usb20_phy_power_on(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(d_phy->usb20_clk); + if (ret) + return ret; + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0); + + return 0; +} + +static int da8xx_usb20_phy_power_off(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, + CFGCHIP2_OTGPWRDN); + + clk_disable_unprepare(d_phy->usb20_clk); + + return 0; +} + +static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + u32 val; + + switch (mode) { + case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */ + val = CFGCHIP2_OTGMODE_FORCE_HOST; + break; + case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */ + val = CFGCHIP2_OTGMODE_FORCE_DEVICE; + break; + case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */ + val = CFGCHIP2_OTGMODE_NO_OVERRIDE; + break; + default: + return -EINVAL; + } + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK, + val); + + return 0; +} + +static const struct phy_ops da8xx_usb20_phy_ops = { + .power_on = da8xx_usb20_phy_power_on, + .power_off = da8xx_usb20_phy_power_off, + .set_mode = da8xx_usb20_phy_set_mode, + .owner = THIS_MODULE, +}; + +static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev); + + if (!d_phy) + return ERR_PTR(-ENODEV); + + switch (args->args[0]) { + case 0: + return d_phy->usb20_phy; + case 1: + return d_phy->usb11_phy; + default: + return ERR_PTR(-EINVAL); + } +} + +static int da8xx_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct da8xx_usb_phy *d_phy; + + d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL); + if (!d_phy) + return -ENOMEM; + + if (node) + d_phy->regmap = syscon_regmap_lookup_by_compatible( + "ti,da830-cfgchip"); + else + d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon.0"); + if (IS_ERR(d_phy->regmap)) { + dev_err(dev, "Failed to get syscon\n"); + return PTR_ERR(d_phy->regmap); + } + + d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); + if (IS_ERR(d_phy->usb11_clk)) { + dev_err(dev, "Failed to get usb11_phy clock\n"); + return PTR_ERR(d_phy->usb11_clk); + } + + d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); + if (IS_ERR(d_phy->usb20_clk)) { + dev_err(dev, "Failed to get usb20_phy clock\n"); + return PTR_ERR(d_phy->usb20_clk); + } + + d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops); + if (IS_ERR(d_phy->usb11_phy)) { + dev_err(dev, "Failed to create usb11 phy\n"); + return PTR_ERR(d_phy->usb11_phy); + } + + d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops); + if (IS_ERR(d_phy->usb20_phy)) { + dev_err(dev, "Failed to create usb20 phy\n"); + return PTR_ERR(d_phy->usb20_phy); + } + + platform_set_drvdata(pdev, d_phy); + phy_set_drvdata(d_phy->usb11_phy, d_phy); + phy_set_drvdata(d_phy->usb20_phy, d_phy); + + if (node) { + d_phy->phy_provider = devm_of_phy_provider_register(dev, + da8xx_usb_phy_of_xlate); + if (IS_ERR(d_phy->phy_provider)) { + dev_err(dev, "Failed to create phy provider\n"); + return PTR_ERR(d_phy->phy_provider); + } + } else { + int ret; + + ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0"); + if (ret) + dev_warn(dev, "Failed to create usb11 phy lookup\n"); + ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", + "musb-da8xx"); + if (ret) + dev_warn(dev, "Failed to create usb20 phy lookup\n"); + } + + return 0; +} + +static int da8xx_usb_phy_remove(struct platform_device *pdev) +{ + struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev); + + if (!pdev->dev.of_node) { + phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); + phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci.0"); + } + + return 0; +} + +static const struct of_device_id da8xx_usb_phy_ids[] = { + { .compatible = "ti,da830-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); + +static struct platform_driver da8xx_usb_phy_driver = { + .probe = da8xx_usb_phy_probe, + .remove = da8xx_usb_phy_remove, + .driver = { + .name = "da8xx-usb-phy", + .of_match_table = da8xx_usb_phy_ids, + }, +}; + +module_platform_driver(da8xx_usb_phy_driver); + +MODULE_ALIAS("platform:da8xx-usb-phy"); +MODULE_AUTHOR("David Lechner <david@lechnology.com>"); +MODULE_DESCRIPTION("TI DA8xx USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c index 56631e77c..6ee51490f 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -140,7 +140,6 @@ static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) { - dev_err(dev, "%s: failed to allocate phy\n", __func__); err = -ENOMEM; goto out; } diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c index b16ea77d0..770087ab0 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c @@ -196,7 +196,6 @@ static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); if (!phy) { - dev_err(dev, "%s: failed to allocate phy\n", __func__); err = -ENOMEM; goto out; } diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 6ebcf3e41..fd57345ff 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include <linux/clk.h> #include <linux/delay.h> #include <linux/mfd/syscon.h> #include <linux/module.h> @@ -31,42 +32,64 @@ ((val) << (shift) | (mask) << ((shift) + 16)) /* Register definition */ -#define GRF_EMMCPHY_CON0 0x0 -#define GRF_EMMCPHY_CON1 0x4 -#define GRF_EMMCPHY_CON2 0x8 -#define GRF_EMMCPHY_CON3 0xc -#define GRF_EMMCPHY_CON4 0x10 -#define GRF_EMMCPHY_CON5 0x14 -#define GRF_EMMCPHY_CON6 0x18 -#define GRF_EMMCPHY_STATUS 0x20 - -#define PHYCTRL_PDB_MASK 0x1 -#define PHYCTRL_PDB_SHIFT 0x0 -#define PHYCTRL_PDB_PWR_ON 0x1 -#define PHYCTRL_PDB_PWR_OFF 0x0 -#define PHYCTRL_ENDLL_MASK 0x1 -#define PHYCTRL_ENDLL_SHIFT 0x1 -#define PHYCTRL_ENDLL_ENABLE 0x1 -#define PHYCTRL_ENDLL_DISABLE 0x0 -#define PHYCTRL_CALDONE_MASK 0x1 -#define PHYCTRL_CALDONE_SHIFT 0x6 -#define PHYCTRL_CALDONE_DONE 0x1 -#define PHYCTRL_CALDONE_GOING 0x0 -#define PHYCTRL_DLLRDY_MASK 0x1 -#define PHYCTRL_DLLRDY_SHIFT 0x5 -#define PHYCTRL_DLLRDY_DONE 0x1 -#define PHYCTRL_DLLRDY_GOING 0x0 +#define GRF_EMMCPHY_CON0 0x0 +#define GRF_EMMCPHY_CON1 0x4 +#define GRF_EMMCPHY_CON2 0x8 +#define GRF_EMMCPHY_CON3 0xc +#define GRF_EMMCPHY_CON4 0x10 +#define GRF_EMMCPHY_CON5 0x14 +#define GRF_EMMCPHY_CON6 0x18 +#define GRF_EMMCPHY_STATUS 0x20 + +#define PHYCTRL_PDB_MASK 0x1 +#define PHYCTRL_PDB_SHIFT 0x0 +#define PHYCTRL_PDB_PWR_ON 0x1 +#define PHYCTRL_PDB_PWR_OFF 0x0 +#define PHYCTRL_ENDLL_MASK 0x1 +#define PHYCTRL_ENDLL_SHIFT 0x1 +#define PHYCTRL_ENDLL_ENABLE 0x1 +#define PHYCTRL_ENDLL_DISABLE 0x0 +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_CALDONE_GOING 0x0 +#define PHYCTRL_DLLRDY_MASK 0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE 0x1 +#define PHYCTRL_DLLRDY_GOING 0x0 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_FREQSEL_MASK 0x3 +#define PHYCTRL_FREQSEL_SHIFT 0xc +#define PHYCTRL_DR_MASK 0x7 +#define PHYCTRL_DR_SHIFT 0x4 +#define PHYCTRL_DR_50OHM 0x0 +#define PHYCTRL_DR_33OHM 0x1 +#define PHYCTRL_DR_66OHM 0x2 +#define PHYCTRL_DR_100OHM 0x3 +#define PHYCTRL_DR_40OHM 0x4 +#define PHYCTRL_OTAPDLYENA 0x1 +#define PHYCTRL_OTAPDLYENA_MASK 0x1 +#define PHYCTRL_OTAPDLYENA_SHIFT 0xb +#define PHYCTRL_OTAPDLYSEL_MASK 0xf +#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 struct rockchip_emmc_phy { unsigned int reg_offset; struct regmap *reg_base; + struct clk *emmcclk; }; -static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, - bool on_off) +static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) { + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); unsigned int caldone; unsigned int dllrdy; + unsigned int freqsel = PHYCTRL_FREQSEL_200M; + unsigned long rate; + unsigned long timeout; /* * Keep phyctrl_pdb and phyctrl_endll low to allow @@ -87,6 +110,43 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, if (on_off == PHYCTRL_PDB_PWR_OFF) return 0; + rate = clk_get_rate(rk_phy->emmcclk); + + if (rate != 0) { + unsigned long ideal_rate; + unsigned long diff; + + switch (rate) { + case 1 ... 74999999: + ideal_rate = 50000000; + freqsel = PHYCTRL_FREQSEL_50M; + break; + case 75000000 ... 124999999: + ideal_rate = 100000000; + freqsel = PHYCTRL_FREQSEL_100M; + break; + case 125000000 ... 174999999: + ideal_rate = 150000000; + freqsel = PHYCTRL_FREQSEL_150M; + break; + default: + ideal_rate = 200000000; + break; + }; + + diff = (rate > ideal_rate) ? + rate - ideal_rate : ideal_rate - rate; + + /* + * In order for tuning delays to be accurate we need to be + * pretty spot on for the DLL range, so warn if we're too + * far off. Also warn if we're above the 200 MHz max. Don't + * warn for really slow rates since we won't be tuning then. + */ + if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + } + /* * According to the user manual, calpad calibration * cycle takes more than 2us without the minimal recommended @@ -113,20 +173,62 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return -ETIMEDOUT; } + /* Set the frequency of the DLL operation */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Turn on the DLL */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); + /* - * After enable analog DLL circuits, we need extra 10.2us - * for dll to be ready for work. + * We turned on the DLL even though the rate was 0 because we the + * clock might be turned on later. ...but we can't wait for the DLL + * to lock when the rate is 0 because it will never lock with no + * input clock. + * + * Technically we should be checking the lock later when the clock + * is turned on, but for now we won't. */ - udelay(11); - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &dllrdy); - dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (rate == 0) + return 0; + + /* + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100 kHZ) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * Hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. We still busy wait here because: + * - In most cases it should be super fast. + * - This is not called lots during normal operation so it shouldn't + * be a power or performance problem to busy wait. We expect it + * only at boot / resume. In both cases, eMMC is probably on the + * critical path so busy waiting a little extra time should be OK. + */ + timeout = jiffies + msecs_to_jiffies(50); + do { + udelay(1); + + regmap_read(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + &dllrdy); + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (dllrdy == PHYCTRL_DLLRDY_DONE) + break; + } while (!time_after(jiffies, timeout)); + if (dllrdy != PHYCTRL_DLLRDY_DONE) { pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); return -ETIMEDOUT; @@ -135,33 +237,82 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return 0; } -static int rockchip_emmc_phy_power_off(struct phy *phy) +static int rockchip_emmc_phy_init(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); int ret = 0; - /* Power down emmc phy analog blocks */ - ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); - if (ret) - return ret; + /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to init the PHY + * + * The clock is optional, so upon any error we just set to NULL. + * + * NOTE: we don't do anything special for EPROBE_DEFER here. Given the + * above expected use case, EPROBE_DEFER isn't sensible to expect, so + * it's just like any other error. + */ + rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + if (IS_ERR(rk_phy->emmcclk)) { + dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); + rk_phy->emmcclk = NULL; + } + + return ret; +} + +static int rockchip_emmc_phy_exit(struct phy *phy) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + + clk_put(rk_phy->emmcclk); return 0; } +static int rockchip_emmc_phy_power_off(struct phy *phy) +{ + /* Power down emmc phy analog blocks */ + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); +} + static int rockchip_emmc_phy_power_on(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - int ret = 0; - /* Power up emmc phy analog blocks */ - ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); - if (ret) - return ret; + /* Drive impedance: 50 Ohm */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_DR_50OHM, + PHYCTRL_DR_MASK, + PHYCTRL_DR_SHIFT)); - return 0; + /* Output tap delay: enable */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, + PHYCTRL_OTAPDLYENA_MASK, + PHYCTRL_OTAPDLYENA_SHIFT)); + + /* Output tap delay */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(4, + PHYCTRL_OTAPDLYSEL_MASK, + PHYCTRL_OTAPDLYSEL_SHIFT)); + + /* Power up emmc phy analog blocks */ + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); } static const struct phy_ops ops = { + .init = rockchip_emmc_phy_init, + .exit = rockchip_emmc_phy_exit, .power_on = rockchip_emmc_phy_power_on, .power_off = rockchip_emmc_phy_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index d60b149cf..2a7381f4f 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -236,9 +236,10 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, goto err_clk_prov; } - err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy); + err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action, + rk_phy); if (err) - goto err_devm_action; + return err; rk_phy->phy = devm_phy_create(base->dev, child, &ops); if (IS_ERR(rk_phy->phy)) { @@ -256,9 +257,6 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, else return rockchip_usb_phy_power(rk_phy, 1); -err_devm_action: - if (!rk_phy->uart_enabled) - of_clk_del_provider(child); err_clk_prov: if (!rk_phy->uart_enabled) clk_unregister(rk_phy->clk480m); @@ -397,8 +395,13 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev) phy_base->pdata = match->data; phy_base->dev = dev; - phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node, - "rockchip,grf"); + phy_base->reg_base = ERR_PTR(-ENODEV); + if (dev->parent && dev->parent->of_node) + phy_base->reg_base = syscon_node_to_regmap( + dev->parent->of_node); + if (IS_ERR(phy_base->reg_base)) + phy_base->reg_base = syscon_regmap_lookup_by_phandle( + dev->of_node, "rockchip,grf"); if (IS_ERR(phy_base->reg_base)) { dev_err(&pdev->dev, "Missing rockchip,grf property\n"); return PTR_ERR(phy_base->reg_base); @@ -463,7 +466,11 @@ static int __init rockchip_init_usb_uart(void) return -ENOTSUPP; } - grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + grf = ERR_PTR(-ENODEV); + if (np->parent) + grf = syscon_node_to_regmap(np->parent); + if (IS_ERR(grf)) + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(grf)) { pr_err("%s: Missing rockchip,grf property, %lu\n", __func__, PTR_ERR(grf)); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index de3101fbb..4d74ca918 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -40,6 +40,8 @@ #include <linux/power_supply.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> +#include <linux/spinlock.h> +#include <linux/usb/of.h> #include <linux/workqueue.h> #define REG_ISCR 0x00 @@ -94,6 +96,7 @@ enum sun4i_usb_phy_type { sun4i_a10_phy, + sun6i_a31_phy, sun8i_a33_phy, sun8i_h3_phy, }; @@ -109,7 +112,8 @@ struct sun4i_usb_phy_cfg { struct sun4i_usb_phy_data { void __iomem *base; const struct sun4i_usb_phy_cfg *cfg; - struct mutex mutex; + enum usb_dr_mode dr_mode; + spinlock_t reg_lock; /* guard access to phyctl reg */ struct sun4i_usb_phy { struct phy *phy; void __iomem *pmu; @@ -119,10 +123,10 @@ struct sun4i_usb_phy_data { bool regulator_on; int index; } phys[MAX_PHYS]; + int first_phy; /* phy0 / otg related variables */ struct extcon_dev *extcon; bool phy0_init; - bool phy0_poll; struct gpio_desc *id_det_gpio; struct gpio_desc *vbus_det_gpio; struct power_supply *vbus_power_supply; @@ -176,9 +180,10 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); u32 temp, usbc_bit = BIT(phy->index * 2); void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; + unsigned long flags; int i; - mutex_lock(&phy_data->mutex); + spin_lock_irqsave(&phy_data->reg_lock, flags); if (phy_data->cfg->type == sun8i_a33_phy) { /* A33 needs us to set phyctl to 0 explicitly */ @@ -215,7 +220,8 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, data >>= 1; } - mutex_unlock(&phy_data->mutex); + + spin_unlock_irqrestore(&phy_data->reg_lock, flags); } static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) @@ -285,16 +291,10 @@ static int sun4i_usb_phy_init(struct phy *_phy) sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); - if (data->id_det_gpio) { - /* OTG mode, force ISCR and cable state updates */ - data->id_det = -1; - data->vbus_det = -1; - queue_delayed_work(system_wq, &data->detect, 0); - } else { - /* Host only mode */ - sun4i_usb_phy0_set_id_detect(_phy, 0); - sun4i_usb_phy0_set_vbus_detect(_phy, 1); - } + /* Force ISCR and cable state updates */ + data->id_det = -1; + data->vbus_det = -1; + queue_delayed_work(system_wq, &data->detect, 0); } return 0; @@ -319,6 +319,19 @@ static int sun4i_usb_phy_exit(struct phy *_phy) return 0; } +static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) +{ + switch (data->dr_mode) { + case USB_DR_MODE_OTG: + return gpiod_get_value_cansleep(data->id_det_gpio); + case USB_DR_MODE_HOST: + return 0; + case USB_DR_MODE_PERIPHERAL: + default: + return 1; + } +} + static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) { if (data->vbus_det_gpio) @@ -343,6 +356,24 @@ static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) return data->vbus_det_gpio || data->vbus_power_supply; } +static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) +{ + if ((data->id_det_gpio && data->id_det_irq <= 0) || + (data->vbus_det_gpio && data->vbus_det_irq <= 0)) + return true; + + /* + * The A31 companion pmic (axp221) does not generate vbus change + * interrupts when the board is driving vbus, so we must poll + * when using the pmic for vbus-det _and_ we're driving vbus. + */ + if (data->cfg->type == sun6i_a31_phy && + data->vbus_power_supply && data->phys[0].regulator_on) + return true; + + return false; +} + static int sun4i_usb_phy_power_on(struct phy *_phy) { struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); @@ -364,7 +395,7 @@ static int sun4i_usb_phy_power_on(struct phy *_phy) phy->regulator_on = true; /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ - if (phy->index == 0 && data->vbus_det_gpio && data->phy0_poll) + if (phy->index == 0 && sun4i_usb_phy0_poll(data)) mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); return 0; @@ -385,7 +416,7 @@ static int sun4i_usb_phy_power_off(struct phy *_phy) * phy0 vbus typically slowly discharges, sometimes this causes the * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. */ - if (phy->index == 0 && data->vbus_det_gpio && !data->phy0_poll) + if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) mod_delayed_work(system_wq, &data->detect, POLL_TIME); return 0; @@ -414,7 +445,10 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) struct phy *phy0 = data->phys[0].phy; int id_det, vbus_det, id_notify = 0, vbus_notify = 0; - id_det = gpiod_get_value_cansleep(data->id_det_gpio); + if (phy0 == NULL) + return; + + id_det = sun4i_usb_phy0_get_id_det(data); vbus_det = sun4i_usb_phy0_get_vbus_det(data); mutex_lock(&phy0->mutex); @@ -430,7 +464,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) * without vbus detection report vbus low for long enough for * the musb-ip to end the current device session. */ - if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { + if (data->dr_mode == USB_DR_MODE_OTG && + !sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) { sun4i_usb_phy0_set_vbus_detect(phy0, 0); msleep(200); sun4i_usb_phy0_set_vbus_detect(phy0, 1); @@ -456,7 +491,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) * without vbus detection report vbus low for long enough to * the musb-ip to end the current host session. */ - if (!sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { + if (data->dr_mode == USB_DR_MODE_OTG && + !sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) { mutex_lock(&phy0->mutex); sun4i_usb_phy0_set_vbus_detect(phy0, 0); msleep(1000); @@ -468,7 +504,7 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) if (vbus_notify) extcon_set_cable_state_(data->extcon, EXTCON_USB, vbus_det); - if (data->phy0_poll) + if (sun4i_usb_phy0_poll(data)) queue_delayed_work(system_wq, &data->detect, POLL_TIME); } @@ -501,7 +537,8 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, { struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - if (args->args[0] >= data->cfg->num_phys) + if (args->args[0] < data->first_phy || + args->args[0] >= data->cfg->num_phys) return ERR_PTR(-ENODEV); return data->phys[args->args[0]].phy; @@ -543,7 +580,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (!data) return -ENOMEM; - mutex_init(&data->mutex); + spin_lock_init(&data->reg_lock); INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); dev_set_drvdata(dev, data); data->cfg = of_device_get_match_data(dev); @@ -575,13 +612,17 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - /* vbus_det without id_det makes no sense, and is not supported */ - if (sun4i_usb_phy0_have_vbus_det(data) && !data->id_det_gpio) { - dev_err(dev, "usb0_id_det missing or invalid\n"); - return -ENODEV; - } - - if (data->id_det_gpio) { + data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); + switch (data->dr_mode) { + case USB_DR_MODE_OTG: + /* otg without id_det makes no sense, and is not supported */ + if (!data->id_det_gpio) { + dev_err(dev, "usb0_id_det missing or invalid\n"); + return -ENODEV; + } + /* fall through */ + case USB_DR_MODE_HOST: + case USB_DR_MODE_PERIPHERAL: data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); if (IS_ERR(data->extcon)) @@ -592,9 +633,13 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) dev_err(dev, "failed to register extcon: %d\n", ret); return ret; } + break; + default: + dev_info(dev, "dr_mode unknown, not registering usb phy0\n"); + data->first_phy = 1; } - for (i = 0; i < data->cfg->num_phys; i++) { + for (i = data->first_phy; i < data->cfg->num_phys; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[16]; @@ -644,11 +689,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } data->id_det_irq = gpiod_to_irq(data->id_det_gpio); - data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); - if ((data->id_det_gpio && data->id_det_irq <= 0) || - (data->vbus_det_gpio && data->vbus_det_irq <= 0)) - data->phy0_poll = true; - if (data->id_det_irq > 0) { ret = devm_request_irq(dev, data->id_det_irq, sun4i_usb_phy0_id_vbus_det_irq, @@ -660,6 +700,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } } + data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); if (data->vbus_det_irq > 0) { ret = devm_request_irq(dev, data->vbus_det_irq, sun4i_usb_phy0_id_vbus_det_irq, @@ -711,7 +752,7 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { .num_phys = 3, - .type = sun4i_a10_phy, + .type = sun6i_a31_phy, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, diff --git a/drivers/phy/phy-sun9i-usb.c b/drivers/phy/phy-sun9i-usb.c index ac4f31abe..28fce4bce 100644 --- a/drivers/phy/phy-sun9i-usb.c +++ b/drivers/phy/phy-sun9i-usb.c @@ -141,9 +141,9 @@ static int sun9i_usb_phy_probe(struct platform_device *pdev) } phy->hsic_clk = devm_clk_get(dev, "hsic_12M"); - if (IS_ERR(phy->clk)) { + if (IS_ERR(phy->hsic_clk)) { dev_err(dev, "failed to get hsic_12M clock\n"); - return PTR_ERR(phy->clk); + return PTR_ERR(phy->hsic_clk); } phy->reset = devm_reset_control_get(dev, "hsic"); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 385362e5b..ae266e0c8 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -518,7 +518,7 @@ enum clk_type_t { CLK_INT_SING = 2, /* Internal single ended */ }; -enum phy_mode { +enum xgene_phy_mode { MODE_SATA = 0, /* List them for simple reference */ MODE_SGMII = 1, MODE_PCIE = 2, @@ -542,7 +542,7 @@ struct xgene_sata_override_param { struct xgene_phy_ctx { struct device *dev; struct phy *phy; - enum phy_mode mode; /* Mode of operation */ + enum xgene_phy_mode mode; /* Mode of operation */ enum clk_type_t clk_type; /* Input clock selection */ void __iomem *sds_base; /* PHY CSR base addr */ struct clk *clk; /* Optional clock */ |