diff options
Diffstat (limited to 'drivers/phy')
28 files changed, 1948 insertions, 132 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 2a436e607f99..ccad8809ecb1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,6 +15,13 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. +config PHY_BERLIN_USB + tristate "Marvell Berlin USB PHY Driver" + depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the USB PHY on Marvell Berlin SoCs. + config PHY_BERLIN_SATA tristate "Marvell Berlin SATA PHY driver" depends on ARCH_BERLIN && HAS_IOMEM && OF @@ -22,6 +29,12 @@ config PHY_BERLIN_SATA help Enable this to support the SATA PHY on Marvell Berlin SoCs. +config ARMADA375_USBCLUSTER_PHY + def_bool y + depends on MACH_ARMADA_375 || COMPILE_TEST + depends on OF + select GENERIC_PHY + config PHY_EXYNOS_MIPI_VIDEO tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM @@ -38,6 +51,14 @@ config PHY_MVEBU_SATA depends on OF select GENERIC_PHY +config PHY_MIPHY28LP + tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" + depends on ARCH_STI + select GENERIC_PHY + help + Enable this to support the miphy transceiver (for SATA/PCIE/USB3) + that is part of STMicroelectronics STiH407 SoC. + config PHY_MIPHY365X tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series" depends on ARCH_STI @@ -193,7 +214,7 @@ config PHY_EXYNOS5250_USB2 config PHY_EXYNOS5_USBDRD tristate "Exynos5 SoC series USB DRD PHY driver" - depends on ARCH_EXYNOS5 && OF + depends on ARCH_EXYNOS && OF depends on HAS_IOMEM depends on USB_DWC3_EXYNOS select GENERIC_PHY diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c4590fce082f..aa74f961e44e 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,11 +3,14 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o +obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o +obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o +obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c new file mode 100644 index 000000000000..ac7d99d01cb3 --- /dev/null +++ b/drivers/phy/phy-armada375-usb2.c @@ -0,0 +1,158 @@ +/* + * USB cluster support for Armada 375 platform. + * + * Copyright (C) 2014 Marvell + * + * 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. + */ + +#include <dt-bindings/phy/phy.h> +#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> + +#define USB2_PHY_CONFIG_DISABLE BIT(0) + +struct armada375_cluster_phy { + struct phy *phy; + void __iomem *reg; + bool use_usb3; + int phy_provided; +}; + +static int armada375_usb_phy_init(struct phy *phy) +{ + struct armada375_cluster_phy *cluster_phy; + u32 reg; + + cluster_phy = dev_get_drvdata(phy->dev.parent); + if (!cluster_phy) + return -ENODEV; + + reg = readl(cluster_phy->reg); + if (cluster_phy->use_usb3) + reg |= USB2_PHY_CONFIG_DISABLE; + else + reg &= ~USB2_PHY_CONFIG_DISABLE; + writel(reg, cluster_phy->reg); + + return 0; +} + +static struct phy_ops armada375_usb_phy_ops = { + .init = armada375_usb_phy_init, + .owner = THIS_MODULE, +}; + +/* + * Only one controller can use this PHY. We shouldn't have the case + * when two controllers want to use this PHY. But if this case occurs + * then we provide a phy to the first one and return an error for the + * next one. This error has also to be an error returned by + * devm_phy_optional_get() so different from ENODEV for USB2. In the + * USB3 case it still optional and we use ENODEV. + */ +static struct phy *armada375_usb_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); + + if (!cluster_phy) + return ERR_PTR(-ENODEV); + + /* + * Either the phy had never been requested and then the first + * usb claiming it can get it, or it had already been + * requested in this case, we only allow to use it with the + * same configuration. + */ + if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && + (cluster_phy->phy_provided != args->args[0]))) { + dev_err(dev, "This PHY has already been provided!\n"); + dev_err(dev, "Check your device tree, only one controller can use it\n."); + if (args->args[0] == PHY_TYPE_USB2) + return ERR_PTR(-EBUSY); + else + return ERR_PTR(-ENODEV); + } + + if (args->args[0] == PHY_TYPE_USB2) + cluster_phy->use_usb3 = false; + else if (args->args[0] == PHY_TYPE_USB3) + cluster_phy->use_usb3 = true; + else { + dev_err(dev, "Invalid PHY mode\n"); + return ERR_PTR(-ENODEV); + } + + /* Store which phy mode is used for next test */ + cluster_phy->phy_provided = args->args[0]; + + return cluster_phy->phy; +} + +static int armada375_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *phy; + struct phy_provider *phy_provider; + void __iomem *usb_cluster_base; + struct resource *res; + struct armada375_cluster_phy *cluster_phy; + + cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); + if (!cluster_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); + if (!usb_cluster_base) + return -ENOMEM; + + phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + cluster_phy->phy = phy; + cluster_phy->reg = usb_cluster_base; + + dev_set_drvdata(dev, cluster_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + armada375_usb_phy_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +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, + .driver = { + .of_match_table = of_usb_cluster_table, + .name = "armada-375-usb-cluster", + .owner = THIS_MODULE, + } +}; +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"); diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c index c1e0ca335c0e..ef2dc1aab2b9 100644 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ b/drivers/phy/phy-bcm-kona-usb2.c @@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); - gphy = devm_phy_create(dev, NULL, &ops, NULL); + gphy = devm_phy_create(dev, NULL, &ops); if (IS_ERR(gphy)) return PTR_ERR(gphy); diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c index 69ced52d72aa..099eee8851e5 100644 --- a/drivers/phy/phy-berlin-sata.c +++ b/drivers/phy/phy-berlin-sata.c @@ -30,7 +30,8 @@ #define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) #define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) -#define PHY_BASE 0x200 +#define BG2_PHY_BASE 0x080 +#define BG2Q_PHY_BASE 0x200 /* register 0x01 */ #define REF_FREF_SEL_25 BIT(0) @@ -61,15 +62,16 @@ struct phy_berlin_priv { struct clk *clk; struct phy_berlin_desc **phys; unsigned nphys; + u32 phy_base; }; -static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg, - u32 mask, u32 val) +static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, + u32 phy_base, u32 reg, u32 mask, u32 val) { u32 regval; /* select register */ - writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR); + writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); /* set bits */ regval = readl(ctrl_reg + PORT_VSR_DATA); @@ -103,17 +105,20 @@ static int phy_berlin_sata_power_on(struct phy *phy) writel(regval, priv->base + HOST_VSA_DATA); /* set PHY mode and ref freq to 25 MHz */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff, - REF_FREF_SEL_25 | PHY_MODE_SATA); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, + 0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA); /* set PHY up to 6 Gbps */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, + 0x0c00, PHY_GEN_MAX_6_0); /* set 40 bits width */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x23, 0xc00, DATA_BIT_WIDTH_40); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, + 0x0c00, DATA_BIT_WIDTH_40); /* use max pll rate */ - phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE); + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, + 0x0000, USE_MAX_PLL_RATE); /* set Gen3 controller speed */ regval = readl(ctrl_reg + PORT_SCR_CTL); @@ -218,6 +223,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!priv->phys) return -ENOMEM; + if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) + priv->phy_base = BG2_PHY_BASE; + else + priv->phy_base = BG2Q_PHY_BASE; + dev_set_drvdata(dev, priv); spin_lock_init(&priv->lock); @@ -239,7 +249,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) if (!phy_desc) return -ENOMEM; - phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL); + phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", phy_id); return PTR_ERR(phy); @@ -258,13 +268,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id phy_berlin_sata_of_match[] = { + { .compatible = "marvell,berlin2-sata-phy" }, { .compatible = "marvell,berlin2q-sata-phy" }, { }, }; diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c new file mode 100644 index 000000000000..c8a8d53a6ece --- /dev/null +++ b/drivers/phy/phy-berlin-usb.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Tenart <antoine.tenart@free-electrons.com> + * Jisheng Zhang <jszhang@marvell.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#define USB_PHY_PLL 0x04 +#define USB_PHY_PLL_CONTROL 0x08 +#define USB_PHY_TX_CTRL0 0x10 +#define USB_PHY_TX_CTRL1 0x14 +#define USB_PHY_TX_CTRL2 0x18 +#define USB_PHY_RX_CTRL 0x20 +#define USB_PHY_ANALOG 0x34 + +/* USB_PHY_PLL */ +#define CLK_REF_DIV(x) ((x) << 4) +#define FEEDBACK_CLK_DIV(x) ((x) << 8) + +/* USB_PHY_PLL_CONTROL */ +#define CLK_STABLE BIT(0) +#define PLL_CTRL_PIN BIT(1) +#define PLL_CTRL_REG BIT(2) +#define PLL_ON BIT(3) +#define PHASE_OFF_TOL_125 (0x0 << 5) +#define PHASE_OFF_TOL_250 BIT(5) +#define KVC0_CALIB (0x0 << 9) +#define KVC0_REG_CTRL BIT(9) +#define KVC0_HIGH (0x0 << 10) +#define KVC0_LOW (0x3 << 10) +#define CLK_BLK_EN BIT(13) + +/* USB_PHY_TX_CTRL0 */ +#define EXT_HS_RCAL_EN BIT(3) +#define EXT_FS_RCAL_EN BIT(4) +#define IMPCAL_VTH_DIV(x) ((x) << 5) +#define EXT_RS_RCAL_DIV(x) ((x) << 8) +#define EXT_FS_RCAL_DIV(x) ((x) << 12) + +/* USB_PHY_TX_CTRL1 */ +#define TX_VDD15_14 (0x0 << 4) +#define TX_VDD15_15 BIT(4) +#define TX_VDD15_16 (0x2 << 4) +#define TX_VDD15_17 (0x3 << 4) +#define TX_VDD12_VDD (0x0 << 6) +#define TX_VDD12_11 BIT(6) +#define TX_VDD12_12 (0x2 << 6) +#define TX_VDD12_13 (0x3 << 6) +#define LOW_VDD_EN BIT(8) +#define TX_OUT_AMP(x) ((x) << 9) + +/* USB_PHY_TX_CTRL2 */ +#define TX_CHAN_CTRL_REG(x) ((x) << 0) +#define DRV_SLEWRATE(x) ((x) << 4) +#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) +#define IMP_CAL_FS_HS_DLY_1 BIT(6) +#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) +#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) +#define FS_DRV_EN_MASK(x) ((x) << 8) +#define HS_DRV_EN_MASK(x) ((x) << 12) + +/* USB_PHY_RX_CTRL */ +#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) +#define PHASE_FREEZE_DLY_4_CL BIT(0) +#define ACK_LENGTH_8_CL (0x0 << 2) +#define ACK_LENGTH_12_CL BIT(2) +#define ACK_LENGTH_16_CL (0x2 << 2) +#define ACK_LENGTH_20_CL (0x3 << 2) +#define SQ_LENGTH_3 (0x0 << 4) +#define SQ_LENGTH_6 BIT(4) +#define SQ_LENGTH_9 (0x2 << 4) +#define SQ_LENGTH_12 (0x3 << 4) +#define DISCON_THRESHOLD_260 (0x0 << 6) +#define DISCON_THRESHOLD_270 BIT(6) +#define DISCON_THRESHOLD_280 (0x2 << 6) +#define DISCON_THRESHOLD_290 (0x3 << 6) +#define SQ_THRESHOLD(x) ((x) << 8) +#define LPF_COEF(x) ((x) << 12) +#define INTPL_CUR_10 (0x0 << 14) +#define INTPL_CUR_20 BIT(14) +#define INTPL_CUR_30 (0x2 << 14) +#define INTPL_CUR_40 (0x3 << 14) + +/* USB_PHY_ANALOG */ +#define ANA_PWR_UP BIT(1) +#define ANA_PWR_DOWN BIT(2) +#define V2I_VCO_RATIO(x) ((x) << 7) +#define R_ROTATE_90 (0x0 << 10) +#define R_ROTATE_0 BIT(10) +#define MODE_TEST_EN BIT(11) +#define ANA_TEST_DC_CTRL(x) ((x) << 12) + +#define to_phy_berlin_usb_priv(p) \ + container_of((p), struct phy_berlin_usb_priv, phy) + +static const u32 phy_berlin_pll_dividers[] = { + /* Berlin 2 */ + CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), + /* Berlin 2CD */ + CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), +}; + +struct phy_berlin_usb_priv { + void __iomem *base; + struct phy *phy; + struct reset_control *rst_ctrl; + u32 pll_divider; +}; + +static int phy_berlin_usb_power_on(struct phy *phy) +{ + struct phy_berlin_usb_priv *priv = dev_get_drvdata(phy->dev.parent); + + reset_control_reset(priv->rst_ctrl); + + writel(priv->pll_divider, + priv->base + USB_PHY_PLL); + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), + priv->base + USB_PHY_ANALOG); + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); + + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), + priv->base + USB_PHY_TX_CTRL0); + + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); + + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), + priv->base + USB_PHY_TX_CTRL0); + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); + + return 0; +} + +static struct phy_ops phy_berlin_usb_ops = { + .power_on = phy_berlin_usb_power_on, + .owner = THIS_MODULE, +}; + +static const struct of_device_id phy_berlin_sata_of_match[] = { + { + .compatible = "marvell,berlin2-usb-phy", + .data = &phy_berlin_pll_dividers[0], + }, + { + .compatible = "marvell,berlin2cd-usb-phy", + .data = &phy_berlin_pll_dividers[1], + }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); + +static int phy_berlin_usb_probe(struct platform_device *pdev) +{ + const struct of_device_id *match = + of_match_device(phy_berlin_sata_of_match, &pdev->dev); + struct phy_berlin_usb_priv *priv; + struct resource *res; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(priv->rst_ctrl)) + return PTR_ERR(priv->rst_ctrl); + + priv->pll_divider = *((u32 *)match->data); + + priv->phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); + if (IS_ERR(priv->phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(priv->phy); + } + + platform_set_drvdata(pdev, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static struct platform_driver phy_berlin_usb_driver = { + .probe = phy_berlin_usb_probe, + .driver = { + .name = "phy-berlin-usb", + .owner = THIS_MODULE, + .of_match_table = phy_berlin_sata_of_match, + }, +}; +module_platform_driver(phy_berlin_usb_driver); + +MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index ff5eec5af817..a12d35338313 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -26,6 +26,7 @@ static struct class *phy_class; static DEFINE_MUTEX(phy_provider_mutex); static LIST_HEAD(phy_provider_list); +static LIST_HEAD(phys); static DEFINE_IDA(phy_ida); static void devm_phy_release(struct device *dev, void *res) @@ -54,34 +55,79 @@ static int devm_phy_match(struct device *dev, void *res, void *match_data) return res == match_data; } -static struct phy *phy_lookup(struct device *device, const char *port) +/** + * phy_create_lookup() - allocate and register PHY/device association + * @phy: the phy of the association + * @con_id: connection ID string on device + * @dev_id: the device of the association + * + * Creates and registers phy_lookup entry. + */ +int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id) { - unsigned int count; - struct phy *phy; - struct device *dev; - struct phy_consumer *consumers; - struct class_dev_iter iter; + struct phy_lookup *pl; - class_dev_iter_init(&iter, phy_class, NULL, NULL); - while ((dev = class_dev_iter_next(&iter))) { - phy = to_phy(dev); + if (!phy || !dev_id || !con_id) + return -EINVAL; - if (!phy->init_data) - continue; - count = phy->init_data->num_consumers; - consumers = phy->init_data->consumers; - while (count--) { - if (!strcmp(consumers->dev_name, dev_name(device)) && - !strcmp(consumers->port, port)) { - class_dev_iter_exit(&iter); - return phy; - } - consumers++; + pl = kzalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return -ENOMEM; + + pl->dev_id = dev_id; + pl->con_id = con_id; + pl->phy = phy; + + mutex_lock(&phy_provider_mutex); + list_add_tail(&pl->node, &phys); + mutex_unlock(&phy_provider_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(phy_create_lookup); + +/** + * phy_remove_lookup() - find and remove PHY/device association + * @phy: the phy of the association + * @con_id: connection ID string on device + * @dev_id: the device of the association + * + * Finds and unregisters phy_lookup entry that was created with + * phy_create_lookup(). + */ +void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id) +{ + struct phy_lookup *pl; + + if (!phy || !dev_id || !con_id) + return; + + mutex_lock(&phy_provider_mutex); + list_for_each_entry(pl, &phys, node) + if (pl->phy == phy && !strcmp(pl->dev_id, dev_id) && + !strcmp(pl->con_id, con_id)) { + list_del(&pl->node); + kfree(pl); + break; } - } + mutex_unlock(&phy_provider_mutex); +} +EXPORT_SYMBOL_GPL(phy_remove_lookup); - class_dev_iter_exit(&iter); - return ERR_PTR(-ENODEV); +static struct phy *phy_find(struct device *dev, const char *con_id) +{ + const char *dev_id = dev_name(dev); + struct phy_lookup *p, *pl = NULL; + + mutex_lock(&phy_provider_mutex); + list_for_each_entry(p, &phys, node) + if (!strcmp(p->dev_id, dev_id) && !strcmp(p->con_id, con_id)) { + pl = p; + break; + } + mutex_unlock(&phy_provider_mutex); + + return pl ? pl->phy : ERR_PTR(-ENODEV); } static struct phy_provider *of_phy_provider_lookup(struct device_node *node) @@ -414,21 +460,13 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args { struct phy *phy; struct class_dev_iter iter; - struct device_node *node = dev->of_node; - struct device_node *child; class_dev_iter_init(&iter, phy_class, NULL, NULL); while ((dev = class_dev_iter_next(&iter))) { phy = to_phy(dev); - if (node != phy->dev.of_node) { - for_each_child_of_node(node, child) { - if (child == phy->dev.of_node) - goto phy_found; - } + if (args->np != phy->dev.of_node) continue; - } -phy_found: class_dev_iter_exit(&iter); return phy; } @@ -463,7 +501,7 @@ struct phy *phy_get(struct device *dev, const char *string) string); phy = _of_phy_get(dev->of_node, index); } else { - phy = phy_lookup(dev, string); + phy = phy_find(dev, string); } if (IS_ERR(phy)) return phy; @@ -588,13 +626,11 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get); * @dev: device that is creating the new phy * @node: device node of the phy * @ops: function pointers for performing phy operations - * @init_data: contains the list of PHY consumers or NULL * * Called to create a phy using phy framework. */ struct phy *phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data) + const struct phy_ops *ops) { int ret; int id; @@ -632,7 +668,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node, phy->dev.of_node = node ?: dev->of_node; phy->id = id; phy->ops = ops; - phy->init_data = init_data; ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id); if (ret) @@ -667,7 +702,6 @@ EXPORT_SYMBOL_GPL(phy_create); * @dev: device that is creating the new phy * @node: device node of the phy * @ops: function pointers for performing phy operations - * @init_data: contains the list of PHY consumers or NULL * * Creates a new PHY device adding it to the PHY class. * While at that, it also associates the device with the phy using devres. @@ -675,8 +709,7 @@ EXPORT_SYMBOL_GPL(phy_create); * then, devres data is freed. */ struct phy *devm_phy_create(struct device *dev, struct device_node *node, - const struct phy_ops *ops, - struct phy_init_data *init_data) + const struct phy_ops *ops) { struct phy **ptr, *phy; @@ -684,7 +717,7 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node, if (!ptr) return ERR_PTR(-ENOMEM); - phy = phy_create(dev, node, ops, init_data); + phy = phy_create(dev, node, ops); if (!IS_ERR(phy)) { *ptr = phy; devres_add(dev, ptr); diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c index 84f49e5a3f24..f86cbe68ddaf 100644 --- a/drivers/phy/phy-exynos-dp-video.c +++ b/drivers/phy/phy-exynos-dp-video.c @@ -112,7 +112,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev) match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); state->drvdata = match->data; - phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c index 6a9bef138617..943e0f88a120 100644 --- a/drivers/phy/phy-exynos-mipi-video.c +++ b/drivers/phy/phy-exynos-mipi-video.c @@ -137,7 +137,7 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev) for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, - &exynos_mipi_video_phy_ops, NULL); + &exynos_mipi_video_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c index f756aca871db..04374018425f 100644 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ b/drivers/phy/phy-exynos5-usbdrd.c @@ -141,6 +141,7 @@ struct exynos5_usbdrd_phy_drvdata { const struct exynos5_usbdrd_phy_config *phy_cfg; u32 pmu_offset_usbdrd0_phy; u32 pmu_offset_usbdrd1_phy; + bool has_common_clk_gate; }; /** @@ -148,6 +149,9 @@ struct exynos5_usbdrd_phy_drvdata { * @dev: pointer to device instance of this platform device * @reg_phy: usb phy controller register memory base * @clk: phy clock for register access + * @pipeclk: clock for pipe3 phy + * @utmiclk: clock for utmi+ phy + * @itpclk: clock for ITP generation * @drv_data: pointer to SoC level driver data structure * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY * instances each with its 'phy' and 'phy_cfg'. @@ -155,12 +159,16 @@ struct exynos5_usbdrd_phy_drvdata { * reference clocks' for SS and HS operations * @ref_clk: reference clock to PHY block from which PHY's * operational clocks are derived - * @ref_rate: rate of above reference clock + * vbus: VBUS regulator for phy + * vbus_boost: Boost regulator for VBUS present on few Exynos boards */ struct exynos5_usbdrd_phy { struct device *dev; void __iomem *reg_phy; struct clk *clk; + struct clk *pipeclk; + struct clk *utmiclk; + struct clk *itpclk; const struct exynos5_usbdrd_phy_drvdata *drv_data; struct phy_usb_instance { struct phy *phy; @@ -172,6 +180,7 @@ struct exynos5_usbdrd_phy { u32 extrefclk; struct clk *ref_clk; struct regulator *vbus; + struct regulator *vbus_boost; }; static inline @@ -447,13 +456,27 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); clk_prepare_enable(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_prepare_enable(phy_drd->pipeclk); + clk_prepare_enable(phy_drd->utmiclk); + clk_prepare_enable(phy_drd->itpclk); + } /* Enable VBUS supply */ + if (phy_drd->vbus_boost) { + ret = regulator_enable(phy_drd->vbus_boost); + if (ret) { + dev_err(phy_drd->dev, + "Failed to enable VBUS boost supply\n"); + goto fail_vbus; + } + } + if (phy_drd->vbus) { ret = regulator_enable(phy_drd->vbus); if (ret) { dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); - goto fail_vbus; + goto fail_vbus_boost; } } @@ -462,8 +485,17 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy) return 0; +fail_vbus_boost: + if (phy_drd->vbus_boost) + regulator_disable(phy_drd->vbus_boost); + fail_vbus: clk_disable_unprepare(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_disable_unprepare(phy_drd->itpclk); + clk_disable_unprepare(phy_drd->utmiclk); + clk_disable_unprepare(phy_drd->pipeclk); + } return ret; } @@ -481,8 +513,15 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy) /* Disable VBUS supply */ if (phy_drd->vbus) regulator_disable(phy_drd->vbus); + if (phy_drd->vbus_boost) + regulator_disable(phy_drd->vbus_boost); clk_disable_unprepare(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_disable_unprepare(phy_drd->itpclk); + clk_disable_unprepare(phy_drd->pipeclk); + clk_disable_unprepare(phy_drd->utmiclk); + } return 0; } @@ -506,6 +545,57 @@ static struct phy_ops exynos5_usbdrd_phy_ops = { .owner = THIS_MODULE, }; +static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) +{ + unsigned long ref_rate; + int ret; + + phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); + if (IS_ERR(phy_drd->clk)) { + dev_err(phy_drd->dev, "Failed to get phy clock\n"); + return PTR_ERR(phy_drd->clk); + } + + phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); + if (IS_ERR(phy_drd->ref_clk)) { + dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); + return PTR_ERR(phy_drd->ref_clk); + } + ref_rate = clk_get_rate(phy_drd->ref_clk); + + ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (ret) { + dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", + ref_rate); + return ret; + } + + if (!phy_drd->drv_data->has_common_clk_gate) { + phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); + if (IS_ERR(phy_drd->pipeclk)) { + dev_info(phy_drd->dev, + "PIPE3 phy operational clock not specified\n"); + phy_drd->pipeclk = NULL; + } + + phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); + if (IS_ERR(phy_drd->utmiclk)) { + dev_info(phy_drd->dev, + "UTMI phy operational clock not specified\n"); + phy_drd->utmiclk = NULL; + } + + phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); + if (IS_ERR(phy_drd->itpclk)) { + dev_info(phy_drd->dev, + "ITP clock from main OSC not specified\n"); + phy_drd->itpclk = NULL; + } + } + + return 0; +} + static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { { .id = EXYNOS5_DRDPHY_UTMI, @@ -525,11 +615,19 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, + .has_common_clk_gate = true, }; static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = true, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = false, }; static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { @@ -539,6 +637,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { }, { .compatible = "samsung,exynos5420-usbdrd-phy", .data = &exynos5420_usbdrd_phy + }, { + .compatible = "samsung,exynos7-usbdrd-phy", + .data = &exynos7_usbdrd_phy }, { }, }; @@ -555,7 +656,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) const struct exynos5_usbdrd_phy_drvdata *drv_data; struct regmap *reg_pmu; u32 pmu_offset; - unsigned long ref_rate; int i, ret; int channel; @@ -576,23 +676,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) drv_data = match->data; phy_drd->drv_data = drv_data; - phy_drd->clk = devm_clk_get(dev, "phy"); - if (IS_ERR(phy_drd->clk)) { - dev_err(dev, "Failed to get clock of phy controller\n"); - return PTR_ERR(phy_drd->clk); - } - - phy_drd->ref_clk = devm_clk_get(dev, "ref"); - if (IS_ERR(phy_drd->ref_clk)) { - dev_err(dev, "Failed to get reference clock of usbdrd phy\n"); - return PTR_ERR(phy_drd->ref_clk); - } - ref_rate = clk_get_rate(phy_drd->ref_clk); - - ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + ret = exynos5_usbdrd_phy_clk_handle(phy_drd); if (ret) { - dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", - ref_rate); + dev_err(dev, "Failed to initialize clocks\n"); return ret; } @@ -622,7 +708,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) break; } - /* Get Vbus regulator */ + /* Get Vbus regulators */ phy_drd->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(phy_drd->vbus)) { ret = PTR_ERR(phy_drd->vbus); @@ -633,12 +719,21 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) phy_drd->vbus = NULL; } + phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); + if (IS_ERR(phy_drd->vbus_boost)) { + ret = PTR_ERR(phy_drd->vbus_boost); + if (ret == -EPROBE_DEFER) + return ret; + + dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); + phy_drd->vbus_boost = NULL; + } + dev_vdbg(dev, "Creating usbdrd_phy phy\n"); for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { struct phy *phy = devm_phy_create(dev, NULL, - &exynos5_usbdrd_phy_ops, - NULL); + &exynos5_usbdrd_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c index 54cf4ae60d29..bc858cc800a1 100644 --- a/drivers/phy/phy-exynos5250-sata.c +++ b/drivers/phy/phy-exynos5250-sata.c @@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev) return ret; } - sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL); + sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); if (IS_ERR(sata_phy->phy)) { clk_disable_unprepare(sata_phy->phyclk); dev_err(dev, "failed to create PHY\n"); diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c index d5d978085c6d..34915b4202f1 100644 --- a/drivers/phy/phy-hix5hd2-sata.c +++ b/drivers/phy/phy-hix5hd2-sata.c @@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(priv->peri_ctrl)) priv->peri_ctrl = NULL; - phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create PHY\n"); return PTR_ERR(phy); @@ -164,10 +164,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy, priv); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id hix5hd2_sata_phy_of_match[] = { diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c new file mode 100644 index 000000000000..e34da13885e8 --- /dev/null +++ b/drivers/phy/phy-miphy28lp.c @@ -0,0 +1,1283 @@ +/* + * Copyright (C) 2014 STMicroelectronics + * + * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407). + * + * Author: Alexandre Torgue <alexandre.torgue@st.com> + * + * 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/platform_device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/clk.h> +#include <linux/phy/phy.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <dt-bindings/phy/phy.h> + +/* MiPHY registers */ +#define MIPHY_CONF_RESET 0x00 +#define RST_APPLI_SW BIT(0) +#define RST_CONF_SW BIT(1) +#define RST_MACRO_SW BIT(2) + +#define MIPHY_RESET 0x01 +#define RST_PLL_SW BIT(0) +#define RST_COMP_SW BIT(2) + +#define MIPHY_STATUS_1 0x02 +#define PHY_RDY BIT(0) +#define HFC_RDY BIT(1) +#define HFC_PLL BIT(2) + +#define MIPHY_CONTROL 0x04 +#define TERM_EN_SW BIT(2) +#define DIS_LINK_RST BIT(3) +#define AUTO_RST_RX BIT(4) +#define PX_RX_POL BIT(5) + +#define MIPHY_BOUNDARY_SEL 0x0a +#define TX_SEL BIT(6) +#define SSC_SEL BIT(4) +#define GENSEL_SEL BIT(0) + +#define MIPHY_BOUNDARY_1 0x0b +#define MIPHY_BOUNDARY_2 0x0c +#define SSC_EN_SW BIT(2) + +#define MIPHY_PLL_CLKREF_FREQ 0x0d +#define MIPHY_SPEED 0x0e +#define TX_SPDSEL_80DEC 0 +#define TX_SPDSEL_40DEC 1 +#define TX_SPDSEL_20DEC 2 +#define RX_SPDSEL_80DEC 0 +#define RX_SPDSEL_40DEC (1 << 2) +#define RX_SPDSEL_20DEC (2 << 2) + +#define MIPHY_CONF 0x0f +#define MIPHY_CTRL_TEST_SEL 0x20 +#define MIPHY_CTRL_TEST_1 0x21 +#define MIPHY_CTRL_TEST_2 0x22 +#define MIPHY_CTRL_TEST_3 0x23 +#define MIPHY_CTRL_TEST_4 0x24 +#define MIPHY_FEEDBACK_TEST 0x25 +#define MIPHY_DEBUG_BUS 0x26 +#define MIPHY_DEBUG_STATUS_MSB 0x27 +#define MIPHY_DEBUG_STATUS_LSB 0x28 +#define MIPHY_PWR_RAIL_1 0x29 +#define MIPHY_PWR_RAIL_2 0x2a +#define MIPHY_SYNCHAR_CONTROL 0x30 + +#define MIPHY_COMP_FSM_1 0x3a +#define COMP_START BIT(6) + +#define MIPHY_COMP_FSM_6 0x3f +#define COMP_DONE BIT(7) + +#define MIPHY_COMP_POSTP 0x42 +#define MIPHY_TX_CTRL_1 0x49 +#define TX_REG_STEP_0V 0 +#define TX_REG_STEP_P_25MV 1 +#define TX_REG_STEP_P_50MV 2 +#define TX_REG_STEP_N_25MV 7 +#define TX_REG_STEP_N_50MV 6 +#define TX_REG_STEP_N_75MV 5 + +#define MIPHY_TX_CTRL_2 0x4a +#define TX_SLEW_SW_40_PS 0 +#define TX_SLEW_SW_80_PS 1 +#define TX_SLEW_SW_120_PS 2 + +#define MIPHY_TX_CTRL_3 0x4b +#define MIPHY_TX_CAL_MAN 0x4e +#define TX_SLEW_CAL_MAN_EN BIT(0) + +#define MIPHY_TST_BIAS_BOOST_2 0x62 +#define MIPHY_BIAS_BOOST_1 0x63 +#define MIPHY_BIAS_BOOST_2 0x64 +#define MIPHY_RX_DESBUFF_FDB_2 0x67 +#define MIPHY_RX_DESBUFF_FDB_3 0x68 +#define MIPHY_SIGDET_COMPENS1 0x69 +#define MIPHY_SIGDET_COMPENS2 0x6a +#define MIPHY_JITTER_PERIOD 0x6b +#define MIPHY_JITTER_AMPLITUDE_1 0x6c +#define MIPHY_JITTER_AMPLITUDE_2 0x6d +#define MIPHY_JITTER_AMPLITUDE_3 0x6e +#define MIPHY_RX_K_GAIN 0x78 +#define MIPHY_RX_BUFFER_CTRL 0x7a +#define VGA_GAIN BIT(0) +#define EQ_DC_GAIN BIT(2) +#define EQ_BOOST_GAIN BIT(3) + +#define MIPHY_RX_VGA_GAIN 0x7b +#define MIPHY_RX_EQU_GAIN_1 0x7f +#define MIPHY_RX_EQU_GAIN_2 0x80 +#define MIPHY_RX_EQU_GAIN_3 0x81 +#define MIPHY_RX_CAL_CTRL_1 0x97 +#define MIPHY_RX_CAL_CTRL_2 0x98 + +#define MIPHY_RX_CAL_OFFSET_CTRL 0x99 +#define CAL_OFFSET_VGA_64 (0x03 << 0) +#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2) +#define VGA_OFFSET_POLARITY BIT(4) +#define OFFSET_COMPENSATION_EN BIT(6) + +#define MIPHY_RX_CAL_VGA_STEP 0x9a +#define MIPHY_RX_CAL_EYE_MIN 0x9d +#define MIPHY_RX_CAL_OPT_LENGTH 0x9f +#define MIPHY_RX_LOCK_CTRL_1 0xc1 +#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2 +#define MIPHY_RX_LOCK_STEP 0xc4 + +#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9 +#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca +#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb +#define MIPHY_RX_SIGDET_DATA_SEL 0xcc +#define EN_ULTRA_LOW_POWER BIT(0) +#define EN_FIRST_HALF BIT(1) +#define EN_SECOND_HALF BIT(2) +#define EN_DIGIT_SIGNAL_CHECK BIT(3) + +#define MIPHY_RX_POWER_CTRL_1 0xcd +#define MIPHY_RX_POWER_CTRL_2 0xce +#define MIPHY_PLL_CALSET_CTRL 0xd3 +#define MIPHY_PLL_CALSET_1 0xd4 +#define MIPHY_PLL_CALSET_2 0xd5 +#define MIPHY_PLL_CALSET_3 0xd6 +#define MIPHY_PLL_CALSET_4 0xd7 +#define MIPHY_PLL_SBR_1 0xe3 +#define SET_NEW_CHANGE BIT(1) + +#define MIPHY_PLL_SBR_2 0xe4 +#define MIPHY_PLL_SBR_3 0xe5 +#define MIPHY_PLL_SBR_4 0xe6 +#define MIPHY_PLL_COMMON_MISC_2 0xe9 +#define START_ACT_FILT BIT(6) + +#define MIPHY_PLL_SPAREIN 0xeb + +/* + * On STiH407 the glue logic can be different among MiPHY devices; for example: + * MiPHY0: OSC_FORCE_EXT means: + * 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1 + * MiPHY1: OSC_FORCE_EXT means: + * 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1 + * Some devices have not the possibility to check if the osc is ready. + */ +#define MIPHY_OSC_FORCE_EXT BIT(3) +#define MIPHY_OSC_RDY BIT(5) + +#define MIPHY_CTRL_MASK 0x0f +#define MIPHY_CTRL_DEFAULT 0 +#define MIPHY_CTRL_SYNC_D_EN BIT(2) + +/* SATA / PCIe defines */ +#define SATA_CTRL_MASK 0x07 +#define PCIE_CTRL_MASK 0xff +#define SATA_CTRL_SELECT_SATA 1 +#define SATA_CTRL_SELECT_PCIE 0 +#define SYSCFG_PCIE_PCIE_VAL 0x80 +#define SATA_SPDMODE 1 + +#define MIPHY_SATA_BANK_NB 3 +#define MIPHY_PCIE_BANK_NB 2 + +struct miphy28lp_phy { + struct phy *phy; + struct miphy28lp_dev *phydev; + void __iomem *base; + void __iomem *pipebase; + + bool osc_force_ext; + bool osc_rdy; + bool px_rx_pol_inv; + bool ssc; + bool tx_impedance; + + struct reset_control *miphy_rst; + + u32 sata_gen; + + /* Sysconfig registers offsets needed to configure the device */ + u32 syscfg_miphy_ctrl; + u32 syscfg_miphy_status; + u32 syscfg_pci; + u32 syscfg_sata; + u8 type; +}; + +struct miphy28lp_dev { + struct device *dev; + struct regmap *regmap; + struct mutex miphy_mutex; + struct miphy28lp_phy **phys; +}; + +struct miphy_initval { + u16 reg; + u16 val; +}; + +enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; + +static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" }; + +struct pll_ratio { + int clk_ref; + int calset_1; + int calset_2; + int calset_3; + int calset_4; + int cal_ctrl; +}; + +static struct pll_ratio sata_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xc8, + .calset_2 = 0x00, + .calset_3 = 0x00, + .calset_4 = 0x00, + .cal_ctrl = 0x00, +}; + +static struct pll_ratio pcie_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xa6, + .calset_2 = 0xaa, + .calset_3 = 0xaa, + .calset_4 = 0x00, + .cal_ctrl = 0x00, +}; + +static struct pll_ratio usb3_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xa6, + .calset_2 = 0xaa, + .calset_3 = 0xaa, + .calset_4 = 0x04, + .cal_ctrl = 0x00, +}; + +struct miphy28lp_pll_gen { + int bank; + int speed; + int bias_boost_1; + int bias_boost_2; + int tx_ctrl_1; + int tx_ctrl_2; + int tx_ctrl_3; + int rx_k_gain; + int rx_vga_gain; + int rx_equ_gain_1; + int rx_equ_gain_2; + int rx_equ_gain_3; + int rx_buff_ctrl; +}; + +static struct miphy28lp_pll_gen sata_pll_gen[] = { + { + .bank = 0x00, + .speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0x53, + .tx_ctrl_3 = 0x00, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, + { + .bank = 0x01, + .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0x72, + .tx_ctrl_3 = 0x20, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, + { + .bank = 0x02, + .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0xc0, + .tx_ctrl_3 = 0x20, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, +}; + +static struct miphy28lp_pll_gen pcie_pll_gen[] = { + { + .bank = 0x00, + .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xa5, + .tx_ctrl_1 = TX_REG_STEP_N_25MV, + .tx_ctrl_2 = 0x71, + .tx_ctrl_3 = 0x60, + .rx_k_gain = 0x98, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x79, + .rx_equ_gain_2 = 0x56, + }, + { + .bank = 0x01, + .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xa5, + .tx_ctrl_1 = TX_REG_STEP_N_25MV, + .tx_ctrl_2 = 0x70, + .tx_ctrl_3 = 0x60, + .rx_k_gain = 0xcc, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x78, + .rx_equ_gain_2 = 0x07, + }, +}; + +static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) +{ + void *base = miphy_phy->base; + u8 val; + + /* Putting Macro in reset */ + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + + val = RST_APPLI_SW | RST_CONF_SW; + writeb_relaxed(val, base + MIPHY_CONF_RESET); + + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + + /* Bringing the MIPHY-CPU registers out of reset */ + if (miphy_phy->type == PHY_TYPE_PCIE) { + val = AUTO_RST_RX | TERM_EN_SW; + writeb_relaxed(val, base + MIPHY_CONTROL); + } else { + val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST; + writeb_relaxed(val, base + MIPHY_CONTROL); + } +} + +static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, + struct pll_ratio *pll_ratio) +{ + void *base = miphy_phy->base; + u8 val; + + /* Applying PLL Settings */ + writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN); + writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); + + /* PLL Ratio */ + writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1); + writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2); + writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3); + writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4); + writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL); + + writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL); + + val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN; + writeb_relaxed(val, base + MIPHY_TX_CAL_MAN); + + val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; + + if (miphy_phy->type != PHY_TYPE_SATA) + val |= OFFSET_COMPENSATION_EN; + + writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); + + if (miphy_phy->type == PHY_TYPE_USB3) { + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL); + + val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF; + writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL); + } + +} + +static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int i; + + for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) { + struct miphy28lp_pll_gen *gen = &sata_pll_gen[i]; + + /* Banked settings */ + writeb_relaxed(gen->bank, base + MIPHY_CONF); + writeb_relaxed(gen->speed, base + MIPHY_SPEED); + writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); + + /* TX buffer Settings */ + writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); + writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); + + /* RX Buffer Settings */ + writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); + writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); + writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3); + } +} + +static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int i; + + for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) { + struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i]; + + /* Banked settings */ + writeb_relaxed(gen->bank, base + MIPHY_CONF); + writeb_relaxed(gen->speed, base + MIPHY_SPEED); + writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); + + /* TX buffer Settings */ + writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1); + writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); + writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); + + writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN); + + /* RX Buffer Settings */ + writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); + writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); + } +} + +static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) +{ + unsigned long finish = jiffies + 5 * HZ; + u8 val; + + /* Waiting for Compensation to complete */ + do { + val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); + + if (time_after_eq(jiffies, finish)) + return -EBUSY; + cpu_relax(); + } while (!(val & COMP_DONE)); + + return 0; +} + + +static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy, + struct pll_ratio *pll_ratio) +{ + void __iomem *base = miphy_phy->base; + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); + writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); + + if (miphy_phy->type == PHY_TYPE_PCIE) + writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_RESET); + writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* TX compensation offset to re-center TX impedance */ + writeb_relaxed(0x00, base + MIPHY_COMP_POSTP); + + if (miphy_phy->type == PHY_TYPE_PCIE) + return miphy28lp_wait_compensation(miphy_phy); + + return 0; +} + +static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* MIPHY Reset */ + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET); + + val = RST_COMP_SW | RST_PLL_SW; + writeb_relaxed(val, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ); + writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); + writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); + writeb_relaxed(0x00, base + MIPHY_RESET); + writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1); + writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2); + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS); + writeb_relaxed(0x00, base + MIPHY_CONF); +} + +static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Compensate Tx impedance to avoid out of range values */ + /* + * Enable the SSC on PLL for all banks + * SSC Modulation @ 31 KHz and 4000 ppm modulation amp + */ + val = readb_relaxed(base + MIPHY_BOUNDARY_2); + val |= SSC_EN_SW; + writeb_relaxed(val, base + MIPHY_BOUNDARY_2); + + val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); + val |= SSC_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + for (val = 0; val < MIPHY_SATA_BANK_NB; val++) { + writeb_relaxed(val, base + MIPHY_CONF); + + /* Add value to each reference clock cycle */ + /* and define the period length of the SSC */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + } +} + +static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Compensate Tx impedance to avoid out of range values */ + /* + * Enable the SSC on PLL for all banks + * SSC Modulation @ 31 KHz and 4000 ppm modulation amp + */ + val = readb_relaxed(base + MIPHY_BOUNDARY_2); + val |= SSC_EN_SW; + writeb_relaxed(val, base + MIPHY_BOUNDARY_2); + + val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); + val |= SSC_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) { + writeb_relaxed(val, base + MIPHY_CONF); + + /* Validate Step component */ + writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); + + /* Validate Period component */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + } +} + +static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy) +{ + /* Compensate Tx impedance to avoid out of range values */ + writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP); +} + +static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int err; + u8 val; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio); + + /* Banked settings Gen1/Gen2/Gen3 */ + miphy28lp_sata_config_gen(miphy_phy); + + /* Power control */ + /* Input bridge enable, manual input bridge control */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + + /* Macro out of reset */ + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio); + if (err) + return err; + + if (miphy_phy->px_rx_pol_inv) { + /* Invert Rx polarity */ + val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL); + val |= PX_RX_POL; + writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); + } + + if (miphy_phy->ssc) + miphy_sata_tune_ssc(miphy_phy); + + if (miphy_phy->tx_impedance) + miphy_tune_tx_impedance(miphy_phy); + + return 0; +} + +static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int err; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio); + + /* Banked settings Gen1/Gen2 */ + miphy28lp_pcie_config_gen(miphy_phy); + + /* Power control */ + /* Input bridge enable, manual input bridge control */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + + /* Macro out of reset */ + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio); + if (err) + return err; + + if (miphy_phy->ssc) + miphy_pcie_tune_ssc(miphy_phy); + + if (miphy_phy->tx_impedance) + miphy_tune_tx_impedance(miphy_phy); + + return 0; +} + + +static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio); + + /* Writing The Speed Rate */ + writeb_relaxed(0x00, base + MIPHY_CONF); + + val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC; + writeb_relaxed(val, base + MIPHY_SPEED); + + /* RX Channel compensation and calibration */ + writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT); + writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1); + writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2); + + val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY | + CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; + writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); + writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP); + writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH); + + val = EQ_DC_GAIN | VGA_GAIN; + writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL); + + /* TX compensation offset to re-center TX impedance */ + writeb_relaxed(0x02, base + MIPHY_COMP_POSTP); + + /* Enable GENSEL_SEL and SSC */ + /* TX_SEL=0 swing preemp forced by pipe registres */ + val = SSC_SEL | GENSEL_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + /* MIPHY Bias boost */ + writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2); + + /* SSC modulation */ + writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2); + + /* MIPHY TX control */ + writeb_relaxed(0x00, base + MIPHY_CONF); + + /* Validate Step component */ + writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4); + + /* Validate Period component */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* Rx PI controller settings */ + writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN); + + /* MIPHY RX input bridge control */ + /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1); + writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2); + + /* MIPHY Reset for usb3 */ + miphy28_usb3_miphy_reset(miphy_phy); +} + +static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) +{ + unsigned long finish = jiffies + 5 * HZ; + u8 mask = HFC_PLL | HFC_RDY; + u8 val; + + /* + * For PCIe and USB3 check only that PLL and HFC are ready + * For SATA check also that phy is ready! + */ + if (miphy_phy->type == PHY_TYPE_SATA) + mask |= PHY_RDY; + + do { + val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); + if ((val & mask) != mask) + cpu_relax(); + else + return 0; + } while (!time_after_eq(jiffies, finish)); + + return -EBUSY; +} + +static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + unsigned long finish = jiffies + 5 * HZ; + u32 val; + + if (!miphy_phy->osc_rdy) + return 0; + + if (!miphy_phy->syscfg_miphy_status) + return -EINVAL; + + do { + regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status, + &val); + + if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) + cpu_relax(); + else + return 0; + } while (!time_after_eq(jiffies, finish)); + + return -EBUSY; +} + +static int miphy28lp_get_resource_byname(struct device_node *child, + char *rname, struct resource *res) +{ + int index; + + index = of_property_match_string(child, "reg-names", rname); + if (index < 0) + return -ENODEV; + + return of_address_to_resource(child, index, res); +} + +static int miphy28lp_get_one_addr(struct device *dev, + struct device_node *child, char *rname, + void __iomem **base) +{ + struct resource res; + int ret; + + ret = miphy28lp_get_resource_byname(child, rname, &res); + if (!ret) { + *base = devm_ioremap(dev, res.start, resource_size(&res)); + if (!*base) { + dev_err(dev, "failed to ioremap %s address region\n" + , rname); + return -ENOENT; + } + } + + return 0; +} + +/* MiPHY reset and sysconf setup */ +static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) +{ + int err; + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + + if (!miphy_phy->syscfg_miphy_ctrl) + return -EINVAL; + + err = reset_control_assert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + if (miphy_phy->osc_force_ext) + miphy_val |= MIPHY_OSC_FORCE_EXT; + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl, + MIPHY_CTRL_MASK, miphy_val); + + err = reset_control_deassert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + return miphy_osc_is_ready(miphy_phy); +} + +static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err, sata_conf = SATA_CTRL_SELECT_SATA; + + if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci) + || (!miphy_phy->base)) + return -EINVAL; + + dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); + + /* Configure the glue-logic */ + sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + SATA_CTRL_MASK, sata_conf); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); + + if (err) { + dev_err(miphy_dev->dev, "SATA phy setup failed\n"); + return err; + } + + /* initialize miphy */ + miphy28lp_configure_sata(miphy_phy); + + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci) + || (!miphy_phy->base) || (!miphy_phy->pipebase)) + return -EINVAL; + + dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); + + /* Configure the glue-logic */ + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata, + SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci, + PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); + + if (err) { + dev_err(miphy_dev->dev, "PCIe phy setup failed\n"); + return err; + } + + /* initialize miphy */ + err = miphy28lp_configure_pcie(miphy_phy); + if (err) + return err; + + /* PIPE Wrapper Configuration */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */ + writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */ + writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */ + writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */ + + /* Wait for phy_ready */ + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + if ((!miphy_phy->base) || (!miphy_phy->pipebase)) + return -EINVAL; + + dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN); + if (err) { + dev_err(miphy_dev->dev, "USB3 phy setup failed\n"); + return err; + } + + /* initialize miphy */ + miphy28lp_configure_usb3(miphy_phy); + + /* PIPE Wrapper Configuration */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x23); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x24); + writeb_relaxed(0x68, miphy_phy->pipebase + 0x26); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x27); + writeb_relaxed(0x18, miphy_phy->pipebase + 0x29); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a); + + /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */ + writeb_relaxed(0X67, miphy_phy->pipebase + 0x68); + writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f); + + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init(struct phy *phy) +{ + struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy); + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int ret; + + mutex_lock(&miphy_dev->miphy_mutex); + + switch (miphy_phy->type) { + + case PHY_TYPE_SATA: + ret = miphy28lp_init_sata(miphy_phy); + break; + case PHY_TYPE_PCIE: + ret = miphy28lp_init_pcie(miphy_phy); + break; + case PHY_TYPE_USB3: + ret = miphy28lp_init_usb3(miphy_phy); + break; + default: + return -EINVAL; + } + + mutex_unlock(&miphy_dev->miphy_mutex); + + return ret; +} + +static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + struct device_node *phynode = miphy_phy->phy->dev.of_node; + int err; + + if ((miphy_phy->type != PHY_TYPE_SATA) && + (miphy_phy->type != PHY_TYPE_PCIE) && + (miphy_phy->type != PHY_TYPE_USB3)) { + return -EINVAL; + } + + err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, + PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA], + &miphy_phy->base); + if (err) + return err; + + if ((miphy_phy->type == PHY_TYPE_PCIE) || + (miphy_phy->type == PHY_TYPE_USB3)) { + err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew", + &miphy_phy->pipebase); + if (err) + return err; + } + + return 0; +} + +static struct phy *miphy28lp_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev); + struct miphy28lp_phy *miphy_phy = NULL; + struct device_node *phynode = args->np; + int ret, index = 0; + + if (!of_device_is_available(phynode)) { + dev_warn(dev, "Requested PHY is disabled\n"); + return ERR_PTR(-ENODEV); + } + + if (args->args_count != 1) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < of_get_child_count(dev->of_node); index++) + if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { + miphy_phy = miphy_dev->phys[index]; + break; + } + + if (!miphy_phy) { + dev_err(dev, "Failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + miphy_phy->type = args->args[0]; + + ret = miphy28lp_get_addr(miphy_phy); + if (ret < 0) + return ERR_PTR(ret); + + return miphy_phy->phy; +} + +static struct phy_ops miphy28lp_ops = { + .init = miphy28lp_init, +}; + +static int miphy28lp_probe_resets(struct device_node *node, + struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + miphy_phy->miphy_rst = of_reset_control_get(node, "miphy-sw-rst"); + + if (IS_ERR(miphy_phy->miphy_rst)) { + dev_err(miphy_dev->dev, + "miphy soft reset control not defined\n"); + return PTR_ERR(miphy_phy->miphy_rst); + } + + err = reset_control_deassert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + return 0; +} + +static int miphy28lp_of_probe(struct device_node *np, + struct miphy28lp_phy *miphy_phy) +{ + struct resource res; + + miphy_phy->osc_force_ext = + of_property_read_bool(np, "st,osc-force-ext"); + + miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy"); + + miphy_phy->px_rx_pol_inv = + of_property_read_bool(np, "st,px_rx_pol_inv"); + + miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); + + miphy_phy->tx_impedance = + of_property_read_bool(np, "st,tx-impedance-comp"); + + of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); + if (!miphy_phy->sata_gen) + miphy_phy->sata_gen = SATA_GEN1; + + if (!miphy28lp_get_resource_byname(np, "miphy-ctrl-glue", &res)) + miphy_phy->syscfg_miphy_ctrl = res.start; + + if (!miphy28lp_get_resource_byname(np, "miphy-status-glue", &res)) + miphy_phy->syscfg_miphy_status = res.start; + + if (!miphy28lp_get_resource_byname(np, "pcie-glue", &res)) + miphy_phy->syscfg_pci = res.start; + + if (!miphy28lp_get_resource_byname(np, "sata-glue", &res)) + miphy_phy->syscfg_sata = res.start; + + + return 0; +} + +static int miphy28lp_probe(struct platform_device *pdev) +{ + struct device_node *child, *np = pdev->dev.of_node; + struct miphy28lp_dev *miphy_dev; + struct phy_provider *provider; + struct phy *phy; + int chancount, port = 0; + int ret; + + miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); + if (!miphy_dev) + return -ENOMEM; + + chancount = of_get_child_count(np); + miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount, + GFP_KERNEL); + if (!miphy_dev->phys) + return -ENOMEM; + + miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(miphy_dev->regmap)) { + dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(miphy_dev->regmap); + } + + miphy_dev->dev = &pdev->dev; + + dev_set_drvdata(&pdev->dev, miphy_dev); + + mutex_init(&miphy_dev->miphy_mutex); + + for_each_child_of_node(np, child) { + struct miphy28lp_phy *miphy_phy; + + miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), + GFP_KERNEL); + if (!miphy_phy) + return -ENOMEM; + + miphy_dev->phys[port] = miphy_phy; + + phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + miphy_dev->phys[port]->phy = phy; + miphy_dev->phys[port]->phydev = miphy_dev; + + ret = miphy28lp_of_probe(child, miphy_phy); + if (ret) + return ret; + + ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]); + if (ret) + return ret; + + phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + + } + + provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + return 0; +} + +static const struct of_device_id miphy28lp_of_match[] = { + {.compatible = "st,miphy28lp-phy", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, miphy28lp_of_match); + +static struct platform_driver miphy28lp_driver = { + .probe = miphy28lp_probe, + .driver = { + .name = "miphy28lp-phy", + .owner = THIS_MODULE, + .of_match_table = miphy28lp_of_match, + } +}; + +module_platform_driver(miphy28lp_driver); + +MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 801afaf2d449..6ab43a814ad2 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -593,7 +593,7 @@ static int miphy365x_probe(struct platform_device *pdev) miphy_dev->phys[port] = miphy_phy; - phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL); + phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops); if (IS_ERR(phy)) { dev_err(&pdev->dev, "failed to create PHY\n"); return PTR_ERR(phy); @@ -610,10 +610,7 @@ static int miphy365x_probe(struct platform_device *pdev) } provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); - if (IS_ERR(provider)) - return PTR_ERR(provider); - - return 0; + return PTR_ERR_OR_ZERO(provider); } static const struct of_device_id miphy365x_of_match[] = { diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c index d395558cb12e..03b94f92e6f1 100644 --- a/drivers/phy/phy-mvebu-sata.c +++ b/drivers/phy/phy-mvebu-sata.c @@ -101,7 +101,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev) if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL); + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); if (IS_ERR(phy)) return PTR_ERR(phy); diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c index 8c842980834a..4e489a8850e5 100644 --- a/drivers/phy/phy-omap-usb2.c +++ b/drivers/phy/phy-omap-usb2.c @@ -60,7 +60,7 @@ EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) { - struct omap_usb *phy = phy_to_omapusb(otg->phy); + struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); if (!phy->comparator) return -ENODEV; @@ -70,7 +70,7 @@ static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) static int omap_usb_start_srp(struct usb_otg *otg) { - struct omap_usb *phy = phy_to_omapusb(otg->phy); + struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); if (!phy->comparator) return -ENODEV; @@ -80,11 +80,9 @@ static int omap_usb_start_srp(struct usb_otg *otg) static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) { - struct usb_phy *phy = otg->phy; - otg->host = host; if (!host) - phy->state = OTG_STATE_UNDEFINED; + otg->state = OTG_STATE_UNDEFINED; return 0; } @@ -92,11 +90,9 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) static int omap_usb_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { - struct usb_phy *phy = otg->phy; - otg->gadget = gadget; if (!gadget) - phy->state = OTG_STATE_UNDEFINED; + otg->state = OTG_STATE_UNDEFINED; return 0; } @@ -255,17 +251,19 @@ static int omap_usb2_probe(struct platform_device *pdev) otg->set_vbus = omap_usb_set_vbus; if (phy_data->flags & OMAP_USB2_HAS_START_SRP) otg->start_srp = omap_usb_start_srp; - otg->phy = &phy->phy; + otg->usb_phy = &phy->phy; platform_set_drvdata(pdev, phy); + pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); - if (IS_ERR(generic_phy)) + 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); - pm_runtime_enable(phy->dev); phy_provider = devm_of_phy_provider_register(phy->dev, of_phy_simple_xlate); if (IS_ERR(phy_provider)) { diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c index 7b3ddfb65898..4b243f7a10e4 100644 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ b/drivers/phy/phy-qcom-apq8064-sata.c @@ -228,8 +228,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); - generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops, - NULL); + generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops); if (IS_ERR(generic_phy)) { dev_err(dev, "%s: failed to create phy\n", __func__); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c index 759b0bf5b6b3..6f2fe2627916 100644 --- a/drivers/phy/phy-qcom-ipq806x-sata.c +++ b/drivers/phy/phy-qcom-ipq806x-sata.c @@ -150,8 +150,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) if (IS_ERR(phy->mmio)) return PTR_ERR(phy->mmio); - generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops, - NULL); + generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops); if (IS_ERR(generic_phy)) { dev_err(dev, "%s: failed to create phy\n", __func__); return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index 2793af17799f..778276aba3aa 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -304,7 +304,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev) phy->select_value = select_value[channel_num][n]; phy->phy = devm_phy_create(dev, NULL, - &rcar_gen2_phy_ops, NULL); + &rcar_gen2_phy_ops); if (IS_ERR(phy->phy)) { dev_err(dev, "Failed to create PHY\n"); return PTR_ERR(phy->phy); diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index 908949dfb4db..4a12f66b7fb5 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -202,8 +202,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) struct samsung_usb2_phy_instance *p = &drv->instances[i]; dev_dbg(dev, "Creating phy \"%s\"\n", label); - p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops, - NULL); + p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops); if (IS_ERR(p->phy)) { dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", label); diff --git a/drivers/phy/phy-spear1310-miphy.c b/drivers/phy/phy-spear1310-miphy.c index 5f4c586ee951..9f47fae7eecb 100644 --- a/drivers/phy/phy-spear1310-miphy.c +++ b/drivers/phy/phy-spear1310-miphy.c @@ -227,7 +227,7 @@ static int spear1310_miphy_probe(struct platform_device *pdev) return -EINVAL; } - priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL); + priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); if (IS_ERR(priv->phy)) { dev_err(dev, "failed to create SATA PCIe PHY\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/phy-spear1340-miphy.c b/drivers/phy/phy-spear1340-miphy.c index 1ecd0945bad3..e42bc200275f 100644 --- a/drivers/phy/phy-spear1340-miphy.c +++ b/drivers/phy/phy-spear1340-miphy.c @@ -259,7 +259,7 @@ static int spear1340_miphy_probe(struct platform_device *pdev) return PTR_ERR(priv->misc); } - priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL); + priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); if (IS_ERR(priv->phy)) { dev_err(dev, "failed to create SATA PCIe PHY\n"); return PTR_ERR(priv->phy); diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c index 42428d4181ea..74f0fab3cd8a 100644 --- a/drivers/phy/phy-stih407-usb.c +++ b/drivers/phy/phy-stih407-usb.c @@ -137,7 +137,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) } phy_dev->param = res->start; - phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL); + phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); if (IS_ERR(phy)) { dev_err(dev, "failed to create Display Port PHY\n"); return PTR_ERR(phy); diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c index 9f16cb8e01f4..a603801293ff 100644 --- a/drivers/phy/phy-stih41x-usb.c +++ b/drivers/phy/phy-stih41x-usb.c @@ -148,7 +148,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy_dev->clk); } - phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL); + phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create phy\n"); @@ -160,10 +160,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev) phy_set_drvdata(phy, phy_dev); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - return 0; + return PTR_ERR_OR_ZERO(phy_provider); } static const struct of_device_id stih41x_usb_phy_of_match[] = { diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 0baf5efc8a40..fb02a67c9181 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -157,6 +157,10 @@ static int sun4i_usb_phy_init(struct phy *_phy) return ret; } + /* Enable USB 45 Ohm resistor calibration */ + if (phy->index == 0) + sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); + /* Adjust PHY's magnitude and rate */ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); @@ -213,7 +217,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, { struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys)) + if (args->args[0] >= data->num_phys) return ERR_PTR(-ENODEV); return data->phys[args->args[0]].phy; @@ -255,8 +259,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - /* Skip 0, 0 is the phy for otg which is not yet supported. */ - for (i = 1; i < data->num_phys; i++) { + for (i = 0; i < data->num_phys; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[16]; @@ -295,7 +298,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return PTR_ERR(phy->pmu); } - phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL); + phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); if (IS_ERR(phy->phy)) { dev_err(dev, "failed to create PHY %d\n", i); return PTR_ERR(phy->phy); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index ab1e22d9a1e8..c297b7a10d30 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -399,7 +399,7 @@ static int ti_pipe3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, phy); pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL); + generic_phy = devm_phy_create(phy->dev, NULL, &ops); if (IS_ERR(generic_phy)) return PTR_ERR(generic_phy); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index 7b04befd5271..8e87f54671f3 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -606,7 +606,7 @@ static int twl4030_set_peripheral(struct usb_otg *otg, otg->gadget = gadget; if (!gadget) - otg->phy->state = OTG_STATE_UNDEFINED; + otg->state = OTG_STATE_UNDEFINED; return 0; } @@ -618,7 +618,7 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) otg->host = host; if (!host) - otg->phy->state = OTG_STATE_UNDEFINED; + otg->state = OTG_STATE_UNDEFINED; return 0; } @@ -644,7 +644,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) struct usb_otg *otg; struct device_node *np = pdev->dev.of_node; struct phy_provider *phy_provider; - struct phy_init_data *init_data = NULL; twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); if (!twl) @@ -655,7 +654,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) (enum twl4030_usb_mode *)&twl->usb_mode); else if (pdata) { twl->usb_mode = pdata->usb_mode; - init_data = pdata->init_data; } else { dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); return -EINVAL; @@ -676,11 +674,11 @@ static int twl4030_usb_probe(struct platform_device *pdev) twl->phy.otg = otg; twl->phy.type = USB_PHY_TYPE_USB2; - otg->phy = &twl->phy; + otg->usb_phy = &twl->phy; otg->set_host = twl4030_set_host; otg->set_peripheral = twl4030_set_peripheral; - phy = devm_phy_create(twl->dev, NULL, &ops, init_data); + phy = devm_phy_create(twl->dev, NULL, &ops); if (IS_ERR(phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); return PTR_ERR(phy); @@ -733,6 +731,11 @@ static int twl4030_usb_probe(struct platform_device *pdev) return status; } + if (pdata) + err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); + if (err) + return err; + pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(twl->dev); diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index f8a51b16ade8..29214a36ea28 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1707,7 +1707,7 @@ static int xgene_phy_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); - ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL); + ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops); if (IS_ERR(ctx->phy)) { dev_dbg(&pdev->dev, "Failed to create PHY\n"); rc = PTR_ERR(ctx->phy); |