--- /dev/null
+Broadcom USB3 phy binding for northstar plus SoC
+The USB3 phy is internal to the SoC and is accessed using mdio interface.
+
+Required mdio bus properties:
+- reg: Should be 0x0 for SoC internal USB3 phy
+- #address-cells: must be 1
+- #size-cells: must be 0
+
+Required USB3 PHY properties:
+- compatible: should be "brcm,nsp-usb3-phy"
+- reg: USB3 Phy address on SoC internal MDIO bus and it should be 0x10.
+- usb3-ctrl-syscon: handler of syscon node defining physical address
+ of usb3 control register.
+- #phy-cells: must be 0
+
+Required usb3 control properties:
+- compatible: should be "brcm,nsp-usb3-ctrl"
+- reg: offset and length of the control registers
+
+Example:
+
+ mdio@0 {
+ reg = <0x0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ usb3_phy: usb-phy@10 {
+ compatible = "brcm,nsp-usb3-phy";
+ reg = <0x10>;
+ usb3-ctrl-syscon = <&usb3_ctrl>;
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+ };
+
+ usb3_ctrl: syscon@104408 {
+ compatible = "brcm,nsp-usb3-ctrl", "syscon";
+ reg = <0x104408 0x3fc>;
+ };
--- /dev/null
+Qualcomm's USB HS PHY
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,usb-hs-phy" and more specifically one of the
+ following:
+
+ "qcom,usb-hs-phy-apq8064"
+ "qcom,usb-hs-phy-msm8916"
+ "qcom,usb-hs-phy-msm8974"
+
+- #phy-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Should contain 0
+
+- clocks:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain clock specifier for the reference and sleep
+ clocks
+
+- clock-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "ref" and "sleep" for the reference and sleep
+ clocks respectively
+
+- resets:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain the phy and POR resets
+
+- reset-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "phy" and "por" for the phy and POR resets
+ respectively
+
+- v3p3-supply:
+ Usage: required
+ Value type: <phandle>
+ Definition: Should contain a reference to the 3.3V supply
+
+- v1p8-supply:
+ Usage: required
+ Value type: <phandle>
+ Definition: Should contain a reference to the 1.8V supply
+
+- extcon:
+ Usage: optional
+ Value type: <prop-encoded-array>
+ Definition: Should contain the vbus extcon
+
+- qcom,init-seq:
+ Usage: optional
+ Value type: <u8 array>
+ Definition: Should contain a sequence of ULPI address and value pairs to
+ program into the ULPI_EXT_VENDOR_SPECIFIC area. This is related
+ to Device Mode Eye Diagram test. The addresses are offsets
+ from the ULPI_EXT_VENDOR_SPECIFIC address, for example,
+ <0x1 0x53> would mean "write the value 0x53 to address 0x81".
+
+EXAMPLE
+
+otg: usb-controller {
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hs-phy-msm8974", "qcom,usb-hs-phy";
+ #phy-cells = <0>;
+ clocks = <&xo_board>, <&gcc GCC_USB2A_PHY_SLEEP_CLK>;
+ clock-names = "ref", "sleep";
+ resets = <&gcc GCC_USB2A_PHY_BCR>, <&otg 0>;
+ reset-names = "phy", "por";
+ v3p3-supply = <&pm8941_l24>;
+ v1p8-supply = <&pm8941_l6>;
+ extcon = <&smbb>;
+ qcom,init-seq = /bits/ 8 <0x1 0x63>;
+ };
+ };
+};
--- /dev/null
+Qualcomm's USB HSIC PHY
+
+PROPERTIES
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Should contain "qcom,usb-hsic-phy" and more specifically one of the
+ following:
+
+ "qcom,usb-hsic-phy-mdm9615"
+ "qcom,usb-hsic-phy-msm8974"
+
+- #phy-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: Should contain 0
+
+- clocks:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Should contain clock specifier for phy, calibration and
+ a calibration sleep clock
+
+- clock-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "phy, "cal" and "cal_sleep"
+
+- pinctrl-names:
+ Usage: required
+ Value type: <stringlist>
+ Definition: Should contain "init" and "default" in that order
+
+- pinctrl-0:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of pinctrl settings to apply to keep HSIC pins in a glitch
+ free state
+
+- pinctrl-1:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: List of pinctrl settings to apply to mux out the HSIC pins
+
+EXAMPLE
+
+usb-controller {
+ ulpi {
+ phy {
+ compatible = "qcom,usb-hsic-phy-msm8974",
+ "qcom,usb-hsic-phy";
+ #phy-cells = <0>;
+ pinctrl-names = "init", "default";
+ pinctrl-0 = <&hsic_sleep>;
+ pinctrl-1 = <&hsic_default>;
+ clocks = <&gcc GCC_USB_HSIC_CLK>,
+ <&gcc GCC_USB_HSIC_IO_CAL_CLK>,
+ <&gcc GCC_USB_HSIC_IO_CAL_SLEEP_CLK>;
+ clock-names = "phy", "cal", "cal_sleep";
+ assigned-clocks = <&gcc GCC_USB_HSIC_IO_CAL_CLK>;
+ assigned-clock-rates = <960000>;
+ };
+ };
+};
* allwinner,sun8i-a23-usb-phy
* allwinner,sun8i-a33-usb-phy
* allwinner,sun8i-h3-usb-phy
+ * allwinner,sun8i-v3s-usb-phy
* allwinner,sun50i-a64-usb-phy
- reg : a list of offset + length pairs
- reg-names :
- vdda-pll-max-microamp : specifies max. load that can be drawn from pll supply
- vddp-ref-clk-supply : phandle to UFS device ref_clk pad power supply
- vddp-ref-clk-max-microamp : specifies max. load that can be drawn from this supply
-- vddp-ref-clk-always-on : specifies if this supply needs to be kept always on
Example:
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
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
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
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
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
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,
--- /dev/null
+/*
+ * 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");
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,
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,
int min_uV;
int max_uV;
bool enabled;
- bool is_always_on;
};
struct ufs_qcom_phy {
&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;
&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);
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.
}
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;
}
}
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")) {
}
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;
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;
}
{
int ret = 0;
- if (!vreg || !vreg->enabled || vreg->is_always_on)
+ if (!vreg || !vreg->enabled)
goto out;
ret = regulator_disable(vreg->reg);
--- /dev/null
+/**
+ * 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");
--- /dev/null
+/**
+ * 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");
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);
}
}
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,
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:
sun6i_a31_phy,
sun8i_a33_phy,
sun8i_h3_phy,
+ sun8i_v3s_phy,
sun50i_a64_phy,
};
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);
}
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) {
}
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);
.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,
{ .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},
{ },