diff options
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/Kconfig | 24 | ||||
-rw-r--r-- | drivers/phy/Makefile | 3 | ||||
-rw-r--r-- | drivers/phy/phy-bcm-cygnus-pcie.c | 2 | ||||
-rw-r--r-- | drivers/phy/phy-bcm-nsp-usb3.c | 177 | ||||
-rw-r--r-- | drivers/phy/phy-hi6220-usb.c | 2 | ||||
-rw-r--r-- | drivers/phy/phy-mt65xx-usb3.c | 2 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ufs-i.h | 1 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-14nm.c | 15 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ufs-qmp-20nm.c | 12 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-ufs.c | 37 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-usb-hs.c | 296 | ||||
-rw-r--r-- | drivers/phy/phy-qcom-usb-hsic.c | 160 | ||||
-rw-r--r-- | drivers/phy/phy-rcar-gen3-usb2.c | 10 | ||||
-rw-r--r-- | drivers/phy/phy-rockchip-inno-usb2.c | 7 | ||||
-rw-r--r-- | drivers/phy/phy-sun4i-usb.c | 18 |
15 files changed, 704 insertions, 62 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index bbad035c60cc..dc5277ad1b5a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -371,6 +371,7 @@ config PHY_ROCKCHIP_INNO_USB2 tristate "Rockchip INNO USB2PHY Driver" depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF depends on COMMON_CLK + depends on EXTCON depends on USB_SUPPORT select GENERIC_PHY select USB_COMMON @@ -445,6 +446,21 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_QCOM_USB_HS + tristate "Qualcomm USB HS PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for the USB high-speed ULPI compliant phy on Qualcomm + chipsets. + +config PHY_QCOM_USB_HSIC + tristate "Qualcomm USB HSIC ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. + config PHY_TUSB1210 tristate "TI TUSB1210 ULPI PHY module" depends on USB_ULPI_BUS @@ -494,4 +510,12 @@ config PHY_MESON8B_USB2 and GXBB SoCs. If unsure, say N. +config PHY_NSP_USB3 + tristate "Broadcom NorthStar plus USB3 PHY driver" + depends on OF && (ARCH_BCM_NSP || COMPILE_TEST) + select GENERIC_PHY + default ARCH_BCM_NSP + help + Enable this to support the Broadcom Northstar plus USB3 PHY. + If unsure, say N. endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 081aeb4efd13..e7b0feb1e125 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -53,6 +53,8 @@ obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o 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_QCOM_USB_HS) += phy-qcom-usb-hs.o +obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o 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 @@ -60,3 +62,4 @@ 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 obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o +obj-$(CONFIG_PHY_NSP_USB3) += phy-bcm-nsp-usb3.o diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c index 082c03f6438f..0f4ac5d63cff 100644 --- a/drivers/phy/phy-bcm-cygnus-pcie.c +++ b/drivers/phy/phy-bcm-cygnus-pcie.c @@ -114,7 +114,7 @@ static int cygnus_pcie_phy_power_off(struct phy *p) return cygnus_pcie_power_config(phy, false); } -static struct phy_ops cygnus_pcie_phy_ops = { +static const struct phy_ops cygnus_pcie_phy_ops = { .power_on = cygnus_pcie_phy_power_on, .power_off = cygnus_pcie_phy_power_off, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-bcm-nsp-usb3.c b/drivers/phy/phy-bcm-nsp-usb3.c new file mode 100644 index 000000000000..49024eaa5545 --- /dev/null +++ b/drivers/phy/phy-bcm-nsp-usb3.c @@ -0,0 +1,177 @@ +/* + * 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/delay.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> + +#define NSP_USB3_RST_CTRL_OFFSET 0x3f8 + +/* mdio reg access */ +#define NSP_USB3_PHY_BASE_ADDR_REG 0x1f + +#define NSP_USB3_PHY_PLL30_BLOCK 0x8000 +#define NSP_USB3_PLL_CONTROL 0x01 +#define NSP_USB3_PLLA_CONTROL0 0x0a +#define NSP_USB3_PLLA_CONTROL1 0x0b + +#define NSP_USB3_PHY_TX_PMD_BLOCK 0x8040 +#define NSP_USB3_TX_PMD_CONTROL1 0x01 + +#define NSP_USB3_PHY_PIPE_BLOCK 0x8060 +#define NSP_USB3_LFPS_CMP 0x02 +#define NSP_USB3_LFPS_DEGLITCH 0x03 + +struct nsp_usb3_phy { + struct regmap *usb3_ctrl; + struct phy *phy; + struct mdio_device *mdiodev; +}; + +static int nsp_usb3_phy_init(struct phy *phy) +{ + struct nsp_usb3_phy *iphy = phy_get_drvdata(phy); + struct mii_bus *bus = iphy->mdiodev->bus; + int addr = iphy->mdiodev->addr; + u32 data; + int rc; + + rc = regmap_read(iphy->usb3_ctrl, 0, &data); + if (rc) + return rc; + data |= 1; + rc = regmap_write(iphy->usb3_ctrl, 0, data); + if (rc) + return rc; + + rc = regmap_write(iphy->usb3_ctrl, NSP_USB3_RST_CTRL_OFFSET, 1); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PHY_BASE_ADDR_REG, + NSP_USB3_PHY_PLL30_BLOCK); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PLL_CONTROL, 0x1000); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PLLA_CONTROL0, 0x6400); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PLLA_CONTROL1, 0xc000); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PLLA_CONTROL1, 0x8000); + if (rc) + return rc; + + rc = regmap_write(iphy->usb3_ctrl, NSP_USB3_RST_CTRL_OFFSET, 0); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PLL_CONTROL, 0x9000); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PHY_BASE_ADDR_REG, + NSP_USB3_PHY_PIPE_BLOCK); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_LFPS_CMP, 0xf30d); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_LFPS_DEGLITCH, 0x6302); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_PHY_BASE_ADDR_REG, + NSP_USB3_PHY_TX_PMD_BLOCK); + if (rc) + return rc; + + rc = mdiobus_write(bus, addr, NSP_USB3_TX_PMD_CONTROL1, 0x1003); + + return rc; +} + +static struct phy_ops nsp_usb3_phy_ops = { + .init = nsp_usb3_phy_init, + .owner = THIS_MODULE, +}; + +static int nsp_usb3_phy_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct phy_provider *provider; + struct nsp_usb3_phy *iphy; + + iphy = devm_kzalloc(dev, sizeof(*iphy), GFP_KERNEL); + if (!iphy) + return -ENOMEM; + iphy->mdiodev = mdiodev; + + iphy->usb3_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, + "usb3-ctrl-syscon"); + if (IS_ERR(iphy->usb3_ctrl)) + return PTR_ERR(iphy->usb3_ctrl); + + iphy->phy = devm_phy_create(dev, dev->of_node, &nsp_usb3_phy_ops); + if (IS_ERR(iphy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(iphy->phy); + } + + phy_set_drvdata(iphy->phy, iphy); + + 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); + } + + return 0; +} + +static const struct of_device_id nsp_usb3_phy_of_match[] = { + {.compatible = "brcm,nsp-usb3-phy",}, + { /* sentinel */ } +}; + +static struct mdio_driver nsp_usb3_phy_driver = { + .mdiodrv = { + .driver = { + .name = "nsp-usb3-phy", + .of_match_table = nsp_usb3_phy_of_match, + }, + }, + .probe = nsp_usb3_phy_probe, +}; + +mdio_module_driver(nsp_usb3_phy_driver); + +MODULE_DESCRIPTION("Broadcom NSP USB3 PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Yendapally Reddy Dhananjaya Reddy <yendapally.reddy@broadcom.com"); diff --git a/drivers/phy/phy-hi6220-usb.c b/drivers/phy/phy-hi6220-usb.c index b2141cbd4cf6..398c1021deec 100644 --- a/drivers/phy/phy-hi6220-usb.c +++ b/drivers/phy/phy-hi6220-usb.c @@ -112,7 +112,7 @@ static int hi6220_phy_exit(struct phy *phy) return hi6220_phy_setup(priv, false); } -static struct phy_ops hi6220_phy_ops = { +static const struct phy_ops hi6220_phy_ops = { .init = hi6220_phy_start, .exit = hi6220_phy_exit, .owner = THIS_MODULE, diff --git a/drivers/phy/phy-mt65xx-usb3.c b/drivers/phy/phy-mt65xx-usb3.c index 4d85e730ccab..d9720675b9db 100644 --- a/drivers/phy/phy-mt65xx-usb3.c +++ b/drivers/phy/phy-mt65xx-usb3.c @@ -506,7 +506,7 @@ static struct phy *mt65xx_phy_xlate(struct device *dev, return instance->phy; } -static struct phy_ops mt65xx_u3phy_ops = { +static const struct phy_ops mt65xx_u3phy_ops = { .init = mt65xx_phy_init, .exit = mt65xx_phy_exit, .power_on = mt65xx_phy_power_on, diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h index d505d98cf5f8..13b02b7de30b 100644 --- a/drivers/phy/phy-qcom-ufs-i.h +++ b/drivers/phy/phy-qcom-ufs-i.h @@ -77,7 +77,6 @@ struct ufs_qcom_phy_vreg { int min_uV; int max_uV; bool enabled; - bool is_always_on; }; struct ufs_qcom_phy { diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c index c71c84734916..12a1b498dc4b 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c @@ -132,27 +132,18 @@ static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); if (!generic_phy) { - dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n", - __func__); err = -EIO; goto out; } err = ufs_qcom_phy_init_clks(phy_common); - if (err) { - dev_err(phy_common->dev, - "%s: ufs_qcom_phy_init_clks() failed %d\n", - __func__, err); + if (err) goto out; - } err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) { - dev_err(phy_common->dev, - "%s: ufs_qcom_phy_init_vregulators() failed %d\n", - __func__, err); + if (err) goto out; - } + phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c index 1a26a64e06d3..4f68acb58b73 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c @@ -190,25 +190,17 @@ static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); if (!generic_phy) { - dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n", - __func__); err = -EIO; goto out; } err = ufs_qcom_phy_init_clks(phy_common); - if (err) { - dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n", - __func__, err); + if (err) goto out; - } err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) { - dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n", - __func__, err); + if (err) goto out; - } ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c index c69568b8543d..43865ef340e2 100644 --- a/drivers/phy/phy-qcom-ufs.c +++ b/drivers/phy/phy-qcom-ufs.c @@ -189,12 +189,12 @@ int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) if (err) goto out; +skip_txrx_clk: err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", &phy_common->ref_clk_src); if (err) goto out; -skip_txrx_clk: /* * "ref_clk_parent" is optional hence don't abort init if it's not * found. @@ -210,25 +210,19 @@ out: } EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); -static int __ufs_qcom_phy_init_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional) +static int ufs_qcom_phy_init_vreg(struct device *dev, + struct ufs_qcom_phy_vreg *vreg, + const char *name) { int err = 0; char prop_name[MAX_PROP_NAME]; - vreg->name = devm_kstrdup(dev, name, GFP_KERNEL); - if (!vreg->name) { - err = -ENOMEM; - goto out; - } - + vreg->name = name; vreg->reg = devm_regulator_get(dev, name); if (IS_ERR(vreg->reg)) { err = PTR_ERR(vreg->reg); - vreg->reg = NULL; - if (!optional) - dev_err(dev, "failed to get %s, %d\n", name, err); + dev_err(dev, "failed to get %s, %d\n", name, err); goto out; } @@ -248,9 +242,6 @@ static int __ufs_qcom_phy_init_vreg(struct device *dev, } err = 0; } - snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name); - vreg->is_always_on = of_property_read_bool(dev->of_node, - prop_name); } if (!strcmp(name, "vdda-pll")) { @@ -265,17 +256,9 @@ static int __ufs_qcom_phy_init_vreg(struct device *dev, } out: - if (err) - kfree(vreg->name); return err; } -static int ufs_qcom_phy_init_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, const char *name) -{ - return __ufs_qcom_phy_init_vreg(dev, vreg, name, false); -} - int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) { int err; @@ -291,9 +274,9 @@ int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) if (err) goto out; - /* vddp-ref-clk-* properties are optional */ - __ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, - "vddp-ref-clk", true); + err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, + "vddp-ref-clk"); + out: return err; } @@ -416,7 +399,7 @@ static int ufs_qcom_phy_disable_vreg(struct device *dev, { int ret = 0; - if (!vreg || !vreg->enabled || vreg->is_always_on) + if (!vreg || !vreg->enabled) goto out; ret = regulator_disable(vreg->reg); diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c new file mode 100644 index 000000000000..94dfbfd739c3 --- /dev/null +++ b/drivers/phy/phy-qcom-usb-hs.c @@ -0,0 +1,296 @@ +/** + * Copyright (C) 2016 Linaro Ltd + * + * 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/module.h> +#include <linux/ulpi/driver.h> +#include <linux/ulpi/regs.h> +#include <linux/clk.h> +#include <linux/regulator/consumer.h> +#include <linux/of_device.h> +#include <linux/reset.h> +#include <linux/extcon.h> +#include <linux/notifier.h> + +#include "ulpi_phy.h" + +#define ULPI_PWR_CLK_MNG_REG 0x88 +# define ULPI_PWR_OTG_COMP_DISABLE BIT(0) + +#define ULPI_MISC_A 0x96 +# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +# define ULPI_MISC_A_VBUSVLDEXT BIT(0) + + +struct ulpi_seq { + u8 addr; + u8 val; +}; + +struct qcom_usb_hs_phy { + struct ulpi *ulpi; + struct phy *phy; + struct clk *ref_clk; + struct clk *sleep_clk; + struct regulator *v1p8; + struct regulator *v3p3; + struct reset_control *reset; + struct ulpi_seq *init_seq; + struct extcon_dev *vbus_edev; + struct notifier_block vbus_notify; +}; + +static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + u8 addr; + int ret; + + if (!uphy->vbus_edev) { + u8 val = 0; + + switch (mode) { + case PHY_MODE_USB_OTG: + case PHY_MODE_USB_HOST: + val |= ULPI_INT_IDGRD; + case PHY_MODE_USB_DEVICE: + val |= ULPI_INT_SESS_VALID; + default: + break; + } + + ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val); + if (ret) + return ret; + ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val); + } else { + switch (mode) { + case PHY_MODE_USB_OTG: + case PHY_MODE_USB_DEVICE: + addr = ULPI_SET(ULPI_MISC_A); + break; + case PHY_MODE_USB_HOST: + addr = ULPI_CLR(ULPI_MISC_A); + break; + default: + return -EINVAL; + } + + ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG), + ULPI_PWR_OTG_COMP_DISABLE); + if (ret) + return ret; + ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL); + } + + return ret; +} + +static int +qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct qcom_usb_hs_phy *uphy; + u8 addr; + + uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify); + + if (event) + addr = ULPI_SET(ULPI_MISC_A); + else + addr = ULPI_CLR(ULPI_MISC_A); + + return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT); +} + +static int qcom_usb_hs_phy_power_on(struct phy *phy) +{ + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + struct ulpi *ulpi = uphy->ulpi; + const struct ulpi_seq *seq; + int ret, state; + + ret = clk_prepare_enable(uphy->ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(uphy->sleep_clk); + if (ret) + goto err_sleep; + + ret = regulator_set_load(uphy->v1p8, 50000); + if (ret < 0) + goto err_1p8; + + ret = regulator_enable(uphy->v1p8); + if (ret) + goto err_1p8; + + ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000, + 3300000); + if (ret) + goto err_3p3; + + ret = regulator_set_load(uphy->v3p3, 50000); + if (ret < 0) + goto err_3p3; + + ret = regulator_enable(uphy->v3p3); + if (ret) + goto err_3p3; + + for (seq = uphy->init_seq; seq->addr; seq++) { + ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr, + seq->val); + if (ret) + goto err_ulpi; + } + + if (uphy->reset) { + ret = reset_control_reset(uphy->reset); + if (ret) + goto err_ulpi; + } + + if (uphy->vbus_edev) { + state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); + /* setup initial state */ + qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, + uphy->vbus_edev); + ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); + if (ret) + goto err_ulpi; + } + + return 0; +err_ulpi: + regulator_disable(uphy->v3p3); +err_3p3: + regulator_disable(uphy->v1p8); +err_1p8: + clk_disable_unprepare(uphy->sleep_clk); +err_sleep: + clk_disable_unprepare(uphy->ref_clk); + return ret; +} + +static int qcom_usb_hs_phy_power_off(struct phy *phy) +{ + int ret; + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + + if (uphy->vbus_edev) { + ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); + if (ret) + return ret; + } + + regulator_disable(uphy->v3p3); + regulator_disable(uphy->v1p8); + clk_disable_unprepare(uphy->sleep_clk); + clk_disable_unprepare(uphy->ref_clk); + + return 0; +} + +static const struct phy_ops qcom_usb_hs_phy_ops = { + .power_on = qcom_usb_hs_phy_power_on, + .power_off = qcom_usb_hs_phy_power_off, + .set_mode = qcom_usb_hs_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) +{ + struct qcom_usb_hs_phy *uphy; + struct phy_provider *p; + struct clk *clk; + struct regulator *reg; + struct reset_control *reset; + int size; + int ret; + + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + ulpi_set_drvdata(ulpi, uphy); + uphy->ulpi = ulpi; + + size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); + if (size < 0) + size = 0; + uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, + sizeof(*uphy->init_seq), GFP_KERNEL); + if (!uphy->init_seq) + return -ENOMEM; + ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", + (u8 *)uphy->init_seq, size); + if (ret && size) + return ret; + /* NUL terminate */ + uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0; + + uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por"); + if (IS_ERR(reset)) { + if (PTR_ERR(reset) == -EPROBE_DEFER) + return PTR_ERR(reset); + uphy->reset = NULL; + } + + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, + &qcom_usb_hs_phy_ops); + if (IS_ERR(uphy->phy)) + return PTR_ERR(uphy->phy); + + uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0); + if (IS_ERR(uphy->vbus_edev)) { + if (PTR_ERR(uphy->vbus_edev) != -ENODEV) + return PTR_ERR(uphy->vbus_edev); + uphy->vbus_edev = NULL; + } + + uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier; + phy_set_drvdata(uphy->phy, uphy); + + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(p); +} + +static const struct of_device_id qcom_usb_hs_phy_match[] = { + { .compatible = "qcom,usb-hs-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match); + +static struct ulpi_driver qcom_usb_hs_phy_driver = { + .probe = qcom_usb_hs_phy_probe, + .driver = { + .name = "qcom_usb_hs_phy", + .of_match_table = qcom_usb_hs_phy_match, + }, +}; +module_ulpi_driver(qcom_usb_hs_phy_driver); + +MODULE_DESCRIPTION("Qualcomm USB HS phy"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c new file mode 100644 index 000000000000..47690f9945b9 --- /dev/null +++ b/drivers/phy/phy-qcom-usb-hsic.c @@ -0,0 +1,160 @@ +/** + * Copyright (C) 2016 Linaro Ltd + * + * 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/module.h> +#include <linux/ulpi/driver.h> +#include <linux/ulpi/regs.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/pinctrl-state.h> +#include <linux/delay.h> +#include <linux/clk.h> + +#include "ulpi_phy.h" + +#define ULPI_HSIC_CFG 0x30 +#define ULPI_HSIC_IO_CAL 0x33 + +struct qcom_usb_hsic_phy { + struct ulpi *ulpi; + struct phy *phy; + struct pinctrl *pctl; + struct clk *phy_clk; + struct clk *cal_clk; + struct clk *cal_sleep_clk; +}; + +static int qcom_usb_hsic_phy_power_on(struct phy *phy) +{ + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); + struct ulpi *ulpi = uphy->ulpi; + struct pinctrl_state *pins_default; + int ret; + + ret = clk_prepare_enable(uphy->phy_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(uphy->cal_clk); + if (ret) + goto err_cal; + + ret = clk_prepare_enable(uphy->cal_sleep_clk); + if (ret) + goto err_sleep; + + /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ + ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); + if (ret) + goto err_ulpi; + + /* Enable periodic IO calibration in HSIC_CFG register */ + ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); + if (ret) + goto err_ulpi; + + /* Configure pins for HSIC functionality */ + pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); + if (IS_ERR(pins_default)) + return PTR_ERR(pins_default); + + ret = pinctrl_select_state(uphy->pctl, pins_default); + if (ret) + goto err_ulpi; + + /* Enable HSIC mode in HSIC_CFG register */ + ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); + if (ret) + goto err_ulpi; + + /* Disable auto-resume */ + ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), + ULPI_IFC_CTRL_AUTORESUME); + if (ret) + goto err_ulpi; + + return ret; +err_ulpi: + clk_disable_unprepare(uphy->cal_sleep_clk); +err_sleep: + clk_disable_unprepare(uphy->cal_clk); +err_cal: + clk_disable_unprepare(uphy->phy_clk); + return ret; +} + +static int qcom_usb_hsic_phy_power_off(struct phy *phy) +{ + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); + + clk_disable_unprepare(uphy->cal_sleep_clk); + clk_disable_unprepare(uphy->cal_clk); + clk_disable_unprepare(uphy->phy_clk); + + return 0; +} + +static const struct phy_ops qcom_usb_hsic_phy_ops = { + .power_on = qcom_usb_hsic_phy_power_on, + .power_off = qcom_usb_hsic_phy_power_off, + .owner = THIS_MODULE, +}; + +static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) +{ + struct qcom_usb_hsic_phy *uphy; + struct phy_provider *p; + struct clk *clk; + + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + ulpi_set_drvdata(ulpi, uphy); + + uphy->ulpi = ulpi; + uphy->pctl = devm_pinctrl_get(&ulpi->dev); + if (IS_ERR(uphy->pctl)) + return PTR_ERR(uphy->pctl); + + uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, + &qcom_usb_hsic_phy_ops); + if (IS_ERR(uphy->phy)) + return PTR_ERR(uphy->phy); + phy_set_drvdata(uphy->phy, uphy); + + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(p); +} + +static const struct of_device_id qcom_usb_hsic_phy_match[] = { + { .compatible = "qcom,usb-hsic-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); + +static struct ulpi_driver qcom_usb_hsic_phy_driver = { + .probe = qcom_usb_hsic_phy_probe, + .driver = { + .name = "qcom_usb_hsic_phy", + .of_match_table = qcom_usb_hsic_phy_match, + }, +}; +module_ulpi_driver(qcom_usb_hsic_phy_driver); + +MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c index c63da1b955c1..afb4d048d3e9 100644 --- a/drivers/phy/phy-rcar-gen3-usb2.c +++ b/drivers/phy/phy-rcar-gen3-usb2.c @@ -94,11 +94,11 @@ static void rcar_gen3_phy_usb2_work(struct work_struct *work) work); if (ch->extcon_host) { - extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, true); - extcon_set_cable_state_(ch->extcon, EXTCON_USB, false); + extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); + extcon_set_state_sync(ch->extcon, EXTCON_USB, false); } else { - extcon_set_cable_state_(ch->extcon, EXTCON_USB_HOST, false); - extcon_set_cable_state_(ch->extcon, EXTCON_USB, true); + extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); + extcon_set_state_sync(ch->extcon, EXTCON_USB, true); } } @@ -350,7 +350,7 @@ static int rcar_gen3_phy_usb2_power_off(struct phy *p) return ret; } -static struct phy_ops rcar_gen3_phy_usb2_ops = { +static const 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, diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c index 2f99ec95079c..4ea95c28a66f 100644 --- a/drivers/phy/phy-rockchip-inno-usb2.c +++ b/drivers/phy/phy-rockchip-inno-usb2.c @@ -595,9 +595,14 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) if (rport->vbus_attached != vbus_attach) { rport->vbus_attached = vbus_attach; - if (notify_charger && rphy->edev) + if (notify_charger && rphy->edev) { extcon_set_cable_state_(rphy->edev, cable, vbus_attach); + if (cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(rphy->edev, + EXTCON_USB, + vbus_attach); + } } break; case OTG_STATE_B_PERIPHERAL: diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index bf28a0fdd569..a21b5f24a340 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -99,6 +99,7 @@ enum sun4i_usb_phy_type { sun6i_a31_phy, sun8i_a33_phy, sun8i_h3_phy, + sun8i_v3s_phy, sun50i_a64_phy, }; @@ -188,7 +189,8 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, spin_lock_irqsave(&phy_data->reg_lock, flags); if (phy_data->cfg->type == sun8i_a33_phy || - phy_data->cfg->type == sun50i_a64_phy) { + phy_data->cfg->type == sun50i_a64_phy || + phy_data->cfg->type == sun8i_v3s_phy) { /* A33 or A64 needs us to set phyctl to 0 explicitly */ writel(0, phyctl); } @@ -534,7 +536,7 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) mutex_unlock(&phy0->mutex); if (id_notify) { - extcon_set_cable_state_(data->extcon, EXTCON_USB_HOST, + extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, !id_det); /* When leaving host mode force end the session here */ if (force_session_end && id_det == 1) { @@ -547,7 +549,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); + extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); if (sun4i_usb_phy0_poll(data)) queue_delayed_work(system_wq, &data->detect, POLL_TIME); @@ -825,6 +827,15 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { .enable_pmu_unk1 = true, }; +static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { + .num_phys = 1, + .type = sun8i_v3s_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .enable_pmu_unk1 = true, +}; + static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { .num_phys = 2, .type = sun50i_a64_phy, @@ -842,6 +853,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = { { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, + { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, { .compatible = "allwinner,sun50i-a64-usb-phy", .data = &sun50i_a64_cfg}, { }, |