| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (c) 2014-2023, The Linux Foundation. All rights reserved.
- */
- #include <linux/clk.h>
- #include <linux/delay.h>
- #include <linux/err.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>
- #include <linux/reset.h>
- #include <linux/slab.h>
- #define USB2PHY_PORT_UTMI_CTRL1 0x40
- #define USB2PHY_PORT_UTMI_CTRL2 0x44
- #define UTMI_ULPI_SEL BIT(7)
- #define UTMI_TEST_MUX_SEL BIT(6)
- #define HS_PHY_CTRL_REG 0x10
- #define UTMI_OTG_VBUS_VALID BIT(20)
- #define SW_SESSVLD_SEL BIT(28)
- #define USB_PHY_UTMI_CTRL0 0x3c
- #define USB_PHY_UTMI_CTRL5 0x50
- #define POR_EN BIT(1)
- #define USB_PHY_HS_PHY_CTRL_COMMON0 0x54
- #define COMMONONN BIT(7)
- #define FSEL BIT(4)
- #define RETENABLEN BIT(3)
- #define FREQ_24MHZ (BIT(6) | BIT(4))
- #define USB_PHY_HS_PHY_CTRL2 0x64
- #define USB2_SUSPEND_N_SEL BIT(3)
- #define USB2_SUSPEND_N BIT(2)
- #define USB2_UTMI_CLK_EN BIT(1)
- #define USB_PHY_CFG0 0x94
- #define UTMI_PHY_OVERRIDE_EN BIT(1)
- #define USB_PHY_REFCLK_CTRL 0xa0
- #define CLKCORE BIT(1)
- #define USB2PHY_PORT_POWERDOWN 0xa4
- #define POWER_UP BIT(0)
- #define POWER_DOWN 0
- #define USB_PHY_FSEL_SEL 0xb8
- #define FREQ_SEL BIT(0)
- #define USB2PHY_USB_PHY_M31_XCFGI_1 0xbc
- #define USB2_0_TX_ENABLE BIT(2)
- #define USB2PHY_USB_PHY_M31_XCFGI_4 0xc8
- #define HSTX_SLEW_RATE_565PS GENMASK(1, 0)
- #define PLL_CHARGING_PUMP_CURRENT_35UA GENMASK(4, 3)
- #define ODT_VALUE_38_02_OHM GENMASK(7, 6)
- #define USB2PHY_USB_PHY_M31_XCFGI_5 0xcc
- #define ODT_VALUE_45_02_OHM BIT(2)
- #define HSTX_PRE_EMPHASIS_LEVEL_0_55MA BIT(0)
- #define USB2PHY_USB_PHY_M31_XCFGI_11 0xe4
- #define XCFG_COARSE_TUNE_NUM BIT(1)
- #define XCFG_FINE_TUNE_NUM BIT(3)
- struct m31_phy_regs {
- u32 off;
- u32 val;
- u32 delay;
- };
- struct m31_priv_data {
- bool ulpi_mode;
- const struct m31_phy_regs *regs;
- unsigned int nregs;
- };
- static const struct m31_phy_regs m31_ipq5018_regs[] = {
- {
- .off = USB_PHY_CFG0,
- .val = UTMI_PHY_OVERRIDE_EN
- },
- {
- .off = USB_PHY_UTMI_CTRL5,
- .val = POR_EN,
- .delay = 15
- },
- {
- .off = USB_PHY_FSEL_SEL,
- .val = FREQ_SEL
- },
- {
- .off = USB_PHY_HS_PHY_CTRL_COMMON0,
- .val = COMMONONN | FSEL | RETENABLEN
- },
- {
- .off = USB_PHY_REFCLK_CTRL,
- .val = CLKCORE
- },
- {
- .off = USB_PHY_UTMI_CTRL5,
- .val = POR_EN
- },
- {
- .off = USB_PHY_HS_PHY_CTRL2,
- .val = USB2_SUSPEND_N_SEL | USB2_SUSPEND_N | USB2_UTMI_CLK_EN
- },
- {
- .off = USB_PHY_UTMI_CTRL5,
- .val = 0x0
- },
- {
- .off = USB_PHY_HS_PHY_CTRL2,
- .val = USB2_SUSPEND_N | USB2_UTMI_CLK_EN
- },
- {
- .off = USB_PHY_CFG0,
- .val = 0x0
- },
- };
- static struct m31_phy_regs m31_ipq5332_regs[] = {
- {
- USB_PHY_CFG0,
- UTMI_PHY_OVERRIDE_EN,
- 0
- },
- {
- USB_PHY_UTMI_CTRL5,
- POR_EN,
- 15
- },
- {
- USB_PHY_FSEL_SEL,
- FREQ_SEL,
- 0
- },
- {
- USB_PHY_HS_PHY_CTRL_COMMON0,
- COMMONONN | FREQ_24MHZ | RETENABLEN,
- 0
- },
- {
- USB_PHY_UTMI_CTRL5,
- POR_EN,
- 0
- },
- {
- USB_PHY_HS_PHY_CTRL2,
- USB2_SUSPEND_N_SEL | USB2_SUSPEND_N | USB2_UTMI_CLK_EN,
- 0
- },
- {
- USB2PHY_USB_PHY_M31_XCFGI_11,
- XCFG_COARSE_TUNE_NUM | XCFG_FINE_TUNE_NUM,
- 0
- },
- {
- USB2PHY_USB_PHY_M31_XCFGI_4,
- HSTX_SLEW_RATE_565PS | PLL_CHARGING_PUMP_CURRENT_35UA | ODT_VALUE_38_02_OHM,
- 0
- },
- {
- USB2PHY_USB_PHY_M31_XCFGI_1,
- USB2_0_TX_ENABLE,
- 0
- },
- {
- USB2PHY_USB_PHY_M31_XCFGI_5,
- ODT_VALUE_45_02_OHM | HSTX_PRE_EMPHASIS_LEVEL_0_55MA,
- 4
- },
- {
- USB_PHY_UTMI_CTRL5,
- 0x0,
- 0
- },
- {
- USB_PHY_HS_PHY_CTRL2,
- USB2_SUSPEND_N | USB2_UTMI_CLK_EN,
- 0
- },
- };
- struct m31usb_phy {
- struct phy *phy;
- void __iomem *base;
- const struct m31_phy_regs *regs;
- int nregs;
- struct regulator *vreg;
- struct clk *clk;
- struct reset_control *reset;
- bool ulpi_mode;
- };
- static int m31usb_phy_init(struct phy *phy)
- {
- struct m31usb_phy *qphy = phy_get_drvdata(phy);
- const struct m31_phy_regs *regs = qphy->regs;
- int i, ret;
- ret = regulator_enable(qphy->vreg);
- if (ret) {
- dev_err(&phy->dev, "failed to enable regulator, %d\n", ret);
- return ret;
- }
- ret = clk_prepare_enable(qphy->clk);
- if (ret) {
- regulator_disable(qphy->vreg);
- dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
- return ret;
- }
- /* Perform phy reset */
- reset_control_assert(qphy->reset);
- udelay(5);
- reset_control_deassert(qphy->reset);
- /* configure for ULPI mode if requested */
- if (qphy->ulpi_mode)
- writel(0x0, qphy->base + USB2PHY_PORT_UTMI_CTRL2);
- /* Enable the PHY */
- writel(POWER_UP, qphy->base + USB2PHY_PORT_POWERDOWN);
- /* Turn on phy ref clock */
- for (i = 0; i < qphy->nregs; i++) {
- writel(regs[i].val, qphy->base + regs[i].off);
- if (regs[i].delay)
- udelay(regs[i].delay);
- }
- return 0;
- }
- static int m31usb_phy_shutdown(struct phy *phy)
- {
- struct m31usb_phy *qphy = phy_get_drvdata(phy);
- /* Disable the PHY */
- writel_relaxed(POWER_DOWN, qphy->base + USB2PHY_PORT_POWERDOWN);
- clk_disable_unprepare(qphy->clk);
- regulator_disable(qphy->vreg);
- return 0;
- }
- static const struct phy_ops m31usb_phy_gen_ops = {
- .power_on = m31usb_phy_init,
- .power_off = m31usb_phy_shutdown,
- .owner = THIS_MODULE,
- };
- static int m31usb_phy_probe(struct platform_device *pdev)
- {
- struct phy_provider *phy_provider;
- const struct m31_priv_data *data;
- struct device *dev = &pdev->dev;
- struct m31usb_phy *qphy;
- qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
- if (!qphy)
- return -ENOMEM;
- qphy->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(qphy->base))
- return PTR_ERR(qphy->base);
- qphy->reset = devm_reset_control_get_exclusive_by_index(dev, 0);
- if (IS_ERR(qphy->reset))
- return PTR_ERR(qphy->reset);
- qphy->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(qphy->clk))
- return dev_err_probe(dev, PTR_ERR(qphy->clk),
- "failed to get clk\n");
- data = of_device_get_match_data(dev);
- qphy->regs = data->regs;
- qphy->nregs = data->nregs;
- qphy->ulpi_mode = data->ulpi_mode;
- qphy->phy = devm_phy_create(dev, NULL, &m31usb_phy_gen_ops);
- if (IS_ERR(qphy->phy))
- return dev_err_probe(dev, PTR_ERR(qphy->phy),
- "failed to create phy\n");
- qphy->vreg = devm_regulator_get(dev, "vdd");
- if (IS_ERR(qphy->vreg))
- return dev_err_probe(dev, PTR_ERR(qphy->vreg),
- "failed to get vreg\n");
- phy_set_drvdata(qphy->phy, qphy);
- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- if (!IS_ERR(phy_provider))
- dev_info(dev, "Registered M31 USB phy\n");
- return PTR_ERR_OR_ZERO(phy_provider);
- }
- static const struct m31_priv_data m31_ipq5018_data = {
- .ulpi_mode = false,
- .regs = m31_ipq5018_regs,
- .nregs = ARRAY_SIZE(m31_ipq5018_regs),
- };
- static const struct m31_priv_data m31_ipq5332_data = {
- .ulpi_mode = false,
- .regs = m31_ipq5332_regs,
- .nregs = ARRAY_SIZE(m31_ipq5332_regs),
- };
- static const struct of_device_id m31usb_phy_id_table[] = {
- { .compatible = "qcom,ipq5018-usb-hsphy", .data = &m31_ipq5018_data },
- { .compatible = "qcom,ipq5332-usb-hsphy", .data = &m31_ipq5332_data },
- { },
- };
- MODULE_DEVICE_TABLE(of, m31usb_phy_id_table);
- static struct platform_driver m31usb_phy_driver = {
- .probe = m31usb_phy_probe,
- .driver = {
- .name = "qcom-m31usb-phy",
- .of_match_table = m31usb_phy_id_table,
- },
- };
- module_platform_driver(m31usb_phy_driver);
- MODULE_DESCRIPTION("USB2 Qualcomm M31 HSPHY driver");
- MODULE_LICENSE("GPL");
|