diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-20 21:12:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-20 21:12:25 -0700 |
commit | 19e36ad292ab24980db64a5ff17973d3118a8fb9 (patch) | |
tree | 175715409a689814e5cd425a98f2d2d47f82addf /drivers/phy | |
parent | e10abc629f38efd9b6936cf3612583cc846104d9 (diff) | |
parent | 60d5794fe5a50d02f4a0df84b45910a4dfa8b487 (diff) | |
download | lwn-19e36ad292ab24980db64a5ff17973d3118a8fb9.tar.gz lwn-19e36ad292ab24980db64a5ff17973d3118a8fb9.zip |
Merge tag 'usb-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH:
"Here's the big pull request for USB and PHY drivers for 4.7-rc1
Full details in the shortlog, but it's the normal major gadget driver
updates, phy updates, new usbip code, as well as a bit of lots of
other stuff.
All have been in linux-next with no reported issues"
* tag 'usb-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (164 commits)
USB: serial: ti_usb_3410_5052: add MOXA UPORT 11x0 support
USB: serial: fix minor-number allocation
USB: serial: quatech2: fix use-after-free in probe error path
USB: serial: mxuport: fix use-after-free in probe error path
USB: serial: keyspan: fix debug and error messages
USB: serial: keyspan: fix URB unlink
USB: serial: keyspan: fix use-after-free in probe error path
USB: serial: io_edgeport: fix memory leaks in probe error path
USB: serial: io_edgeport: fix memory leaks in attach error path
usb: Remove unnecessary space before operator ','.
usb: Remove unnecessary space before open square bracket.
USB: FHCI: avoid redundant condition
usb: host: xhci-rcar: Avoid long wait in xhci_reset()
usb/host/fotg210: remove dead code in create_sysfs_files
usb: wusbcore: Do not initialise statics to 0.
usb: wusbcore: Remove space before ',' and '(' .
USB: serial: cp210x: clean up CRTSCTS flag code
USB: serial: cp210x: get rid of magic numbers in CRTSCTS flag code
USB: serial: cp210x: fix hardware flow-control disable
USB: serial: option: add even more ZTE device ids
...
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 33 | ||||
-rw-r--r-- | drivers/phy/Makefile | 3 | ||||
-rw-r--r-- | drivers/phy/phy-bcm-ns-usb2.c | 137 | ||||
-rw-r--r-- | drivers/phy/phy-brcm-sata.c | 412 | ||||
-rw-r--r-- | drivers/phy/phy-brcmstb-sata.c | 250 | ||||
-rw-r--r-- | drivers/phy/phy-exynos-mipi-video.c | 321 | ||||
-rw-r--r-- | drivers/phy/phy-mt65xx-usb3.c | 77 | ||||
-rw-r--r-- | drivers/phy/phy-rcar-gen2.c | 1 | ||||
-rw-r--r-- | drivers/phy/phy-rcar-gen3-usb2.c | 88 | ||||
-rw-r--r-- | drivers/phy/phy-rockchip-usb.c | 2 |
10 files changed, 952 insertions, 372 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 27e5f6ee9a2a..b869b98835f4 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,6 +15,15 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config PHY_BCM_NS_USB2 + tristate "Broadcom Northstar USB 2.0 PHY Driver" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support Broadcom USB 2.0 PHY connected to the USB + controller on Northstar family. + config PHY_BERLIN_USB tristate "Marvell Berlin USB PHY Driver" depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF @@ -113,14 +122,15 @@ config PHY_MIPHY365X config PHY_RCAR_GEN2 tristate "Renesas R-Car generation 2 USB PHY driver" - depends on ARCH_SHMOBILE + depends on ARCH_RENESAS depends on GENERIC_PHY help Support for USB PHY found on Renesas R-Car generation 2 SoCs. config PHY_RCAR_GEN3_USB2 tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" - depends on OF && ARCH_SHMOBILE + depends on ARCH_RENESAS + depends on EXTCON select GENERIC_PHY help Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. @@ -218,9 +228,8 @@ config PHY_MT65XX_USB3 depends on ARCH_MEDIATEK && OF select GENERIC_PHY help - Say 'Y' here to add support for Mediatek USB3.0 PHY driver - for mt65xx SoCs. it supports two usb2.0 ports and - one usb3.0 port. + Say 'Y' here to add support for Mediatek USB3.0 PHY driver, + it supports multiple usb2.0 and usb3.0 ports. config PHY_HI6220_USB tristate "hi6220 USB PHY support" @@ -250,7 +259,8 @@ config PHY_SUN9I_USB tristate "Allwinner sun9i SoC USB PHY driver" depends on ARCH_SUNXI && HAS_IOMEM && OF depends on RESET_CONTROLLER - depends on USB_COMMON + depends on USB_SUPPORT + select USB_COMMON select GENERIC_PHY help Enable this to support the transceiver that is part of Allwinner @@ -403,14 +413,15 @@ config PHY_TUSB1210 help Support for TI TUSB1210 USB ULPI PHY. -config PHY_BRCMSTB_SATA - tristate "Broadcom STB SATA PHY driver" - depends on ARCH_BRCMSTB || BMIPS_GENERIC +config PHY_BRCM_SATA + tristate "Broadcom SATA PHY driver" + depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST depends on OF select GENERIC_PHY + default ARCH_BCM_IPROC help - Enable this to support the SATA3 PHY on 28nm or 40nm Broadcom STB SoCs. - Likely useful only with CONFIG_SATA_BRCMSTB enabled. + Enable this to support the Broadcom SATA PHY. + If unsure, say N. config PHY_CYGNUS_PCIE tristate "Broadcom Cygnus PCIe PHY driver" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index d4f06e69fd9a..9c3e73ccabc4 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,6 +3,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_DM816X_USB) += phy-dm816x-usb.o @@ -49,7 +50,7 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o -obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.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 diff --git a/drivers/phy/phy-bcm-ns-usb2.c b/drivers/phy/phy-bcm-ns-usb2.c new file mode 100644 index 000000000000..95ab6b2a0de5 --- /dev/null +++ b/drivers/phy/phy-bcm-ns-usb2.c @@ -0,0 +1,137 @@ +/* + * Broadcom Northstar USB 2.0 PHY Driver + * + * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/bcma/bcma.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct bcm_ns_usb2 { + struct device *dev; + struct clk *ref_clk; + struct phy *phy; + void __iomem *dmu; +}; + +static int bcm_ns_usb2_phy_init(struct phy *phy) +{ + struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); + struct device *dev = usb2->dev; + void __iomem *dmu = usb2->dmu; + u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; + int err = 0; + + err = clk_prepare_enable(usb2->ref_clk); + if (err < 0) { + dev_err(dev, "Failed to prepare ref clock: %d\n", err); + goto err_out; + } + + ref_clk_rate = clk_get_rate(usb2->ref_clk); + if (!ref_clk_rate) { + dev_err(dev, "Failed to get ref clock rate\n"); + err = -EINVAL; + goto err_clk_off; + } + + usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); + + if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { + usb_pll_pdiv = usb2ctl; + usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK; + usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT; + } else { + usb_pll_pdiv = 1 << 3; + } + + /* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */ + usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; + + /* Unlock DMU PLL settings with some magic value */ + writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); + + /* Write USB 2.0 PLL control setting */ + usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; + usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; + writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); + + /* Lock DMU PLL settings */ + writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); + +err_clk_off: + clk_disable_unprepare(usb2->ref_clk); +err_out: + return err; +} + +static const struct phy_ops ops = { + .init = bcm_ns_usb2_phy_init, + .owner = THIS_MODULE, +}; + +static int bcm_ns_usb2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm_ns_usb2 *usb2; + struct resource *res; + struct phy_provider *phy_provider; + + usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); + if (!usb2) + return -ENOMEM; + usb2->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); + usb2->dmu = devm_ioremap_resource(dev, res); + if (IS_ERR(usb2->dmu)) { + dev_err(dev, "Failed to map DMU regs\n"); + return PTR_ERR(usb2->dmu); + } + + usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); + if (IS_ERR(usb2->ref_clk)) { + dev_err(dev, "Clock not defined\n"); + return PTR_ERR(usb2->ref_clk); + } + + usb2->phy = devm_phy_create(dev, NULL, &ops); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + phy_set_drvdata(usb2->phy, usb2); + platform_set_drvdata(pdev, usb2); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id bcm_ns_usb2_id_table[] = { + { .compatible = "brcm,ns-usb2-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table); + +static struct platform_driver bcm_ns_usb2_driver = { + .probe = bcm_ns_usb2_probe, + .driver = { + .name = "bcm_ns_usb2", + .of_match_table = bcm_ns_usb2_id_table, + }, +}; +module_platform_driver(bcm_ns_usb2_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-brcm-sata.c b/drivers/phy/phy-brcm-sata.c new file mode 100644 index 000000000000..6c4c5cb791ca --- /dev/null +++ b/drivers/phy/phy-brcm-sata.c @@ -0,0 +1,412 @@ +/* + * Broadcom SATA3 AHCI Controller PHY Driver + * + * 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; either version 2, or (at your option) + * any later version. + * + * 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/delay.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#define SATA_PCB_BANK_OFFSET 0x23c +#define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4) + +#define MAX_PORTS 2 + +/* Register offset between PHYs in PCB space */ +#define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000 + +/* The older SATA PHY registers duplicated per port registers within the map, + * rather than having a separate map per port. + */ +#define SATA_PCB_REG_40NM_SPACE_SIZE 0x10 + +/* Register offset between PHYs in PHY control space */ +#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 + +enum brcm_sata_phy_version { + BRCM_SATA_PHY_STB_28NM, + BRCM_SATA_PHY_STB_40NM, + BRCM_SATA_PHY_IPROC_NS2, +}; + +struct brcm_sata_port { + int portnum; + struct phy *phy; + struct brcm_sata_phy *phy_priv; + bool ssc_en; +}; + +struct brcm_sata_phy { + struct device *dev; + void __iomem *phy_base; + void __iomem *ctrl_base; + enum brcm_sata_phy_version version; + + struct brcm_sata_port phys[MAX_PORTS]; +}; + +enum sata_phy_regs { + BLOCK0_REG_BANK = 0x000, + BLOCK0_XGXSSTATUS = 0x81, + BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12), + BLOCK0_SPARE = 0x8d, + BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, + BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, + + PLL_REG_BANK_0 = 0x050, + PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, + + PLL1_REG_BANK = 0x060, + PLL1_ACTRL2 = 0x82, + PLL1_ACTRL3 = 0x83, + PLL1_ACTRL4 = 0x84, + + OOB_REG_BANK = 0x150, + OOB_CTRL1 = 0x80, + OOB_CTRL1_BURST_MAX_MASK = 0xf, + OOB_CTRL1_BURST_MAX_SHIFT = 12, + OOB_CTRL1_BURST_MIN_MASK = 0xf, + OOB_CTRL1_BURST_MIN_SHIFT = 8, + OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf, + OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4, + OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf, + OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0, + OOB_CTRL2 = 0x81, + OOB_CTRL2_SEL_ENA_SHIFT = 15, + OOB_CTRL2_SEL_ENA_RC_SHIFT = 14, + OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f, + OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8, + OOB_CTRL2_BURST_CNT_MASK = 0x3, + OOB_CTRL2_BURST_CNT_SHIFT = 6, + OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f, + OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0, + + TXPMD_REG_BANK = 0x1a0, + TXPMD_CONTROL1 = 0x81, + TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), + TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), + TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, + TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, + TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, + TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, + TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, +}; + +enum sata_phy_ctrl_regs { + PHY_CTRL_1 = 0x0, + PHY_CTRL_1_RESET = BIT(0), +}; + +static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + u32 size = 0; + + switch (priv->version) { + case BRCM_SATA_PHY_STB_28NM: + case BRCM_SATA_PHY_IPROC_NS2: + size = SATA_PCB_REG_28NM_SPACE_SIZE; + break; + case BRCM_SATA_PHY_STB_40NM: + size = SATA_PCB_REG_40NM_SPACE_SIZE; + break; + default: + dev_err(priv->dev, "invalid phy version\n"); + break; + }; + + return priv->phy_base + (port->portnum * size); +} + +static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + u32 size = 0; + + switch (priv->version) { + case BRCM_SATA_PHY_IPROC_NS2: + size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE; + break; + default: + dev_err(priv->dev, "invalid phy version\n"); + break; + }; + + return priv->ctrl_base + (port->portnum * size); +} + +static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank, + u32 ofs, u32 msk, u32 value) +{ + u32 tmp; + + writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); + tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); + tmp = (tmp & msk) | value; + writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs)); +} + +static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) +{ + writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); + return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); +} + +/* These defaults were characterized by H/W group */ +#define STB_FMIN_VAL_DEFAULT 0x3df +#define STB_FMAX_VAL_DEFAULT 0x3df +#define STB_FMAX_VAL_SSC 0x83 + +static int brcm_stb_sata_init(struct brcm_sata_port *port) +{ + void __iomem *base = brcm_sata_pcb_base(port); + struct brcm_sata_phy *priv = port->phy_priv; + u32 tmp; + + /* override the TX spread spectrum setting */ + tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; + brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); + + /* set fixed min freq */ + brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, + ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, + STB_FMIN_VAL_DEFAULT); + + /* set fixed max freq depending on SSC config */ + if (port->ssc_en) { + dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum); + tmp = STB_FMAX_VAL_SSC; + } else { + tmp = STB_FMAX_VAL_DEFAULT; + } + + brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, + ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); + + return 0; +} + +/* NS2 SATA PLL1 defaults were characterized by H/W group */ +#define NS2_PLL1_ACTRL2_MAGIC 0x1df8 +#define NS2_PLL1_ACTRL3_MAGIC 0x2b00 +#define NS2_PLL1_ACTRL4_MAGIC 0x8824 + +static int brcm_ns2_sata_init(struct brcm_sata_port *port) +{ + int try; + unsigned int val; + void __iomem *base = brcm_sata_pcb_base(port); + void __iomem *ctrl_base = brcm_sata_ctrl_base(port); + struct device *dev = port->phy_priv->dev; + + /* Configure OOB control */ + val = 0x0; + val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT); + val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT); + val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); + val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val); + val = 0x0; + val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); + val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT); + val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val); + + /* Configure PHY PLL register bank 1 */ + val = NS2_PLL1_ACTRL2_MAGIC; + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val); + val = NS2_PLL1_ACTRL3_MAGIC; + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val); + val = NS2_PLL1_ACTRL4_MAGIC; + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val); + + /* Configure PHY BLOCK0 register bank */ + /* Set oob_clk_sel to refclk/2 */ + brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE, + ~BLOCK0_SPARE_OOB_CLK_SEL_MASK, + BLOCK0_SPARE_OOB_CLK_SEL_REFBY2); + + /* Strobe PHY reset using PHY control register */ + writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1); + mdelay(1); + writel(0x0, ctrl_base + PHY_CTRL_1); + mdelay(1); + + /* Wait for PHY PLL lock by polling pll_lock 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); + try--; + } + 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; + struct brcm_sata_port *port = phy_get_drvdata(phy); + + switch (port->phy_priv->version) { + case BRCM_SATA_PHY_STB_28NM: + case BRCM_SATA_PHY_STB_40NM: + rc = brcm_stb_sata_init(port); + break; + case BRCM_SATA_PHY_IPROC_NS2: + rc = brcm_ns2_sata_init(port); + break; + default: + rc = -ENODEV; + }; + + return 0; +} + +static const struct phy_ops phy_ops = { + .init = brcm_sata_phy_init, + .owner = THIS_MODULE, +}; + +static const struct of_device_id brcm_sata_phy_of_match[] = { + { .compatible = "brcm,bcm7445-sata-phy", + .data = (void *)BRCM_SATA_PHY_STB_28NM }, + { .compatible = "brcm,bcm7425-sata-phy", + .data = (void *)BRCM_SATA_PHY_STB_40NM }, + { .compatible = "brcm,iproc-ns2-sata-phy", + .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); + +static int brcm_sata_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + const struct of_device_id *of_id; + struct brcm_sata_phy *priv; + struct resource *res; + struct phy_provider *provider; + int ret, count = 0; + + if (of_get_child_count(dn) == 0) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(dev, priv); + priv->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + priv->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->phy_base)) + return PTR_ERR(priv->phy_base); + + of_id = of_match_node(brcm_sata_phy_of_match, dn); + if (of_id) + priv->version = (enum brcm_sata_phy_version)of_id->data; + else + priv->version = BRCM_SATA_PHY_STB_28NM; + + if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phy-ctrl"); + priv->ctrl_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ctrl_base)) + return PTR_ERR(priv->ctrl_base); + } + + for_each_available_child_of_node(dn, child) { + unsigned int id; + struct brcm_sata_port *port; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + ret = -EINVAL; + goto put_child; + } + + if (id >= MAX_PORTS) { + dev_err(dev, "invalid reg: %u\n", id); + ret = -EINVAL; + goto put_child; + } + if (priv->phys[id].phy) { + dev_err(dev, "already registered port %u\n", id); + ret = -EINVAL; + goto put_child; + } + + port = &priv->phys[id]; + port->portnum = id; + port->phy_priv = priv; + port->phy = devm_phy_create(dev, child, &phy_ops); + port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); + if (IS_ERR(port->phy)) { + dev_err(dev, "failed to create PHY\n"); + ret = PTR_ERR(port->phy); + goto put_child; + } + + phy_set_drvdata(port->phy, port); + count++; + } + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "could not register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "registered %d port(s)\n", count); + + return 0; +put_child: + of_node_put(child); + return ret; +} + +static struct platform_driver brcm_sata_phy_driver = { + .probe = brcm_sata_phy_probe, + .driver = { + .of_match_table = brcm_sata_phy_of_match, + .name = "brcm-sata-phy", + } +}; +module_platform_driver(brcm_sata_phy_driver); + +MODULE_DESCRIPTION("Broadcom SATA PHY driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marc Carino"); +MODULE_AUTHOR("Brian Norris"); +MODULE_ALIAS("platform:phy-brcm-sata"); diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c deleted file mode 100644 index a23172ff40e3..000000000000 --- a/drivers/phy/phy-brcmstb-sata.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Broadcom SATA3 AHCI Controller PHY Driver - * - * Copyright © 2009-2015 Broadcom Corporation - * - * 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; either version 2, or (at your option) - * any later version. - * - * 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/device.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/phy/phy.h> -#include <linux/platform_device.h> - -#define SATA_MDIO_BANK_OFFSET 0x23c -#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4) - -#define MAX_PORTS 2 - -/* Register offset between PHYs in PCB space */ -#define SATA_MDIO_REG_28NM_SPACE_SIZE 0x1000 - -/* The older SATA PHY registers duplicated per port registers within the map, - * rather than having a separate map per port. - */ -#define SATA_MDIO_REG_40NM_SPACE_SIZE 0x10 - -enum brcm_sata_phy_version { - BRCM_SATA_PHY_28NM, - BRCM_SATA_PHY_40NM, -}; - -struct brcm_sata_port { - int portnum; - struct phy *phy; - struct brcm_sata_phy *phy_priv; - bool ssc_en; -}; - -struct brcm_sata_phy { - struct device *dev; - void __iomem *phy_base; - enum brcm_sata_phy_version version; - - struct brcm_sata_port phys[MAX_PORTS]; -}; - -enum sata_mdio_phy_regs { - PLL_REG_BANK_0 = 0x50, - PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, - - TXPMD_REG_BANK = 0x1a0, - TXPMD_CONTROL1 = 0x81, - TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), - TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), - TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, - TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, - TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, - TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, - TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, -}; - -static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port) -{ - struct brcm_sata_phy *priv = port->phy_priv; - u32 offset = 0; - - if (priv->version == BRCM_SATA_PHY_28NM) - offset = SATA_MDIO_REG_28NM_SPACE_SIZE; - else if (priv->version == BRCM_SATA_PHY_40NM) - offset = SATA_MDIO_REG_40NM_SPACE_SIZE; - else - dev_err(priv->dev, "invalid phy version\n"); - - return priv->phy_base + (port->portnum * offset); -} - -static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs, - u32 msk, u32 value) -{ - u32 tmp; - - writel(bank, addr + SATA_MDIO_BANK_OFFSET); - tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs)); - tmp = (tmp & msk) | value; - writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs)); -} - -/* These defaults were characterized by H/W group */ -#define FMIN_VAL_DEFAULT 0x3df -#define FMAX_VAL_DEFAULT 0x3df -#define FMAX_VAL_SSC 0x83 - -static void brcm_sata_cfg_ssc(struct brcm_sata_port *port) -{ - void __iomem *base = brcm_sata_phy_base(port); - struct brcm_sata_phy *priv = port->phy_priv; - u32 tmp; - - /* override the TX spread spectrum setting */ - tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; - brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); - - /* set fixed min freq */ - brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, - ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, - FMIN_VAL_DEFAULT); - - /* set fixed max freq depending on SSC config */ - if (port->ssc_en) { - dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum); - tmp = FMAX_VAL_SSC; - } else { - tmp = FMAX_VAL_DEFAULT; - } - - brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, - ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); -} - -static int brcm_sata_phy_init(struct phy *phy) -{ - struct brcm_sata_port *port = phy_get_drvdata(phy); - - brcm_sata_cfg_ssc(port); - - return 0; -} - -static const struct phy_ops phy_ops = { - .init = brcm_sata_phy_init, - .owner = THIS_MODULE, -}; - -static const struct of_device_id brcm_sata_phy_of_match[] = { - { .compatible = "brcm,bcm7445-sata-phy", - .data = (void *)BRCM_SATA_PHY_28NM }, - { .compatible = "brcm,bcm7425-sata-phy", - .data = (void *)BRCM_SATA_PHY_40NM }, - {}, -}; -MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); - -static int brcm_sata_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *dn = dev->of_node, *child; - const struct of_device_id *of_id; - struct brcm_sata_phy *priv; - struct resource *res; - struct phy_provider *provider; - int ret, count = 0; - - if (of_get_child_count(dn) == 0) - return -ENODEV; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - dev_set_drvdata(dev, priv); - priv->dev = dev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - priv->phy_base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->phy_base)) - return PTR_ERR(priv->phy_base); - - of_id = of_match_node(brcm_sata_phy_of_match, dn); - if (of_id) - priv->version = (enum brcm_sata_phy_version)of_id->data; - else - priv->version = BRCM_SATA_PHY_28NM; - - for_each_available_child_of_node(dn, child) { - unsigned int id; - struct brcm_sata_port *port; - - if (of_property_read_u32(child, "reg", &id)) { - dev_err(dev, "missing reg property in node %s\n", - child->name); - ret = -EINVAL; - goto put_child; - } - - if (id >= MAX_PORTS) { - dev_err(dev, "invalid reg: %u\n", id); - ret = -EINVAL; - goto put_child; - } - if (priv->phys[id].phy) { - dev_err(dev, "already registered port %u\n", id); - ret = -EINVAL; - goto put_child; - } - - port = &priv->phys[id]; - port->portnum = id; - port->phy_priv = priv; - port->phy = devm_phy_create(dev, child, &phy_ops); - port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); - if (IS_ERR(port->phy)) { - dev_err(dev, "failed to create PHY\n"); - ret = PTR_ERR(port->phy); - goto put_child; - } - - phy_set_drvdata(port->phy, port); - count++; - } - - provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(provider)) { - dev_err(dev, "could not register PHY provider\n"); - return PTR_ERR(provider); - } - - dev_info(dev, "registered %d port(s)\n", count); - - return 0; -put_child: - of_node_put(child); - return ret; -} - -static struct platform_driver brcm_sata_phy_driver = { - .probe = brcm_sata_phy_probe, - .driver = { - .of_match_table = brcm_sata_phy_of_match, - .name = "brcmstb-sata-phy", - } -}; -module_platform_driver(brcm_sata_phy_driver); - -MODULE_DESCRIPTION("Broadcom STB SATA PHY driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marc Carino"); -MODULE_AUTHOR("Brian Norris"); -MODULE_ALIAS("platform:phy-brcmstb-sata"); diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 2a54caba93b4..cc093ebfda94 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -1,7 +1,7 @@ /* * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -13,96 +13,276 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/mfd/syscon/exynos4-pmu.h> +#include <linux/mfd/syscon/exynos5-pmu.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> -#include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/spinlock.h> #include <linux/mfd/syscon.h> -/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */ -#define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4) - enum exynos_mipi_phy_id { + EXYNOS_MIPI_PHY_ID_NONE = -1, EXYNOS_MIPI_PHY_ID_CSIS0, EXYNOS_MIPI_PHY_ID_DSIM0, EXYNOS_MIPI_PHY_ID_CSIS1, EXYNOS_MIPI_PHY_ID_DSIM1, + EXYNOS_MIPI_PHY_ID_CSIS2, EXYNOS_MIPI_PHYS_NUM }; -#define is_mipi_dsim_phy_id(id) \ - ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1) +enum exynos_mipi_phy_regmap_id { + EXYNOS_MIPI_REGMAP_PMU, + EXYNOS_MIPI_REGMAP_DISP, + EXYNOS_MIPI_REGMAP_CAM0, + EXYNOS_MIPI_REGMAP_CAM1, + EXYNOS_MIPI_REGMAPS_NUM +}; + +struct mipi_phy_device_desc { + int num_phys; + int num_regmaps; + const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; + struct exynos_mipi_phy_desc { + enum exynos_mipi_phy_id coupled_phy_id; + u32 enable_val; + unsigned int enable_reg; + enum exynos_mipi_phy_regmap_id enable_map; + u32 resetn_val; + unsigned int resetn_reg; + enum exynos_mipi_phy_regmap_id resetn_map; + } phys[EXYNOS_MIPI_PHYS_NUM]; +}; + +static const struct mipi_phy_device_desc s5pv210_mipi_phy = { + .num_regmaps = 1, + .regmap_names = {"syscon"}, + .num_phys = 4, + .phys = { + { + /* EXYNOS_MIPI_PHY_ID_CSIS0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, + .enable_val = EXYNOS4_MIPI_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, + .enable_val = EXYNOS4_MIPI_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, + .enable_val = EXYNOS4_MIPI_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, + .enable_val = EXYNOS4_MIPI_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, + }, +}; + +static const struct mipi_phy_device_desc exynos5420_mipi_phy = { + .num_regmaps = 1, + .regmap_names = {"syscon"}, + .num_phys = 5, + .phys = { + { + /* EXYNOS_MIPI_PHY_ID_CSIS0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS5_MIPI_PHY_S_RESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL, + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY0_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS5_MIPI_PHY_M_RESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY0_CONTROL, + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS5_MIPI_PHY_S_RESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL, + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY1_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS5_MIPI_PHY_M_RESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY1_CONTROL, + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS2 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY2_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS5_MIPI_PHY_S_RESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY2_CONTROL, + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, + }, +}; + +#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C +#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 +#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 + +static const struct mipi_phy_device_desc exynos5433_mipi_phy = { + .num_regmaps = 4, + .regmap_names = { + "samsung,pmu-syscon", + "samsung,disp-sysreg", + "samsung,cam0-sysreg", + "samsung,cam1-sysreg" + }, + .num_phys = 5, + .phys = { + { + /* EXYNOS_MIPI_PHY_ID_CSIS0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(0), + .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, + .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5433_MIPI_PHY0_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(0), + .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, + .resetn_map = EXYNOS_MIPI_REGMAP_DISP, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(1), + .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, + .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5433_MIPI_PHY1_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(1), + .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, + .resetn_map = EXYNOS_MIPI_REGMAP_DISP, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS2 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS5_PHY_ENABLE, + .enable_reg = EXYNOS5433_MIPI_PHY2_CONTROL, + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(0), + .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, + .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, + }, + }, +}; struct exynos_mipi_video_phy { + struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; + int num_phys; struct video_phy_desc { struct phy *phy; unsigned int index; + const struct exynos_mipi_phy_desc *data; } phys[EXYNOS_MIPI_PHYS_NUM]; spinlock_t slock; - void __iomem *regs; - struct regmap *regmap; }; -static int __set_phy_state(struct exynos_mipi_video_phy *state, - enum exynos_mipi_phy_id id, unsigned int on) +static inline int __is_running(const struct exynos_mipi_phy_desc *data, + struct exynos_mipi_video_phy *state) { - const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2); - void __iomem *addr; - u32 val, reset; + u32 val; - if (is_mipi_dsim_phy_id(id)) - reset = EXYNOS4_MIPI_PHY_MRESETN; - else - reset = EXYNOS4_MIPI_PHY_SRESETN; + regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); + return val & data->resetn_val; +} + +static int __set_phy_state(const struct exynos_mipi_phy_desc *data, + struct exynos_mipi_video_phy *state, unsigned int on) +{ + u32 val; spin_lock(&state->slock); - if (!IS_ERR(state->regmap)) { - regmap_read(state->regmap, offset, &val); - if (on) - val |= reset; - else - val &= ~reset; - regmap_write(state->regmap, offset, val); - if (on) - val |= EXYNOS4_MIPI_PHY_ENABLE; - else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) - val &= ~EXYNOS4_MIPI_PHY_ENABLE; - regmap_write(state->regmap, offset, val); - } else { - addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2); - - val = readl(addr); - if (on) - val |= reset; - else - val &= ~reset; - writel(val, addr); - /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */ - if (on) - val |= EXYNOS4_MIPI_PHY_ENABLE; - else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK)) - val &= ~EXYNOS4_MIPI_PHY_ENABLE; - - writel(val, addr); + /* disable in PMU sysreg */ + if (!on && data->coupled_phy_id >= 0 && + !__is_running(state->phys[data->coupled_phy_id].data, state)) { + regmap_read(state->regmaps[data->enable_map], data->enable_reg, + &val); + val &= ~data->enable_val; + regmap_write(state->regmaps[data->enable_map], data->enable_reg, + val); + } + + /* PHY reset */ + regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); + val = on ? (val | data->resetn_val) : (val & ~data->resetn_val); + regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val); + + /* enable in PMU sysreg */ + if (on) { + regmap_read(state->regmaps[data->enable_map], data->enable_reg, + &val); + val |= data->enable_val; + regmap_write(state->regmaps[data->enable_map], data->enable_reg, + val); } spin_unlock(&state->slock); + return 0; } #define to_mipi_video_phy(desc) \ - container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]); + container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) static int exynos_mipi_video_phy_power_on(struct phy *phy) { struct video_phy_desc *phy_desc = phy_get_drvdata(phy); struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); - return __set_phy_state(state, phy_desc->index, 1); + return __set_phy_state(phy_desc->data, state, 1); } static int exynos_mipi_video_phy_power_off(struct phy *phy) @@ -110,7 +290,7 @@ static int exynos_mipi_video_phy_power_off(struct phy *phy) struct video_phy_desc *phy_desc = phy_get_drvdata(phy); struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); - return __set_phy_state(state, phy_desc->index, 0); + return __set_phy_state(phy_desc->data, state, 0); } static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, @@ -118,7 +298,7 @@ static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, { struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); - if (WARN_ON(args->args[0] >= EXYNOS_MIPI_PHYS_NUM)) + if (WARN_ON(args->args[0] >= state->num_phys)) return ERR_PTR(-ENODEV); return state->phys[args->args[0]].phy; @@ -132,32 +312,33 @@ static const struct phy_ops exynos_mipi_video_phy_ops = { static int exynos_mipi_video_phy_probe(struct platform_device *pdev) { + const struct mipi_phy_device_desc *phy_dev; struct exynos_mipi_video_phy *state; struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct phy_provider *phy_provider; unsigned int i; + phy_dev = of_device_get_match_data(dev); + if (!phy_dev) + return -ENODEV; + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; - state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); - if (IS_ERR(state->regmap)) { - struct resource *res; - - dev_info(dev, "regmap lookup failed: %ld\n", - PTR_ERR(state->regmap)); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - state->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(state->regs)) - return PTR_ERR(state->regs); + for (i = 0; i < phy_dev->num_regmaps; i++) { + state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, + phy_dev->regmap_names[i]); + if (IS_ERR(state->regmaps[i])) + return PTR_ERR(state->regmaps[i]); } + state->num_phys = phy_dev->num_phys; + spin_lock_init(&state->slock); dev_set_drvdata(dev, state); - spin_lock_init(&state->slock); - for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { + for (i = 0; i < state->num_phys; i++) { struct phy *phy = devm_phy_create(dev, NULL, &exynos_mipi_video_phy_ops); if (IS_ERR(phy)) { @@ -167,6 +348,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) state->phys[i].phy = phy; state->phys[i].index = i; + state->phys[i].data = &phy_dev->phys[i]; phy_set_drvdata(phy, &state->phys[i]); } @@ -177,8 +359,17 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) } static const struct of_device_id exynos_mipi_video_phy_of_match[] = { - { .compatible = "samsung,s5pv210-mipi-video-phy" }, - { }, + { + .compatible = "samsung,s5pv210-mipi-video-phy", + .data = &s5pv210_mipi_phy, + }, { + .compatible = "samsung,exynos5420-mipi-video-phy", + .data = &exynos5420_mipi_phy, + }, { + .compatible = "samsung,exynos5433-mipi-video-phy", + .data = &exynos5433_mipi_phy, + }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); diff --git a/drivers/phy/phy-mt65xx-usb3.c b/drivers/phy/phy-mt65xx-usb3.c index c0e7b4b0cf5c..4d85e730ccab 100644 --- a/drivers/phy/phy-mt65xx-usb3.c +++ b/drivers/phy/phy-mt65xx-usb3.c @@ -134,6 +134,11 @@ #define U3P_SR_COEF_DIVISOR 1000 #define U3P_FM_DET_CYCLE_CNT 1024 +struct mt65xx_phy_pdata { + /* avoid RX sensitivity level degradation only for mt8173 */ + bool avoid_rx_sen_degradation; +}; + struct mt65xx_phy_instance { struct phy *phy; void __iomem *port_base; @@ -145,6 +150,7 @@ struct mt65xx_u3phy { struct device *dev; void __iomem *sif_base; /* include sif2, but exclude port's */ struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */ + const struct mt65xx_phy_pdata *pdata; struct mt65xx_phy_instance **phys; int nphys; }; @@ -241,22 +247,26 @@ static void phy_instance_init(struct mt65xx_u3phy *u3phy, tmp = readl(port_base + U3P_U2PHYACR4); tmp &= ~P2C_U2_GPIO_CTR_MSK; writel(tmp, port_base + U3P_U2PHYACR4); + } - tmp = readl(port_base + U3P_USBPHYACR2); - tmp |= PA2_RG_SIF_U2PLL_FORCE_EN; - writel(tmp, port_base + U3P_USBPHYACR2); - - tmp = readl(port_base + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, port_base + U3D_U2PHYDCR0); - } else { - tmp = readl(port_base + U3D_U2PHYDCR0); - tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, port_base + U3D_U2PHYDCR0); - - tmp = readl(port_base + U3P_U2PHYDTM0); - tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; - writel(tmp, port_base + U3P_U2PHYDTM0); + if (u3phy->pdata->avoid_rx_sen_degradation) { + if (!index) { + tmp = readl(port_base + U3P_USBPHYACR2); + tmp |= PA2_RG_SIF_U2PLL_FORCE_EN; + writel(tmp, port_base + U3P_USBPHYACR2); + + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + } else { + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; + writel(tmp, port_base + U3P_U2PHYDTM0); + } } tmp = readl(port_base + U3P_USBPHYACR6); @@ -318,7 +328,7 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy, tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD; writel(tmp, u3phy->sif_base + U3P_XTALCTL3); - /* [mt8173]switch 100uA current to SSUSB */ + /* switch 100uA current to SSUSB */ tmp = readl(port_base + U3P_USBPHYACR5); tmp |= PA5_RG_U2_HS_100U_U3_EN; writel(tmp, port_base + U3P_USBPHYACR5); @@ -335,7 +345,7 @@ static void phy_instance_power_on(struct mt65xx_u3phy *u3phy, tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4); writel(tmp, port_base + U3P_USBPHYACR5); - if (index) { + if (u3phy->pdata->avoid_rx_sen_degradation && index) { tmp = readl(port_base + U3D_U2PHYDCR0); tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; writel(tmp, port_base + U3D_U2PHYDCR0); @@ -386,7 +396,9 @@ static void phy_instance_power_off(struct mt65xx_u3phy *u3phy, tmp = readl(port_base + U3P_U3_PHYA_REG0); tmp &= ~P3A_RG_U3_VUSB10_ON; writel(tmp, port_base + U3P_U3_PHYA_REG0); - } else { + } + + if (u3phy->pdata->avoid_rx_sen_degradation && index) { tmp = readl(port_base + U3D_U2PHYDCR0); tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; writel(tmp, port_base + U3D_U2PHYDCR0); @@ -402,7 +414,7 @@ static void phy_instance_exit(struct mt65xx_u3phy *u3phy, u32 index = instance->index; u32 tmp; - if (index) { + if (u3phy->pdata->avoid_rx_sen_degradation && index) { tmp = readl(port_base + U3D_U2PHYDCR0); tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; writel(tmp, port_base + U3D_U2PHYDCR0); @@ -502,8 +514,24 @@ static struct phy_ops mt65xx_u3phy_ops = { .owner = THIS_MODULE, }; +static const struct mt65xx_phy_pdata mt2701_pdata = { + .avoid_rx_sen_degradation = false, +}; + +static const struct mt65xx_phy_pdata mt8173_pdata = { + .avoid_rx_sen_degradation = true, +}; + +static const struct of_device_id mt65xx_u3phy_id_table[] = { + { .compatible = "mediatek,mt2701-u3phy", .data = &mt2701_pdata }, + { .compatible = "mediatek,mt8173-u3phy", .data = &mt8173_pdata }, + { }, +}; +MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table); + static int mt65xx_u3phy_probe(struct platform_device *pdev) { + const struct of_device_id *match; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct device_node *child_np; @@ -513,10 +541,15 @@ static int mt65xx_u3phy_probe(struct platform_device *pdev) struct resource res; int port, retval; + match = of_match_node(mt65xx_u3phy_id_table, pdev->dev.of_node); + if (!match) + return -EINVAL; + u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL); if (!u3phy) return -ENOMEM; + u3phy->pdata = match->data; u3phy->nphys = of_get_child_count(np); u3phy->phys = devm_kcalloc(dev, u3phy->nphys, sizeof(*u3phy->phys), GFP_KERNEL); @@ -587,12 +620,6 @@ put_child: return retval; } -static const struct of_device_id mt65xx_u3phy_id_table[] = { - { .compatible = "mediatek,mt8173-u3phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table); - static struct platform_driver mt65xx_u3phy_driver = { .probe = mt65xx_u3phy_probe, .driver = { diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index c7a05996d5c1..97d4dd6ea924 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -195,6 +195,7 @@ static const struct of_device_id rcar_gen2_phy_match_table[] = { { .compatible = "renesas,usb-phy-r8a7790" }, { .compatible = "renesas,usb-phy-r8a7791" }, { .compatible = "renesas,usb-phy-r8a7794" }, + { .compatible = "renesas,rcar-gen2-usb-phy" }, { } }; MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c index bc4f7dd821aa..76bb88f0700a 100644 --- a/drivers/phy/phy-rcar-gen3-usb2.c +++ b/drivers/phy/phy-rcar-gen3-usb2.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include <linux/extcon.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -19,6 +20,7 @@ #include <linux/of_address.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> /******* USB2.0 Host registers (original offset is +0x200) *******/ #define USB2_INT_ENABLE 0x000 @@ -74,20 +76,17 @@ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) -struct rcar_gen3_data { - void __iomem *base; - struct clk *clk; -}; - struct rcar_gen3_chan { - struct rcar_gen3_data usb2; + void __iomem *base; + struct extcon_dev *extcon; struct phy *phy; + struct regulator *vbus; bool has_otg; }; static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) { - void __iomem *usb2_base = ch->usb2.base; + void __iomem *usb2_base = ch->base; u32 val = readl(usb2_base + USB2_COMMCTRL); dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host); @@ -100,7 +99,7 @@ static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) { - void __iomem *usb2_base = ch->usb2.base; + void __iomem *usb2_base = ch->base; u32 val = readl(usb2_base + USB2_LINECTRL1); dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); @@ -114,7 +113,7 @@ static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) { - void __iomem *usb2_base = ch->usb2.base; + void __iomem *usb2_base = ch->base; u32 val = readl(usb2_base + USB2_ADPCTRL); dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus); @@ -130,6 +129,9 @@ static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) rcar_gen3_set_linectrl(ch, 1, 1); rcar_gen3_set_host_mode(ch, 1); rcar_gen3_enable_vbus_ctrl(ch, 1); + + extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true); + extcon_set_cable_state_(ch->extcon, EXTCON_USB, false); } static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) @@ -137,17 +139,20 @@ static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) rcar_gen3_set_linectrl(ch, 0, 1); rcar_gen3_set_host_mode(ch, 0); rcar_gen3_enable_vbus_ctrl(ch, 0); + + extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false); + extcon_set_cable_state_(ch->extcon, EXTCON_USB, true); } static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch) { - return !!(readl(ch->usb2.base + USB2_ADPCTRL) & + return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_OTGSESSVLD); } static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) { - return !!(readl(ch->usb2.base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); + return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); } static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) @@ -166,7 +171,7 @@ static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) { - void __iomem *usb2_base = ch->usb2.base; + void __iomem *usb2_base = ch->base; u32 val; val = readl(usb2_base + USB2_VBCTRL); @@ -187,7 +192,7 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) static int rcar_gen3_phy_usb2_init(struct phy *p) { struct rcar_gen3_chan *channel = phy_get_drvdata(p); - void __iomem *usb2_base = channel->usb2.base; + void __iomem *usb2_base = channel->base; /* Initialize USB2 part */ writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); @@ -205,7 +210,7 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p) { struct rcar_gen3_chan *channel = phy_get_drvdata(p); - writel(0, channel->usb2.base + USB2_INT_ENABLE); + writel(0, channel->base + USB2_INT_ENABLE); return 0; } @@ -213,8 +218,15 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p) static int rcar_gen3_phy_usb2_power_on(struct phy *p) { struct rcar_gen3_chan *channel = phy_get_drvdata(p); - void __iomem *usb2_base = channel->usb2.base; + void __iomem *usb2_base = channel->base; u32 val; + int ret; + + if (channel->vbus) { + ret = regulator_enable(channel->vbus); + if (ret) + return ret; + } val = readl(usb2_base + USB2_USBCTR); val |= USB2_USBCTR_PLL_RST; @@ -225,17 +237,29 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p) return 0; } +static int rcar_gen3_phy_usb2_power_off(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + int ret = 0; + + if (channel->vbus) + ret = regulator_disable(channel->vbus); + + return ret; +} + static struct phy_ops rcar_gen3_phy_usb2_ops = { .init = rcar_gen3_phy_usb2_init, .exit = rcar_gen3_phy_usb2_exit, .power_on = rcar_gen3_phy_usb2_power_on, + .power_off = rcar_gen3_phy_usb2_power_off, .owner = THIS_MODULE, }; static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) { struct rcar_gen3_chan *ch = _ch; - void __iomem *usb2_base = ch->usb2.base; + void __iomem *usb2_base = ch->base; u32 status = readl(usb2_base + USB2_OBINTSTA); irqreturn_t ret = IRQ_NONE; @@ -251,10 +275,17 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { { .compatible = "renesas,usb2-phy-r8a7795" }, + { .compatible = "renesas,rcar-gen3-usb2-phy" }, { } }; MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); +static const unsigned int rcar_gen3_phy_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -273,18 +304,30 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - channel->usb2.base = devm_ioremap_resource(dev, res); - if (IS_ERR(channel->usb2.base)) - return PTR_ERR(channel->usb2.base); + channel->base = devm_ioremap_resource(dev, res); + if (IS_ERR(channel->base)) + return PTR_ERR(channel->base); /* call request_irq for OTG */ irq = platform_get_irq(pdev, 0); if (irq >= 0) { + int ret; + irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, IRQF_SHARED, dev_name(dev), channel); if (irq < 0) dev_err(dev, "No irq handler (%d)\n", irq); channel->has_otg = true; + channel->extcon = devm_extcon_dev_allocate(dev, + rcar_gen3_phy_cable); + if (IS_ERR(channel->extcon)) + return PTR_ERR(channel->extcon); + + ret = devm_extcon_dev_register(dev, channel->extcon); + if (ret < 0) { + dev_err(dev, "Failed to register extcon\n"); + return ret; + } } /* devm_phy_create() will call pm_runtime_enable(dev); */ @@ -294,6 +337,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) return PTR_ERR(channel->phy); } + channel->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(channel->vbus)) { + if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) + return PTR_ERR(channel->vbus); + channel->vbus = NULL; + } + phy_set_drvdata(channel->phy, channel); provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index f62d899063a3..d60b149cff0f 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -216,7 +216,7 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, init.parent_names = &clk_name; init.num_parents = 1; } else { - init.flags = CLK_IS_ROOT; + init.flags = 0; init.parent_names = NULL; init.num_parents = 0; } |