diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-06 16:48:27 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-06 16:48:27 -0800 |
commit | f90d64483ebd394958841f67f8794ab203b319a7 (patch) | |
tree | 3da8638603681adfe9bea7897c03ea27b40ef4f4 /drivers/phy | |
parent | 67e79a6dc2664a3ef85113440e60f7aaca3c7815 (diff) | |
parent | 1c7cf3d5e1c181caca75012b65252288c18a25f2 (diff) |
Merge tag 'usb-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH:
"Here is the big USB/PHY driver pull request for 5.1-rc1.
The usual set of gadget driver updates, phy driver updates, xhci
updates, and typec additions. Also included in here are a lot of small
cleanups and fixes and driver updates where needed.
All of these have been in linux-next for a while with no reported
issues"
* tag 'usb-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (167 commits)
wusb: Remove unnecessary static function ckhdid_printf
usb: core: make default autosuspend delay configurable
usb: core: Fix typo in description of "authorized_default"
usb: chipidea: Refactor USB PHY selection and keep a single PHY
usb: chipidea: Grab the (legacy) USB PHY by phandle first
usb: chipidea: imx: set power polarity
dt-bindings: usb: ci-hdrc-usb2: add property power-active-high
usb: chipidea: imx: remove unused header files
usb: chipidea: tegra: Fix missed ci_hdrc_remove_device()
usb: core: add option of only authorizing internal devices
usb: typec: tps6598x: handle block writes separately with plain-I2C adapters
usb: xhci: Fix for Enabling USB ROLE SWITCH QUIRK on INTEL_SUNRISEPOINT_LP_XHCI
usb: xhci: fix build warning - missing prototype
usb: xhci: dbc: Fixing typo error.
usb: xhci: remove unused member 'parent' in xhci_regset struct
xhci: tegra: Prevent error pointer dereference
USB: serial: option: add Telit ME910 ECM composition
usb: core: Replace hardcoded check with inline function from usb.h
usb: core: skip interfaces disabled in devicetree
usb: typec: mux: remove redundant check on variable match
...
Diffstat (limited to 'drivers/phy')
-rw-r--r-- | drivers/phy/broadcom/phy-bcm-sr-pcie.c | 4 | ||||
-rw-r--r-- | drivers/phy/cadence/Kconfig | 13 | ||||
-rw-r--r-- | drivers/phy/cadence/Makefile | 1 | ||||
-rw-r--r-- | drivers/phy/cadence/cdns-dphy.c | 391 | ||||
-rw-r--r-- | drivers/phy/freescale/Kconfig | 2 | ||||
-rw-r--r-- | drivers/phy/marvell/Kconfig | 21 | ||||
-rw-r--r-- | drivers/phy/marvell/Makefile | 2 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-armada375-usb2.c | 13 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 318 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 278 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 2 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-mvebu-sata.c | 9 | ||||
-rw-r--r-- | drivers/phy/phy-core-mipi-dphy.c | 8 | ||||
-rw-r--r-- | drivers/phy/phy-core.c | 12 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.c | 143 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qmp.h | 4 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-qusb2.c | 40 | ||||
-rw-r--r-- | drivers/phy/qualcomm/phy-qcom-ufs-i.h | 19 | ||||
-rw-r--r-- | drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 48 | ||||
-rw-r--r-- | drivers/phy/ti/Kconfig | 6 | ||||
-rw-r--r-- | drivers/phy/ti/phy-omap-usb2.c | 105 |
21 files changed, 1309 insertions, 130 deletions
diff --git a/drivers/phy/broadcom/phy-bcm-sr-pcie.c b/drivers/phy/broadcom/phy-bcm-sr-pcie.c index c10e95f86de5..96a3af126a78 100644 --- a/drivers/phy/broadcom/phy-bcm-sr-pcie.c +++ b/drivers/phy/broadcom/phy-bcm-sr-pcie.c @@ -78,8 +78,8 @@ struct sr_pcie_phy_core { static const u8 pipemux_table[] = { /* PIPEMUX = 0, EP 1x16 */ 0x00, - /* PIPEMUX = 1, EP 2x8 */ - 0x00, + /* PIPEMUX = 1, EP 1x8 + RC 1x8, core 7 */ + 0x80, /* PIPEMUX = 2, EP 4x4 */ 0x00, /* PIPEMUX = 3, RC 2x8, cores 0, 7 */ diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig index 2b8c0851ff33..31f18b67dd7c 100644 --- a/drivers/phy/cadence/Kconfig +++ b/drivers/phy/cadence/Kconfig @@ -1,6 +1,7 @@ # # Phy drivers for Cadence PHYs # + config PHY_CADENCE_DP tristate "Cadence MHDP DisplayPort PHY driver" depends on OF @@ -9,9 +10,19 @@ config PHY_CADENCE_DP help Support for Cadence MHDP DisplayPort PHY. +config PHY_CADENCE_DPHY + tristate "Cadence D-PHY Support" + depends on HAS_IOMEM && OF + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Choose this option if you have a Cadence D-PHY in your + system. If M is selected, the module will be called + cdns-dphy. + config PHY_CADENCE_SIERRA tristate "Cadence Sierra PHY Driver" depends on OF && HAS_IOMEM && RESET_CONTROLLER select GENERIC_PHY help - Enable this to support the Cadence Sierra PHY driver
\ No newline at end of file + Enable this to support the Cadence Sierra PHY driver diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index 412349af0492..2f9e3457b954 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o +obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c new file mode 100644 index 000000000000..90c4e9b5aac8 --- /dev/null +++ b/drivers/phy/cadence/cdns-dphy.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright: 2017-2018 Cadence Design Systems, Inc. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> + +#define REG_WAKEUP_TIME_NS 800 +#define DPHY_PLL_RATE_HZ 108000000 + +/* DPHY registers */ +#define DPHY_PMA_CMN(reg) (reg) +#define DPHY_PMA_LCLK(reg) (0x100 + (reg)) +#define DPHY_PMA_LDATA(lane, reg) (0x200 + ((lane) * 0x100) + (reg)) +#define DPHY_PMA_RCLK(reg) (0x600 + (reg)) +#define DPHY_PMA_RDATA(lane, reg) (0x700 + ((lane) * 0x100) + (reg)) +#define DPHY_PCS(reg) (0xb00 + (reg)) + +#define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) +#define DPHY_CMN_SSM_EN BIT(0) +#define DPHY_CMN_TX_MODE_EN BIT(9) + +#define DPHY_CMN_PWM DPHY_PMA_CMN(0x40) +#define DPHY_CMN_PWM_DIV(x) ((x) << 20) +#define DPHY_CMN_PWM_LOW(x) ((x) << 10) +#define DPHY_CMN_PWM_HIGH(x) (x) + +#define DPHY_CMN_FBDIV DPHY_PMA_CMN(0x4c) +#define DPHY_CMN_FBDIV_VAL(low, high) (((high) << 11) | ((low) << 22)) +#define DPHY_CMN_FBDIV_FROM_REG (BIT(10) | BIT(21)) + +#define DPHY_CMN_OPIPDIV DPHY_PMA_CMN(0x50) +#define DPHY_CMN_IPDIV_FROM_REG BIT(0) +#define DPHY_CMN_IPDIV(x) ((x) << 1) +#define DPHY_CMN_OPDIV_FROM_REG BIT(6) +#define DPHY_CMN_OPDIV(x) ((x) << 7) + +#define DPHY_PSM_CFG DPHY_PCS(0x4) +#define DPHY_PSM_CFG_FROM_REG BIT(0) +#define DPHY_PSM_CLK_DIV(x) ((x) << 1) + +#define DSI_HBP_FRAME_OVERHEAD 12 +#define DSI_HSA_FRAME_OVERHEAD 14 +#define DSI_HFP_FRAME_OVERHEAD 6 +#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4 +#define DSI_BLANKING_FRAME_OVERHEAD 6 +#define DSI_NULL_FRAME_OVERHEAD 6 +#define DSI_EOT_PKT_SIZE 4 + +struct cdns_dphy_cfg { + u8 pll_ipdiv; + u8 pll_opdiv; + u16 pll_fbdiv; + unsigned int nlanes; +}; + +enum cdns_dphy_clk_lane_cfg { + DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0, + DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1, + DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2, + DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3, +}; + +struct cdns_dphy; +struct cdns_dphy_ops { + int (*probe)(struct cdns_dphy *dphy); + void (*remove)(struct cdns_dphy *dphy); + void (*set_psm_div)(struct cdns_dphy *dphy, u8 div); + void (*set_clk_lane_cfg)(struct cdns_dphy *dphy, + enum cdns_dphy_clk_lane_cfg cfg); + void (*set_pll_cfg)(struct cdns_dphy *dphy, + const struct cdns_dphy_cfg *cfg); + unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy); +}; + +struct cdns_dphy { + struct cdns_dphy_cfg cfg; + void __iomem *regs; + struct clk *psm_clk; + struct clk *pll_ref_clk; + const struct cdns_dphy_ops *ops; + struct phy *phy; +}; + +static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, + struct cdns_dphy_cfg *cfg, + struct phy_configure_opts_mipi_dphy *opts, + unsigned int *dsi_hfp_ext) +{ + unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk); + u64 dlane_bps; + + memset(cfg, 0, sizeof(*cfg)); + + if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000) + return -EINVAL; + else if (pll_ref_hz < 19200000) + cfg->pll_ipdiv = 1; + else if (pll_ref_hz < 38400000) + cfg->pll_ipdiv = 2; + else if (pll_ref_hz < 76800000) + cfg->pll_ipdiv = 4; + else + cfg->pll_ipdiv = 8; + + dlane_bps = opts->hs_clk_rate; + + if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL) + return -EINVAL; + else if (dlane_bps >= 1250000000) + cfg->pll_opdiv = 1; + else if (dlane_bps >= 630000000) + cfg->pll_opdiv = 2; + else if (dlane_bps >= 320000000) + cfg->pll_opdiv = 4; + else if (dlane_bps >= 160000000) + cfg->pll_opdiv = 8; + + cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv * + cfg->pll_ipdiv, + pll_ref_hz); + + return 0; +} + +static int cdns_dphy_setup_psm(struct cdns_dphy *dphy) +{ + unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk); + unsigned long psm_div; + + if (!psm_clk_hz || psm_clk_hz > 100000000) + return -EINVAL; + + psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000); + if (dphy->ops->set_psm_div) + dphy->ops->set_psm_div(dphy, psm_div); + + return 0; +} + +static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy, + enum cdns_dphy_clk_lane_cfg cfg) +{ + if (dphy->ops->set_clk_lane_cfg) + dphy->ops->set_clk_lane_cfg(dphy, cfg); +} + +static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy, + const struct cdns_dphy_cfg *cfg) +{ + if (dphy->ops->set_pll_cfg) + dphy->ops->set_pll_cfg(dphy, cfg); +} + +static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy) +{ + return dphy->ops->get_wakeup_time_ns(dphy); +} + +static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy) +{ + /* Default wakeup time is 800 ns (in a simulated environment). */ + return 800; +} + +static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy, + const struct cdns_dphy_cfg *cfg) +{ + u32 fbdiv_low, fbdiv_high; + + fbdiv_low = (cfg->pll_fbdiv / 4) - 2; + fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2; + + writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG | + DPHY_CMN_IPDIV(cfg->pll_ipdiv) | + DPHY_CMN_OPDIV(cfg->pll_opdiv), + dphy->regs + DPHY_CMN_OPIPDIV); + writel(DPHY_CMN_FBDIV_FROM_REG | + DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high), + dphy->regs + DPHY_CMN_FBDIV); + writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) | + DPHY_CMN_PWM_DIV(0x8), + dphy->regs + DPHY_CMN_PWM); +} + +static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div) +{ + writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div), + dphy->regs + DPHY_PSM_CFG); +} + +/* + * This is the reference implementation of DPHY hooks. Specific integration of + * this IP may have to re-implement some of them depending on how they decided + * to wire things in the SoC. + */ +static const struct cdns_dphy_ops ref_dphy_ops = { + .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns, + .set_pll_cfg = cdns_dphy_ref_set_pll_cfg, + .set_psm_div = cdns_dphy_ref_set_psm_div, +}; + +static int cdns_dphy_config_from_opts(struct phy *phy, + struct phy_configure_opts_mipi_dphy *opts, + struct cdns_dphy_cfg *cfg) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + unsigned int dsi_hfp_ext = 0; + int ret; + + ret = phy_mipi_dphy_config_validate(opts); + if (ret) + return ret; + + ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg, + opts, &dsi_hfp_ext); + if (ret) + return ret; + + opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000; + + return 0; +} + +static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, + union phy_configure_opts *opts) +{ + struct cdns_dphy_cfg cfg = { 0 }; + + if (mode != PHY_MODE_MIPI_DPHY) + return -EINVAL; + + return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); +} + +static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + struct cdns_dphy_cfg cfg = { 0 }; + int ret; + + ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); + if (ret) + return ret; + + /* + * Configure the internal PSM clk divider so that the DPHY has a + * 1MHz clk (or something close). + */ + ret = cdns_dphy_setup_psm(dphy); + if (ret) + return ret; + + /* + * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes + * and 8 data lanes, each clk lane can be attache different set of + * data lanes. The 2 groups are named 'left' and 'right', so here we + * just say that we want the 'left' clk lane to drive the 'left' data + * lanes. + */ + cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT); + + /* + * Configure the DPHY PLL that will be used to generate the TX byte + * clk. + */ + cdns_dphy_set_pll_cfg(dphy, &cfg); + + return 0; +} + +static int cdns_dphy_power_on(struct phy *phy) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + + clk_prepare_enable(dphy->psm_clk); + clk_prepare_enable(dphy->pll_ref_clk); + + /* Start TX state machine. */ + writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, + dphy->regs + DPHY_CMN_SSM); + + return 0; +} + +static int cdns_dphy_power_off(struct phy *phy) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + + clk_disable_unprepare(dphy->pll_ref_clk); + clk_disable_unprepare(dphy->psm_clk); + + return 0; +} + +static const struct phy_ops cdns_dphy_ops = { + .configure = cdns_dphy_configure, + .validate = cdns_dphy_validate, + .power_on = cdns_dphy_power_on, + .power_off = cdns_dphy_power_off, +}; + +static int cdns_dphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct cdns_dphy *dphy; + struct resource *res; + int ret; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, dphy); + + dphy->ops = of_device_get_match_data(&pdev->dev); + if (!dphy->ops) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dphy->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->psm_clk = devm_clk_get(&pdev->dev, "psm"); + if (IS_ERR(dphy->psm_clk)) + return PTR_ERR(dphy->psm_clk); + + dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref"); + if (IS_ERR(dphy->pll_ref_clk)) + return PTR_ERR(dphy->pll_ref_clk); + + if (dphy->ops->probe) { + ret = dphy->ops->probe(dphy); + if (ret) + return ret; + } + + dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops); + if (IS_ERR(dphy->phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + if (dphy->ops->remove) + dphy->ops->remove(dphy); + return PTR_ERR(dphy->phy); + } + + phy_set_drvdata(dphy->phy, dphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int cdns_dphy_remove(struct platform_device *pdev) +{ + struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev); + + if (dphy->ops->remove) + dphy->ops->remove(dphy); + + return 0; +} + +static const struct of_device_id cdns_dphy_of_match[] = { + { .compatible = "cdns,dphy", .data = &ref_dphy_ops }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cdns_dphy_of_match); + +static struct platform_driver cdns_dphy_platform_driver = { + .probe = cdns_dphy_probe, + .remove = cdns_dphy_remove, + .driver = { + .name = "cdns-mipi-dphy", + .of_match_table = cdns_dphy_of_match, + }, +}; +module_platform_driver(cdns_dphy_platform_driver); + +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>"); +MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index f050bd4e97e0..832670b4952b 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -2,4 +2,4 @@ config PHY_FSL_IMX8MQ_USB tristate "Freescale i.MX8M USB3 PHY" depends on OF && HAS_IOMEM select GENERIC_PHY - default SOC_IMX8MQ + default ARCH_MXC && ARM64 diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig index 224ea4e6a46d..9ba872325dad 100644 --- a/drivers/phy/marvell/Kconfig +++ b/drivers/phy/marvell/Kconfig @@ -21,6 +21,27 @@ config PHY_BERLIN_USB help Enable this to support the USB PHY on Marvell Berlin SoCs. +config PHY_MVEBU_A3700_COMPHY + tristate "Marvell A3700 comphy driver" + depends on ARCH_MVEBU || COMPILE_TEST + depends on OF + depends on HAVE_ARM_SMCCC + default y + select GENERIC_PHY + help + This driver allows to control the comphy, a hardware block providing + shared serdes PHYs on Marvell Armada 3700. Its serdes lanes can be + used by various controllers: Ethernet, SATA, USB3, PCIe. + +config PHY_MVEBU_A3700_UTMI + tristate "Marvell A3700 UTMI driver" + depends on ARCH_MVEBU || COMPILE_TEST + depends on OF + default y + select GENERIC_PHY + help + Enable this to support Marvell A3700 UTMI PHY driver. + config PHY_MVEBU_A38X_COMPHY tristate "Marvell Armada 38x comphy driver" depends on ARCH_MVEBU || COMPILE_TEST diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile index 59b6c03ef756..434eb9ca6cc3 100644 --- a/drivers/phy/marvell/Makefile +++ b/drivers/phy/marvell/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o +obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o +obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o obj-$(CONFIG_PHY_MVEBU_A38X_COMPHY) += phy-armada38x-comphy.o obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o diff --git a/drivers/phy/marvell/phy-armada375-usb2.c b/drivers/phy/marvell/phy-armada375-usb2.c index 1a3db288c0a9..fa5dc9462d09 100644 --- a/drivers/phy/marvell/phy-armada375-usb2.c +++ b/drivers/phy/marvell/phy-armada375-usb2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB cluster support for Armada 375 platform. * @@ -5,10 +6,6 @@ * * Gregory CLEMENT <gregory.clement@free-electrons.com> * - * This file is licensed under the terms of the GNU General Public - * License version 2 or later. This program is licensed "as is" - * without any warranty of any kind, whether express or implied. - * * Armada 375 comes with an USB2 host and device controller and an * USB3 controller. The USB cluster control register allows to manage * common features of both USB controllers. @@ -18,7 +15,6 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> -#include <linux/module.h> #include <linux/of_address.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -142,7 +138,6 @@ static const struct of_device_id of_usb_cluster_table[] = { { .compatible = "marvell,armada-375-usb-cluster", }, { /* end of list */ }, }; -MODULE_DEVICE_TABLE(of, of_usb_cluster_table); static struct platform_driver armada375_usb_phy_driver = { .probe = armada375_usb_phy_probe, @@ -151,8 +146,4 @@ static struct platform_driver armada375_usb_phy_driver = { .name = "armada-375-usb-cluster", } }; -module_platform_driver(armada375_usb_phy_driver); - -MODULE_DESCRIPTION("Armada 375 USB cluster driver"); -MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); -MODULE_LICENSE("GPL"); +builtin_platform_driver(armada375_usb_phy_driver); diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c new file mode 100644 index 000000000000..8812a104c233 --- /dev/null +++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell + * + * Authors: + * Evan Wang <xswang@marvell.com> + * Miquèl Raynal <miquel.raynal@bootlin.com> + * + * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. + * SMC call initial support done by Grzegorz Jaszczyk. + */ + +#include <linux/arm-smccc.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#define MVEBU_A3700_COMPHY_LANES 3 +#define MVEBU_A3700_COMPHY_PORTS 2 + +/* COMPHY Fast SMC function identifiers */ +#define COMPHY_SIP_POWER_ON 0x82000001 +#define COMPHY_SIP_POWER_OFF 0x82000002 +#define COMPHY_SIP_PLL_LOCK 0x82000003 + +#define COMPHY_FW_MODE_SATA 0x1 +#define COMPHY_FW_MODE_SGMII 0x2 +#define COMPHY_FW_MODE_HS_SGMII 0x3 +#define COMPHY_FW_MODE_USB3H 0x4 +#define COMPHY_FW_MODE_USB3D 0x5 +#define COMPHY_FW_MODE_PCIE 0x6 +#define COMPHY_FW_MODE_RXAUI 0x7 +#define COMPHY_FW_MODE_XFI 0x8 +#define COMPHY_FW_MODE_SFI 0x9 +#define COMPHY_FW_MODE_USB3 0xa + +#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ +#define COMPHY_FW_SPEED_2_5G 1 +#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */ +#define COMPHY_FW_SPEED_5G 3 +#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */ +#define COMPHY_FW_SPEED_6G 5 +#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */ +#define COMPHY_FW_SPEED_MAX 0x3F + +#define COMPHY_FW_MODE(mode) ((mode) << 12) +#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ + ((idx) << 8) | \ + ((speed) << 2)) +#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \ + ((width) << 18)) + +struct mvebu_a3700_comphy_conf { + unsigned int lane; + enum phy_mode mode; + int submode; + unsigned int port; + u32 fw_mode; +}; + +#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \ + { \ + .lane = _lane, \ + .mode = _mode, \ + .submode = _smode, \ + .port = _port, \ + .fw_mode = _fw, \ + } + +#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \ + MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw) + +#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \ + MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw) + +static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { + /* lane 0 */ + MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0, + COMPHY_FW_MODE_USB3H), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, + COMPHY_FW_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, + COMPHY_FW_MODE_HS_SGMII), + /* lane 1 */ + MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0, + COMPHY_FW_MODE_PCIE), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0, + COMPHY_FW_MODE_SGMII), + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, + COMPHY_FW_MODE_HS_SGMII), + /* lane 2 */ + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, + COMPHY_FW_MODE_SATA), + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0, + COMPHY_FW_MODE_USB3H), +}; + +struct mvebu_a3700_comphy_lane { + struct device *dev; + unsigned int id; + enum phy_mode mode; + int submode; + int port; +}; + +static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, + unsigned long mode) +{ + struct arm_smccc_res res; + + arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); + + return res.a0; +} + +static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, + enum phy_mode mode, + int submode) +{ + int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes); + + /* Unused PHY mux value is 0x0 */ + if (mode == PHY_MODE_INVALID) + return -EINVAL; + + for (i = 0; i < n; i++) { + if (mvebu_a3700_comphy_modes[i].lane == lane && + mvebu_a3700_comphy_modes[i].port == port && + mvebu_a3700_comphy_modes[i].mode == mode && + mvebu_a3700_comphy_modes[i].submode == submode) + break; + } + + if (i == n) + return -EINVAL; + + return mvebu_a3700_comphy_modes[i].fw_mode; +} + +static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + int fw_mode; + + if (submode == PHY_INTERFACE_MODE_1000BASEX) + submode = PHY_INTERFACE_MODE_SGMII; + + fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode, + submode); + if (fw_mode < 0) { + dev_err(lane->dev, "invalid COMPHY mode\n"); + return fw_mode; + } + + /* Just remember the mode, ->power_on() will do the real setup */ + lane->mode = mode; + lane->submode = submode; + + return 0; +} + +static int mvebu_a3700_comphy_power_on(struct phy *phy) +{ + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + u32 fw_param; + int fw_mode; + + fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, + lane->mode, lane->submode); + if (fw_mode < 0) { + dev_err(lane->dev, "invalid COMPHY mode\n"); + return fw_mode; + } + + switch (lane->mode) { + case PHY_MODE_USB_HOST_SS: + dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id); + fw_param = COMPHY_FW_MODE(fw_mode); + break; + case PHY_MODE_SATA: + dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id); + fw_param = COMPHY_FW_MODE(fw_mode); + break; + case PHY_MODE_ETHERNET: + switch (lane->submode) { + case PHY_INTERFACE_MODE_SGMII: + dev_dbg(lane->dev, "set lane %d to SGMII mode\n", + lane->id); + fw_param = COMPHY_FW_NET(fw_mode, lane->port, + COMPHY_FW_SPEED_1_25G); + break; + case PHY_INTERFACE_MODE_2500BASEX: + dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n", + lane->id); + fw_param = COMPHY_FW_NET(fw_mode, lane->port, + COMPHY_FW_SPEED_3_125G); + break; + default: + dev_err(lane->dev, "unsupported PHY submode (%d)\n", + lane->submode); + return -ENOTSUPP; + } + break; + case PHY_MODE_PCIE: + dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); + fw_param = COMPHY_FW_PCIE(fw_mode, lane->port, + COMPHY_FW_SPEED_5G, + phy->attrs.bus_width); + break; + default: + dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); + return -ENOTSUPP; + } + + return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); +} + +static int mvebu_a3700_comphy_power_off(struct phy *phy) +{ + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); + + return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); +} + +static const struct phy_ops mvebu_a3700_comphy_ops = { + .power_on = mvebu_a3700_comphy_power_on, + .power_off = mvebu_a3700_comphy_power_off, + .set_mode = mvebu_a3700_comphy_set_mode, + .owner = THIS_MODULE, +}; + +static struct phy *mvebu_a3700_comphy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mvebu_a3700_comphy_lane *lane; + struct phy *phy; + + if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS)) + return ERR_PTR(-EINVAL); + + phy = of_phy_simple_xlate(dev, args); + if (IS_ERR(phy)) + return phy; + + lane = phy_get_drvdata(phy); + lane->port = args->args[0]; + + return phy; +} + +static int mvebu_a3700_comphy_probe(struct platform_device *pdev) +{ + struct phy_provider *provider; + struct device_node *child; + + for_each_available_child_of_node(pdev->dev.of_node, child) { + struct mvebu_a3700_comphy_lane *lane; + struct phy *phy; + int ret; + u32 lane_id; + + ret = of_property_read_u32(child, "reg", &lane_id); + if (ret < 0) { + dev_err(&pdev->dev, "missing 'reg' property (%d)\n", + ret); + continue; + } + + if (lane_id >= MVEBU_A3700_COMPHY_LANES) { + dev_err(&pdev->dev, "invalid 'reg' property\n"); + continue; + } + + lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL); + if (!lane) + return -ENOMEM; + + phy = devm_phy_create(&pdev->dev, child, + &mvebu_a3700_comphy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + lane->dev = &pdev->dev; + lane->mode = PHY_MODE_INVALID; + lane->submode = PHY_INTERFACE_MODE_NA; + lane->id = lane_id; + lane->port = -1; + phy_set_drvdata(phy, lane); + } + + provider = devm_of_phy_provider_register(&pdev->dev, + mvebu_a3700_comphy_xlate); + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = { + { .compatible = "marvell,comphy-a3700" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table); + +static struct platform_driver mvebu_a3700_comphy_driver = { + .probe = mvebu_a3700_comphy_probe, + .driver = { + .name = "mvebu-a3700-comphy", + .of_match_table = mvebu_a3700_comphy_of_match_table, + }, +}; +module_platform_driver(mvebu_a3700_comphy_driver); + +MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>"); +MODULE_DESCRIPTION("Common PHY driver for A3700"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c new file mode 100644 index 000000000000..94a29dea57af --- /dev/null +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Marvell + * + * Authors: + * Igal Liberman <igall@marvell.com> + * Miquèl Raynal <miquel.raynal@bootlin.com> + * + * Marvell A3700 UTMI PHY driver + */ + +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* Armada 3700 UTMI PHY registers */ +#define USB2_PHY_PLL_CTRL_REG0 0x0 +#define PLL_REF_DIV_OFF 0 +#define PLL_REF_DIV_MASK GENMASK(6, 0) +#define PLL_REF_DIV_5 5 +#define PLL_FB_DIV_OFF 16 +#define PLL_FB_DIV_MASK GENMASK(24, 16) +#define PLL_FB_DIV_96 96 +#define PLL_SEL_LPFR_OFF 28 +#define PLL_SEL_LPFR_MASK GENMASK(29, 28) +#define PLL_READY BIT(31) +#define USB2_PHY_CAL_CTRL 0x8 +#define PHY_PLLCAL_DONE BIT(31) +#define PHY_IMPCAL_DONE BIT(23) +#define USB2_RX_CHAN_CTRL1 0x18 +#define USB2PHY_SQCAL_DONE BIT(31) +#define USB2_PHY_OTG_CTRL 0x34 +#define PHY_PU_OTG BIT(4) +#define USB2_PHY_CHRGR_DETECT 0x38 +#define PHY_CDP_EN BIT(2) +#define PHY_DCP_EN BIT(3) +#define PHY_PD_EN BIT(4) +#define PHY_PU_CHRG_DTC BIT(5) +#define PHY_CDP_DM_AUTO BIT(7) +#define PHY_ENSWITCH_DP BIT(12) +#define PHY_ENSWITCH_DM BIT(13) + +/* Armada 3700 USB miscellaneous registers */ +#define USB2_PHY_CTRL(usb32) (usb32 ? 0x20 : 0x4) +#define RB_USB2PHY_PU BIT(0) +#define USB2_DP_PULLDN_DEV_MODE BIT(5) +#define USB2_DM_PULLDN_DEV_MODE BIT(6) +#define RB_USB2PHY_SUSPM(usb32) (usb32 ? BIT(14) : BIT(7)) + +#define PLL_LOCK_DELAY_US 10000 +#define PLL_LOCK_TIMEOUT_US 1000000 + +/** + * struct mvebu_a3700_utmi_caps - PHY capabilities + * + * @usb32: Flag indicating which PHY is in use (impacts the register map): + * - The UTMI PHY wired to the USB3/USB2 controller (otg) + * - The UTMI PHY wired to the USB2 controller (host only) + * @ops: PHY operations + */ +struct mvebu_a3700_utmi_caps { + int usb32; + const struct phy_ops *ops; +}; + +/** + * struct mvebu_a3700_utmi - PHY driver data + * + * @regs: PHY registers + * @usb_mis: Regmap with USB miscellaneous registers including PHY ones + * @caps: PHY capabilities + * @phy: PHY handle + */ +struct mvebu_a3700_utmi { + void __iomem *regs; + struct regmap *usb_misc; + const struct mvebu_a3700_utmi_caps *caps; + struct phy *phy; +}; + +static int mvebu_a3700_utmi_phy_power_on(struct phy *phy) +{ + struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy); + struct device *dev = &phy->dev; + int usb32 = utmi->caps->usb32; + int ret = 0; + u32 reg; + + /* + * Setup PLL. 40MHz clock used to be the default, being 25MHz now. + * See "PLL Settings for Typical REFCLK" table. + */ + reg = readl(utmi->regs + USB2_PHY_PLL_CTRL_REG0); + reg &= ~(PLL_REF_DIV_MASK | PLL_FB_DIV_MASK | PLL_SEL_LPFR_MASK); + reg |= (PLL_REF_DIV_5 << PLL_REF_DIV_OFF) | + (PLL_FB_DIV_96 << PLL_FB_DIV_OFF); + writel(reg, utmi->regs + USB2_PHY_PLL_CTRL_REG0); + + /* Enable PHY pull up and disable USB2 suspend */ + regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), + RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU, + RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU); + + if (usb32) { + /* Power up OTG module */ + reg = readl(utmi->regs + USB2_PHY_OTG_CTRL); + reg |= PHY_PU_OTG; + writel(reg, utmi->regs + USB2_PHY_OTG_CTRL); + + /* Disable PHY charger detection */ + reg = readl(utmi->regs + USB2_PHY_CHRGR_DETECT); + reg &= ~(PHY_CDP_EN | PHY_DCP_EN | PHY_PD_EN | PHY_PU_CHRG_DTC | + PHY_CDP_DM_AUTO | PHY_ENSWITCH_DP | PHY_ENSWITCH_DM); + writel(reg, utmi->regs + USB2_PHY_CHRGR_DETECT); + + /* Disable PHY DP/DM pull-down (used for device mode) */ + regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), + USB2_DP_PULLDN_DEV_MODE | + USB2_DM_PULLDN_DEV_MODE, 0); + } + + /* Wait for PLL calibration */ + ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg, + reg & PHY_PLLCAL_DONE, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + dev_err(dev, "Failed to end USB2 PLL calibration\n"); + return ret; + } + + /* Wait for impedance calibration */ + ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg, + reg & PHY_IMPCAL_DONE, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + dev_err(dev, "Failed to end USB2 impedance calibration\n"); + return ret; + } + + /* Wait for squelch calibration */ + ret = readl_poll_timeout(utmi->regs + USB2_RX_CHAN_CTRL1, reg, + reg & USB2PHY_SQCAL_DONE, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) { + dev_err(dev, "Failed to end USB2 unknown calibration\n"); + return ret; + } + + /* Wait for PLL to be locked */ + ret = readl_poll_timeout(utmi->regs + USB2_PHY_PLL_CTRL_REG0, reg, + reg & PLL_READY, + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); + if (ret) + dev_err(dev, "Failed to lock USB2 PLL\n"); + + return ret; +} + +static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) +{ + struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy); + int usb32 = utmi->caps->usb32; + u32 reg; + + /* Disable PHY pull-up and enable USB2 suspend */ + reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); + reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); + writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); + + /* Power down OTG module */ + if (usb32) { + reg = readl(utmi->regs + USB2_PHY_OTG_CTRL); + reg &= ~PHY_PU_OTG; + writel(reg, utmi->regs + USB2_PHY_OTG_CTRL); + } + + return 0; +} + +static const struct phy_ops mvebu_a3700_utmi_phy_ops = { + .power_on = mvebu_a3700_utmi_phy_power_on, + .power_off = mvebu_a3700_utmi_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = { + .usb32 = true, + .ops = &mvebu_a3700_utmi_phy_ops, +}; + +static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = { + .usb32 = false, + .ops = &mvebu_a3700_utmi_phy_ops, +}; + +static const struct of_device_id mvebu_a3700_utmi_of_match[] = { + { + .compatible = "marvell,a3700-utmi-otg-phy", + .data = &mvebu_a3700_utmi_otg_phy_caps, + }, + { + .compatible = "marvell,a3700-utmi-host-phy", + .data = &mvebu_a3700_utmi_host_phy_caps, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match); + +static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mvebu_a3700_utmi *utmi; + struct phy_provider *provider; + struct resource *res; + + utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL); + if (!utmi) + return -ENOMEM; + + /* Get UTMI memory region */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Missing UTMI PHY memory resource\n"); + return -ENODEV; + } + + utmi->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(utmi->regs)) + return PTR_ERR(utmi->regs); + + /* Get miscellaneous Host/PHY region */ + utmi->usb_misc = syscon_regmap_lookup_by_phandle(dev->of_node, + "marvell,usb-misc-reg"); + if (IS_ERR(utmi->usb_misc)) { + dev_err(dev, + "Missing USB misc purpose system controller\n"); + return PTR_ERR(utmi->usb_misc); + } + + /* Retrieve PHY capabilities */ + utmi->caps = of_device_get_match_data(dev); + + /* Instantiate the PHY */ + utmi->phy = devm_phy_create(dev, NULL, utmi->caps->ops); + if (IS_ERR(utmi->phy)) { + dev_err(dev, "Failed to create the UTMI PHY\n"); + return PTR_ERR(utmi->phy); + } + + phy_set_drvdata(utmi->phy, utmi); + + /* Ensure the PHY is powered off */ + utmi->caps->ops->power_off(utmi->phy); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static struct platform_driver mvebu_a3700_utmi_driver = { + .probe = mvebu_a3700_utmi_phy_probe, + .driver = { + .name = "mvebu-a3700-utmi-phy", + .owner = THIS_MODULE, + .of_match_table = mvebu_a3700_utmi_of_match, + }, +}; +module_platform_driver(mvebu_a3700_utmi_driver); + +MODULE_AUTHOR("Igal Liberman <igall@marvell.com>"); +MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>"); +MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 187cccde53b5..d98e0451f6a1 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -580,8 +580,6 @@ static struct phy *mvebu_comphy_xlate(struct device *dev, return phy; lane = phy_get_drvdata(phy); - if (lane->port >= 0) - return ERR_PTR(-EBUSY); lane->port = args->args[0]; return phy; diff --git a/drivers/phy/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c index 768ce92e81ce..369fece2be7a 100644 --- a/drivers/phy/marvell/phy-mvebu-sata.c +++ b/drivers/phy/marvell/phy-mvebu-sata.c @@ -10,7 +10,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/clk.h> #include <linux/phy/phy.h> #include <linux/io.h> @@ -122,7 +122,6 @@ static const struct of_device_id phy_mvebu_sata_of_match[] = { { .compatible = "marvell,mvebu-sata-phy" }, { }, }; -MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match); static struct platform_driver phy_mvebu_sata_driver = { .probe = phy_mvebu_sata_probe, @@ -131,8 +130,4 @@ static struct platform_driver phy_mvebu_sata_driver = { .of_match_table = phy_mvebu_sata_of_match, } }; -module_platform_driver(phy_mvebu_sata_driver); - -MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); -MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver"); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(phy_mvebu_sata_driver); diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c index 465fa1b91a5f..14e0551cd319 100644 --- a/drivers/phy/phy-core-mipi-dphy.c +++ b/drivers/phy/phy-core-mipi-dphy.c @@ -65,12 +65,12 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock, */ cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui); - cfg->init = 100000000; + cfg->init = 100; cfg->lpx = 60000; cfg->ta_get = 5 * cfg->lpx; cfg->ta_go = 4 * cfg->lpx; cfg->ta_sure = 2 * cfg->lpx; - cfg->wakeup = 1000000000; + cfg->wakeup = 1000; cfg->hs_clk_rate = hs_clk_rate; cfg->lanes = lanes; @@ -143,7 +143,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg) if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui)) return -EINVAL; - if (cfg->init < 100000000) + if (cfg->init < 100) return -EINVAL; if (cfg->lpx < 50000) @@ -158,7 +158,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg) if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx)) return -EINVAL; - if (cfg->wakeup < 1000000000) + if (cfg->wakeup < 1000) return -EINVAL; return 0; diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 19b05e824ee4..cb38f6e8614c 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -1112,14 +1112,4 @@ static int __init phy_core_init(void) return 0; } -module_init(phy_core_init); - -static void __exit phy_core_exit(void) -{ - class_destroy(phy_class); -} -module_exit(phy_core_exit); - -MODULE_DESCRIPTION("Generic PHY Framework"); -MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); -MODULE_LICENSE("GPL v2"); +device_initcall(phy_core_init); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index b4006818e1b6..08d6f6f7f039 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -687,6 +687,116 @@ static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02), }; +static const struct qmp_phy_init_tbl msm8998_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_INITVAL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x43), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x05), +}; + +static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x8a), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), +}; + + /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* phy-type - PCIE/UFS/USB */ @@ -1036,6 +1146,33 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { .no_pcs_sw_reset = true, }; +static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = msm8998_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8998_usb3_serdes_tbl), + .tx_tbl = msm8998_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8998_usb3_tx_tbl), + .rx_tbl = msm8998_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8998_usb3_rx_tbl), + .pcs_tbl = msm8998_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8998_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .mask_pcs_ready = PHYSTATUS, + + .is_dual_lane_phy = true, +}; + static void qcom_qmp_phy_configure(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -1736,6 +1873,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,msm8996-qmp-usb3-phy", .data = &msm8996_usb3phy_cfg, }, { + .compatible = "qcom,msm8998-qmp-ufs-phy", + .data = &sdm845_ufsphy_cfg, + }, { .compatible = "qcom,ipq8074-qmp-pcie-phy", .data = &ipq8074_pciephy_cfg, }, { @@ -1747,6 +1887,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sdm845-qmp-ufs-phy", .data = &sdm845_ufsphy_cfg, + }, { + .compatible = "qcom,msm8998-qmp-usb3-phy", + .data = &msm8998_usb3phy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index d201cc307151..a1b6cdee9a08 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -174,6 +174,7 @@ #define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4 #define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8 #define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc +#define QSERDES_V3_COM_INTEGLOOP_INITVAL 0x0d0 #define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8 #define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc #define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0 @@ -201,6 +202,7 @@ #define QSERDES_V3_COM_DEBUG_BUS2 0x170 #define QSERDES_V3_COM_DEBUG_BUS3 0x174 #define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178 +#define QSERDES_V3_COM_CMN_MODE 0x184 /* Only for QMP V3 PHY - TX registers */ #define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044 @@ -211,6 +213,7 @@ #define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4 /* Only for QMP V3 PHY - RX registers */ +#define QSERDES_V3_RX_UCDR_FO_GAIN 0x008 #define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c #define QSERDES_V3_RX_UCDR_SO_GAIN 0x014 #define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024 @@ -219,6 +222,7 @@ #define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030 #define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 #define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 #define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044 #define QSERDES_V3_RX_RX_TERM_BW 0x07c #define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 9177989f22d1..8fd7ce139772 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -152,6 +152,31 @@ static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), }; +static const unsigned int msm8998_regs_layout[] = { + [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, + [QUSB2PHY_PLL_STATUS] = 0x1a0, + [QUSB2PHY_PORT_TUNE1] = 0x23c, + [QUSB2PHY_PORT_TUNE2] = 0x240, + [QUSB2PHY_PORT_TUNE3] = 0x244, + [QUSB2PHY_PORT_TUNE4] = 0x248, + [QUSB2PHY_PORT_TEST1] = 0x24c, + [QUSB2PHY_PORT_TEST2] = 0x250, + [QUSB2PHY_PORT_POWERDOWN] = 0x210, + [QUSB2PHY_INTR_CTRL] = 0x22c, +}; + +static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = { + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x13), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a), + + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xa5), + QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x09), + + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19), +}; + static const unsigned int sdm845_regs_layout[] = { [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8, [QUSB2PHY_PLL_STATUS] = 0x1a0, @@ -221,6 +246,18 @@ static const struct qusb2_phy_cfg msm8996_phy_cfg = { .autoresume_en = BIT(3), }; +static const struct qusb2_phy_cfg msm8998_phy_cfg = { + .tbl = msm8998_init_tbl, + .tbl_num = ARRAY_SIZE(msm8998_init_tbl), + .regs = msm8998_regs_layout, + + .disable_ctrl = POWER_DOWN, + .mask_core_ready = CORE_READY_STATUS, + .has_pll_override = true, + .autoresume_en = BIT(0), + .update_tune1_with_efuse = true, +}; + static const struct qusb2_phy_cfg sdm845_phy_cfg = { .tbl = sdm845_init_tbl, .tbl_num = ARRAY_SIZE(sdm845_init_tbl), @@ -734,6 +771,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { .compatible = "qcom,msm8996-qusb2-phy", .data = &msm8996_phy_cfg, }, { + .compatible = "qcom,msm8998-qusb2-phy", + .data = &msm8998_phy_cfg, + }, { .compatible = "qcom,sdm845-qusb2-phy", .data = &sdm845_phy_cfg, }, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index 681644e43248..f798fb64de94 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -23,24 +23,7 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/delay.h> - -#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ -({ \ - ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ - might_sleep_if(timeout_us); \ - for (;;) { \ - (val) = readl(addr); \ - if (cond) \ - break; \ - if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ - (val) = readl(addr); \ - break; \ - } \ - if (sleep_us) \ - usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ - } \ - (cond) ? 0 : -ETIMEDOUT; \ -}) +#include <linux/iopoll.h> #define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ { \ diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 91fba60267a0..ba07121c3eff 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -55,16 +55,16 @@ enum rockchip_usb2phy_host_state { }; /** - * Different states involved in USB charger detection. - * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * enum usb_chg_state - Different states involved in USB charger detection. + * @USB_CHG_STATE_UNDEFINED: USB charger is not connected or detection * process is not yet started. - * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. - * USB_CHG_STATE_DCD_DONE Data pin contact is detected. - * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects + * @USB_CHG_STATE_WAIT_FOR_DCD: Waiting for Data pins contact. + * @USB_CHG_STATE_DCD_DONE: Data pin contact is detected. + * @USB_CHG_STATE_PRIMARY_DONE: Primary detection is completed (Detects * between SDP and DCP/CDP). - * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects - * between DCP and CDP). - * USB_CHG_STATE_DETECTED USB charger type is determined. + * @USB_CHG_STATE_SECONDARY_DONE: Secondary detection is completed (Detects + * between DCP and CDP). + * @USB_CHG_STATE_DETECTED: USB charger type is determined. */ enum usb_chg_state { USB_CHG_STATE_UNDEFINED = 0, @@ -94,7 +94,7 @@ struct usb2phy_reg { }; /** - * struct rockchip_chg_det_reg: usb charger detect registers + * struct rockchip_chg_det_reg - usb charger detect registers * @cp_det: charging port detected successfully. * @dcp_det: dedicated charging port detected successfully. * @dp_det: assert data pin connect successfully. @@ -120,7 +120,7 @@ struct rockchip_chg_det_reg { }; /** - * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. + * struct rockchip_usb2phy_port_cfg - usb-phy port configuration. * @phy_sus: phy suspend register. * @bvalid_det_en: vbus valid rise detection enable register. * @bvalid_det_st: vbus valid rise detection status register. @@ -148,10 +148,11 @@ struct rockchip_usb2phy_port_cfg { }; /** - * struct rockchip_usb2phy_cfg: usb-phy configuration. + * struct rockchip_usb2phy_cfg - usb-phy configuration. * @reg: the address offset of grf for usb-phy config. * @num_ports: specify how many ports that the phy has. * @clkout_ctl: keep on/turn off output clk of phy. + * @port_cfgs: usb-phy port configurations. * @chg_det: charger detection registers. */ struct rockchip_usb2phy_cfg { @@ -163,12 +164,10 @@ struct rockchip_usb2phy_cfg { }; /** - * struct rockchip_usb2phy_port: usb-phy port data. + * struct rockchip_usb2phy_port - usb-phy port data. + * @phy: generic phy. * @port_id: flag for otg port or host port. * @suspended: phy suspended flag. - * @utmi_avalid: utmi avalid status usage flag. - * true - use avalid to get vbus status - * flase - use bvalid to get vbus status * @vbus_attached: otg device vbus status. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @ls_irq: IRQ number assigned for linestate detection. @@ -178,7 +177,7 @@ struct rockchip_usb2phy_cfg { * @chg_work: charge detect work. * @otg_sm_work: OTG state machine work. * @sm_work: HOST state machine work. - * @phy_cfg: port register configuration, assigned by driver data. + * @port_cfg: port register configuration, assigned by driver data. * @event_nb: hold event notification callback. * @state: define OTG enumeration states before device reset. * @mode: the dr_mode of the controller. @@ -187,7 +186,6 @@ struct rockchip_usb2phy_port { struct phy *phy; unsigned int port_id; bool suspended; - bool utmi_avalid; bool vbus_attached; int bvalid_irq; int ls_irq; @@ -203,12 +201,13 @@ struct rockchip_usb2phy_port { }; /** - * struct rockchip_usb2phy: usb2.0 phy driver data. + * struct rockchip_usb2phy - usb2.0 phy driver data. + * @dev: pointer to device. * @grf: General Register Files regmap. * @usbgrf: USB General Register Files regmap. * @clk: clock struct of phy input clk. * @clk480m: clock struct of phy output clk. - * @clk_hw: clock struct of phy output clk management. + * @clk480m_hw: clock struct of phy output clk management. * @chg_state: states involved in USB charger detection. * @chg_type: USB charger types. * @dcd_retries: The retry count used to track Data contact @@ -542,12 +541,8 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) unsigned long delay; bool vbus_attach, sch_work, notify_charger; - if (rport->utmi_avalid) - vbus_attach = property_enabled(rphy->grf, - &rport->port_cfg->utmi_avalid); - else - vbus_attach = property_enabled(rphy->grf, - &rport->port_cfg->utmi_bvalid); + vbus_attach = property_enabled(rphy->grf, + &rport->port_cfg->utmi_bvalid); sch_work = false; notify_charger = false; @@ -1021,9 +1016,6 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); - rport->utmi_avalid = - of_property_read_bool(child_np, "rockchip,utmi-avalid"); - /* * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate * interrupts muxed together, so probe the otg-mux interrupt first, diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index c4709ed7fb0e..103efc456a12 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -33,12 +33,11 @@ config OMAP_CONTROL_PHY config OMAP_USB2 tristate "OMAP USB2 PHY Driver" - depends on ARCH_OMAP2PLUS + depends on ARCH_OMAP2PLUS || ARCH_K3 depends on USB_SUPPORT select GENERIC_PHY select USB_PHY - select OMAP_CONTROL_PHY - depends on OMAP_OCP2SCP + select OMAP_CONTROL_PHY if ARCH_OMAP2PLUS help Enable this to support the transceiver that is part of SOC. This driver takes care of all the PHY functionality apart from comparator. @@ -50,7 +49,6 @@ config TI_PIPE3 depends on ARCH_OMAP2PLUS || COMPILE_TEST select GENERIC_PHY select OMAP_CONTROL_PHY - depends on OMAP_OCP2SCP help Enable this to support the PIPE3 PHY that is part of TI SOCs. This driver takes care of all the PHY functionality apart from comparator. diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index fe909fd8144f..e871f2983a0e 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -36,6 +36,10 @@ #define USB2PHY_DISCON_BYP_LATCH (1 << 31) #define USB2PHY_ANA_CONFIG1 0x4c +#define AM654_USB2_OTG_PD BIT(8) +#define AM654_USB2_VBUS_DET_EN BIT(5) +#define AM654_USB2_VBUSVALID_DET_EN BIT(4) + /** * omap_usb2_set_comparator - links the comparator present in the sytem with * this phy @@ -135,9 +139,9 @@ static int omap_usb_power_on(struct phy *x) static int omap_usb2_disable_clocks(struct omap_usb *phy) { - clk_disable(phy->wkupclk); + clk_disable_unprepare(phy->wkupclk); if (!IS_ERR(phy->optclk)) - clk_disable(phy->optclk); + clk_disable_unprepare(phy->optclk); return 0; } @@ -146,14 +150,14 @@ static int omap_usb2_enable_clocks(struct omap_usb *phy) { int ret; - ret = clk_enable(phy->wkupclk); + ret = clk_prepare_enable(phy->wkupclk); if (ret < 0) { dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); goto err0; } if (!IS_ERR(phy->optclk)) { - ret = clk_enable(phy->optclk); + ret = clk_prepare_enable(phy->optclk); if (ret < 0) { dev_err(phy->dev, "Failed to enable optclk %d\n", ret); goto err1; @@ -245,6 +249,15 @@ static const struct usb_phy_data am437x_usb2_data = { .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, }; +static const struct usb_phy_data am654_usb2_data = { + .label = "am654_usb2", + .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, + .mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN | + AM654_USB2_VBUSVALID_DET_EN, + .power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN, + .power_off = AM654_USB2_OTG_PD, +}; + static const struct of_device_id omap_usb2_id_table[] = { { .compatible = "ti,omap-usb2", @@ -266,6 +279,10 @@ static const struct of_device_id omap_usb2_id_table[] = { .compatible = "ti,am437x-usb2", .data = &am437x_usb2_data, }, + { + .compatible = "ti,am654-usb2", + .data = &am654_usb2_data, + }, {}, }; MODULE_DEVICE_TABLE(of, omap_usb2_id_table); @@ -346,63 +363,72 @@ static int omap_usb2_probe(struct platform_device *pdev) } } - otg->set_host = omap_usb_set_host; - otg->set_peripheral = omap_usb_set_peripheral; - if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) - otg->set_vbus = omap_usb_set_vbus; - if (phy_data->flags & OMAP_USB2_HAS_START_SRP) - otg->start_srp = omap_usb_start_srp; - otg->usb_phy = &phy->phy; - - platform_set_drvdata(pdev, phy); - pm_runtime_enable(phy->dev); - - generic_phy = devm_phy_create(phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) { - pm_runtime_disable(phy->dev); - return PTR_ERR(generic_phy); - } - - phy_set_drvdata(generic_phy, phy); - omap_usb_power_off(generic_phy); - - phy_provider = devm_of_phy_provider_register(phy->dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - pm_runtime_disable(phy->dev); - return PTR_ERR(phy_provider); - } phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); if (IS_ERR(phy->wkupclk)) { - dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n"); + if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n", + PTR_ERR(phy->wkupclk)); phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); + if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); - pm_runtime_disable(phy->dev); + if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); return PTR_ERR(phy->wkupclk); } else { dev_warn(&pdev->dev, "found usb_phy_cm_clk32k, please fix DTS\n"); } } - clk_prepare(phy->wkupclk); phy->optclk = devm_clk_get(phy->dev, "refclk"); if (IS_ERR(phy->optclk)) { + if (PTR_ERR(phy->optclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); + if (IS_ERR(phy->optclk)) { - dev_dbg(&pdev->dev, - "unable to get usb_otg_ss_refclk960m\n"); + if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) { + dev_dbg(&pdev->dev, + "unable to get usb_otg_ss_refclk960m\n"); + } } else { dev_warn(&pdev->dev, "found usb_otg_ss_refclk960m, please fix DTS\n"); } } - if (!IS_ERR(phy->optclk)) - clk_prepare(phy->optclk); + otg->set_host = omap_usb_set_host; + otg->set_peripheral = omap_usb_set_peripheral; + if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) + otg->set_vbus = omap_usb_set_vbus; + if (phy_data->flags & OMAP_USB2_HAS_START_SRP) + otg->start_srp = omap_usb_start_srp; + otg->usb_phy = &phy->phy; + + platform_set_drvdata(pdev, phy); + pm_runtime_enable(phy->dev); + + generic_phy = devm_phy_create(phy->dev, NULL, &ops); + if (IS_ERR(generic_phy)) { + pm_runtime_disable(phy->dev); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, phy); + omap_usb_power_off(generic_phy); + + phy_provider = devm_of_phy_provider_register(phy->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + pm_runtime_disable(phy->dev); + return PTR_ERR(phy_provider); + } + usb_add_phy_dev(&phy->phy); @@ -413,9 +439,6 @@ static int omap_usb2_remove(struct platform_device *pdev) { struct omap_usb *phy = platform_get_drvdata(pdev); - clk_unprepare(phy->wkupclk); - if (!IS_ERR(phy->optclk)) - clk_unprepare(phy->optclk); usb_remove_phy(&phy->phy); pm_runtime_disable(phy->dev); |