phy: Add support for Qualcomm's USB HSIC phy
The HSIC USB controller on qcom SoCs has an integrated all digital phy controlled via the ULPI viewport. Cc: Kishon Vijay Abraham I <kishon@ti.com> Acked-by: Rob Herring <robh@kernel.org> Cc: <devicetree@vger.kernel.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
parent
a8df2768c2
commit
605b8652f7
|
@ -0,0 +1,65 @@
|
|||
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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -437,6 +437,13 @@ config PHY_QCOM_UFS
|
|||
help
|
||||
Support for UFS PHY on QCOM 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
|
||||
|
|
|
@ -52,6 +52,7 @@ 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_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
|
||||
|
|
|
@ -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");
|
Loading…
Reference in New Issue