diff options
author | Vivek Gautam <vivek.gautam@codeaurora.org> | 2017-05-11 12:17:42 +0530 |
---|---|---|
committer | Kishon Vijay Abraham I <kishon@ti.com> | 2017-06-01 15:28:33 +0530 |
commit | 0b56e9a7e8358e59b21d8a425e463072bfae523c (patch) | |
tree | a1bd94fb04cd66967e7d2a0f620a37c87da61cf9 /drivers/phy/marvell | |
parent | 858edde001e14f070d0fff347fb56c6c79e15312 (diff) | |
download | linux-0b56e9a7e8358e59b21d8a425e463072bfae523c.tar.bz2 |
phy: Group vendor specific phy drivers
Adding vendor specific directories in phy to group
phy drivers under their respective vendor umbrella.
Also updated the MAINTAINERS file to reflect the correct
directory structure for phy drivers.
Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Geert Uytterhoeven <geert+renesas@glider.be>
Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Cc: Guenter Roeck <linux@roeck-us.net>
Cc: Heiko Stuebner <heiko@sntech.de>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Chen-Yu Tsai <wens@csie.org>
Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: Jaehoon Chung <jh80.chung@samsung.com>
Cc: Stephen Boyd <stephen.boyd@linaro.org>
Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-omap@vger.kernel.org
Cc: linux-renesas-soc@vger.kernel.org
Cc: linux-rockchip@lists.infradead.org
Cc: linux-samsung-soc@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy/marvell')
-rw-r--r-- | drivers/phy/marvell/Kconfig | 50 | ||||
-rw-r--r-- | drivers/phy/marvell/Makefile | 6 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-armada375-usb2.c | 158 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-berlin-sata.c | 299 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-berlin-usb.c | 214 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-mvebu-sata.c | 138 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-pxa-28nm-hsic.c | 220 | ||||
-rw-r--r-- | drivers/phy/marvell/phy-pxa-28nm-usb2.c | 355 |
8 files changed, 1440 insertions, 0 deletions
diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig new file mode 100644 index 000000000000..048d8893bc2e --- /dev/null +++ b/drivers/phy/marvell/Kconfig @@ -0,0 +1,50 @@ +# +# Phy drivers for Marvell platforms +# +config ARMADA375_USBCLUSTER_PHY + def_bool y + depends on MACH_ARMADA_375 || COMPILE_TEST + depends on OF && HAS_IOMEM + select GENERIC_PHY + +config PHY_BERLIN_SATA + tristate "Marvell Berlin SATA PHY driver" + depends on ARCH_BERLIN && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the SATA PHY on Marvell Berlin SoCs. + +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_MVEBU_SATA + def_bool y + depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD + depends on OF + select GENERIC_PHY + +config PHY_PXA_28NM_HSIC + tristate "Marvell USB HSIC 28nm PHY Driver" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support Marvell USB HSIC PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell ehci driver. + + To compile this driver as a module, choose M here. + +config PHY_PXA_28NM_USB2 + tristate "Marvell USB 2.0 28nm PHY Driver" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support Marvell USB 2.0 PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell udc/ehci/otg driver. + + To compile this driver as a module, choose M here. diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile new file mode 100644 index 000000000000..3fc188f59118 --- /dev/null +++ b/drivers/phy/marvell/Makefile @@ -0,0 +1,6 @@ +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_SATA) += phy-mvebu-sata.o +obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o +obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o diff --git a/drivers/phy/marvell/phy-armada375-usb2.c b/drivers/phy/marvell/phy-armada375-usb2.c new file mode 100644 index 000000000000..1a3db288c0a9 --- /dev/null +++ b/drivers/phy/marvell/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 = phy_get_drvdata(phy); + 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 const 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 (IS_ERR(usb_cluster_base)) + return PTR_ERR(usb_cluster_base); + + 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_set_drvdata(phy, 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", + } +}; +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/marvell/phy-berlin-sata.c b/drivers/phy/marvell/phy-berlin-sata.c new file mode 100644 index 000000000000..2c7a57f2d595 --- /dev/null +++ b/drivers/phy/marvell/phy-berlin-sata.c @@ -0,0 +1,299 @@ +/* + * Marvell Berlin SATA PHY driver + * + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Ténart <antoine.tenart@free-electrons.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/clk.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#define HOST_VSA_ADDR 0x0 +#define HOST_VSA_DATA 0x4 +#define PORT_SCR_CTL 0x2c +#define PORT_VSR_ADDR 0x78 +#define PORT_VSR_DATA 0x7c + +#define CONTROL_REGISTER 0x0 +#define MBUS_SIZE_CONTROL 0x4 + +#define POWER_DOWN_PHY0 BIT(6) +#define POWER_DOWN_PHY1 BIT(14) +#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) +#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) + +#define BG2_PHY_BASE 0x080 +#define BG2Q_PHY_BASE 0x200 + +/* register 0x01 */ +#define REF_FREF_SEL_25 BIT(0) +#define PHY_MODE_SATA (0x0 << 5) + +/* register 0x02 */ +#define USE_MAX_PLL_RATE BIT(12) + +/* register 0x23 */ +#define DATA_BIT_WIDTH_10 (0x0 << 10) +#define DATA_BIT_WIDTH_20 (0x1 << 10) +#define DATA_BIT_WIDTH_40 (0x2 << 10) + +/* register 0x25 */ +#define PHY_GEN_MAX_1_5 (0x0 << 10) +#define PHY_GEN_MAX_3_0 (0x1 << 10) +#define PHY_GEN_MAX_6_0 (0x2 << 10) + +struct phy_berlin_desc { + struct phy *phy; + u32 power_bit; + unsigned index; +}; + +struct phy_berlin_priv { + void __iomem *base; + spinlock_t lock; + 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 phy_base, u32 reg, u32 mask, u32 val) +{ + u32 regval; + + /* select register */ + writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); + + /* set bits */ + regval = readl(ctrl_reg + PORT_VSR_DATA); + regval &= ~mask; + regval |= val; + writel(regval, ctrl_reg + PORT_VSR_DATA); +} + +static int phy_berlin_sata_power_on(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power on PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval &= ~desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + /* Configure MBus */ + writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; + writel(regval, priv->base + HOST_VSA_DATA); + + /* set PHY mode and ref freq to 25 MHz */ + 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, priv->phy_base, 0x25, + 0x0c00, PHY_GEN_MAX_6_0); + + /* set 40 bits width */ + 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, priv->phy_base, 0x02, + 0x0000, USE_MAX_PLL_RATE); + + /* set Gen3 controller speed */ + regval = readl(ctrl_reg + PORT_SCR_CTL); + regval &= ~GENMASK(7, 4); + regval |= 0x30; + writel(regval, ctrl_reg + PORT_SCR_CTL); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int phy_berlin_sata_power_off(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power down PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct phy_berlin_priv *priv = dev_get_drvdata(dev); + int i; + + if (WARN_ON(args->args[0] >= priv->nphys)) + return ERR_PTR(-ENODEV); + + for (i = 0; i < priv->nphys; i++) { + if (priv->phys[i]->index == args->args[0]) + break; + } + + if (i == priv->nphys) + return ERR_PTR(-ENODEV); + + return priv->phys[i]->phy; +} + +static const struct phy_ops phy_berlin_sata_ops = { + .power_on = phy_berlin_sata_power_on, + .power_off = phy_berlin_sata_power_off, + .owner = THIS_MODULE, +}; + +static u32 phy_berlin_power_down_bits[] = { + POWER_DOWN_PHY0, + POWER_DOWN_PHY1, +}; + +static int phy_berlin_sata_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy *phy; + struct phy_provider *phy_provider; + struct phy_berlin_priv *priv; + struct resource *res; + int ret, i = 0; + u32 phy_id; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->nphys = of_get_child_count(dev->of_node); + if (priv->nphys == 0) + return -ENODEV; + + priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), + GFP_KERNEL); + 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); + + for_each_available_child_of_node(dev->of_node, child) { + struct phy_berlin_desc *phy_desc; + + if (of_property_read_u32(child, "reg", &phy_id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + ret = -EINVAL; + goto put_child; + } + + if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { + dev_err(dev, "invalid reg in node %s\n", child->name); + ret = -EINVAL; + goto put_child; + } + + phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); + if (!phy_desc) { + ret = -ENOMEM; + goto put_child; + } + + 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); + ret = PTR_ERR(phy); + goto put_child; + } + + phy_desc->phy = phy; + phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; + phy_desc->index = phy_id; + phy_set_drvdata(phy, phy_desc); + + priv->phys[i++] = phy_desc; + + /* Make sure the PHY is off */ + phy_berlin_sata_power_off(phy); + } + + phy_provider = + devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +put_child: + of_node_put(child); + return ret; +} + +static const struct of_device_id phy_berlin_sata_of_match[] = { + { .compatible = "marvell,berlin2-sata-phy" }, + { .compatible = "marvell,berlin2q-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); + +static struct platform_driver phy_berlin_sata_driver = { + .probe = phy_berlin_sata_probe, + .driver = { + .name = "phy-berlin-sata", + .of_match_table = phy_berlin_sata_of_match, + }, +}; +module_platform_driver(phy_berlin_sata_driver); + +MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); +MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c new file mode 100644 index 000000000000..2017751ede26 --- /dev/null +++ b/drivers/phy/marvell/phy-berlin-usb.c @@ -0,0 +1,214 @@ +/* + * 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/io.h> +#include <linux/module.h> +#include <linux/of_device.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) + +static const u32 phy_berlin_pll_dividers[] = { + /* Berlin 2 */ + CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), + /* Berlin 2CD/Q */ + CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), +}; + +struct phy_berlin_usb_priv { + void __iomem *base; + struct reset_control *rst_ctrl; + u32 pll_divider; +}; + +static int phy_berlin_usb_power_on(struct phy *phy) +{ + struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); + + 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 const 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_usb_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_usb_of_match); + +static int phy_berlin_usb_probe(struct platform_device *pdev) +{ + const struct of_device_id *match = + of_match_device(phy_berlin_usb_of_match, &pdev->dev); + struct phy_berlin_usb_priv *priv; + struct resource *res; + struct phy *phy; + 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); + + phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver phy_berlin_usb_driver = { + .probe = phy_berlin_usb_probe, + .driver = { + .name = "phy-berlin-usb", + .of_match_table = phy_berlin_usb_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/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c new file mode 100644 index 000000000000..768ce92e81ce --- /dev/null +++ b/drivers/phy/marvell/phy-mvebu-sata.c @@ -0,0 +1,138 @@ +/* + * phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs. + * + * Copyright (C) 2013 Andrew Lunn <andrew@lunn.ch> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/phy/phy.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +struct priv { + struct clk *clk; + void __iomem *base; +}; + +#define SATA_PHY_MODE_2 0x0330 +#define MODE_2_FORCE_PU_TX BIT(0) +#define MODE_2_FORCE_PU_RX BIT(1) +#define MODE_2_PU_PLL BIT(2) +#define MODE_2_PU_IVREF BIT(3) +#define SATA_IF_CTRL 0x0050 +#define CTRL_PHY_SHUTDOWN BIT(9) + +static int phy_mvebu_sata_power_on(struct phy *phy) +{ + struct priv *priv = phy_get_drvdata(phy); + u32 reg; + + clk_prepare_enable(priv->clk); + + /* Enable PLL and IVREF */ + reg = readl(priv->base + SATA_PHY_MODE_2); + reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | + MODE_2_PU_PLL | MODE_2_PU_IVREF); + writel(reg , priv->base + SATA_PHY_MODE_2); + + /* Enable PHY */ + reg = readl(priv->base + SATA_IF_CTRL); + reg &= ~CTRL_PHY_SHUTDOWN; + writel(reg, priv->base + SATA_IF_CTRL); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int phy_mvebu_sata_power_off(struct phy *phy) +{ + struct priv *priv = phy_get_drvdata(phy); + u32 reg; + + clk_prepare_enable(priv->clk); + + /* Disable PLL and IVREF */ + reg = readl(priv->base + SATA_PHY_MODE_2); + reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | + MODE_2_PU_PLL | MODE_2_PU_IVREF); + writel(reg, priv->base + SATA_PHY_MODE_2); + + /* Disable PHY */ + reg = readl(priv->base + SATA_IF_CTRL); + reg |= CTRL_PHY_SHUTDOWN; + writel(reg, priv->base + SATA_IF_CTRL); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct phy_ops phy_mvebu_sata_ops = { + .power_on = phy_mvebu_sata_power_on, + .power_off = phy_mvebu_sata_power_off, + .owner = THIS_MODULE, +}; + +static int phy_mvebu_sata_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct resource *res; + struct priv *priv; + struct phy *phy; + + 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->clk = devm_clk_get(&pdev->dev, "sata"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + /* The boot loader may of left it on. Turn it off. */ + phy_mvebu_sata_power_off(phy); + + return 0; +} + +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, + .driver = { + .name = "phy-mvebu-sata", + .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"); diff --git a/drivers/phy/marvell/phy-pxa-28nm-hsic.c b/drivers/phy/marvell/phy-pxa-28nm-hsic.c new file mode 100644 index 000000000000..234aacf4db20 --- /dev/null +++ b/drivers/phy/marvell/phy-pxa-28nm-hsic.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie <xiechao.mail@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> + +#define PHY_28NM_HSIC_CTRL 0x08 +#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 +#define PHY_28NM_HSIC_PLL_CTRL01 0x1c +#define PHY_28NM_HSIC_PLL_CTRL2 0x20 +#define PHY_28NM_HSIC_INT 0x28 + +#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 +#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 +#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 + +#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) +#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) +#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) +#define S2H_DRV_SE0_4RESUME BIT(14) +#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) + +#define PHY_28NM_HSIC_CONNECT_INT BIT(1) +#define PHY_28NM_HSIC_HS_READY_INT BIT(2) + +struct mv_hsic_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_hsic_phy_init(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + + clk_prepare_enable(mv_phy->clk); + + /* Set reference clock */ + writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | + 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | + 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, + base + PHY_28NM_HSIC_PLL_CTRL01); + + /* Turn on PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | + PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + /* Make sure PHY PLL is locked */ + if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, + PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { + dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); + clk_disable_unprepare(mv_phy->clk); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_on(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + + reg = readl(base + PHY_28NM_HSIC_CTRL); + /* Avoid SE0 state when resume for some device will take it as reset */ + reg &= ~S2H_DRV_SE0_4RESUME; + reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ + writel(reg, base + PHY_28NM_HSIC_CTRL); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, + PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); + return -ETIMEDOUT; + } + + /* Waiting for HSIC connect int*/ + if (!wait_for_reg(base + PHY_28NM_HSIC_INT, + PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { + dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_off(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, + base + PHY_28NM_HSIC_CTRL); + + return 0; +} + +static int mv_hsic_phy_exit(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + /* Turn off PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & + ~PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + + +static const struct phy_ops hsic_ops = { + .init = mv_hsic_phy_init, + .power_on = mv_hsic_phy_power_on, + .power_off = mv_hsic_phy_power_off, + .exit = mv_hsic_phy_exit, + .owner = THIS_MODULE, +}; + +static int mv_hsic_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_hsic_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_hsic_phy_dt_match[] = { + { .compatible = "marvell,pxa1928-hsic-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); + +static struct platform_driver mv_hsic_phy_driver = { + .probe = mv_hsic_phy_probe, + .driver = { + .name = "mv-hsic-phy", + .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), + }, +}; +module_platform_driver(mv_hsic_phy_driver); + +MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); +MODULE_DESCRIPTION("Marvell HSIC phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c new file mode 100644 index 000000000000..37e9c8ca4983 --- /dev/null +++ b/drivers/phy/marvell/phy-pxa-28nm-usb2.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie <xiechao.mail@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> + +/* USB PXA1928 PHY mapping */ +#define PHY_28NM_PLL_REG0 0x0 +#define PHY_28NM_PLL_REG1 0x4 +#define PHY_28NM_CAL_REG 0x8 +#define PHY_28NM_TX_REG0 0x0c +#define PHY_28NM_TX_REG1 0x10 +#define PHY_28NM_RX_REG0 0x14 +#define PHY_28NM_RX_REG1 0x18 +#define PHY_28NM_DIG_REG0 0x1c +#define PHY_28NM_DIG_REG1 0x20 +#define PHY_28NM_TEST_REG0 0x24 +#define PHY_28NM_TEST_REG1 0x28 +#define PHY_28NM_MOC_REG 0x2c +#define PHY_28NM_PHY_RESERVE 0x30 +#define PHY_28NM_OTG_REG 0x34 +#define PHY_28NM_CHRG_DET 0x38 +#define PHY_28NM_CTRL_REG0 0xc4 +#define PHY_28NM_CTRL_REG1 0xc8 +#define PHY_28NM_CTRL_REG2 0xd4 +#define PHY_28NM_CTRL_REG3 0xdc + +/* PHY_28NM_PLL_REG0 */ +#define PHY_28NM_PLL_READY BIT(31) + +#define PHY_28NM_PLL_SELLPFR_SHIFT 28 +#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) + +#define PHY_28NM_PLL_FBDIV_SHIFT 16 +#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) + +#define PHY_28NM_PLL_ICP_SHIFT 8 +#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) + +#define PHY_28NM_PLL_REFDIV_SHIFT 0 +#define PHY_28NM_PLL_REFDIV_MASK 0x7f + +/* PHY_28NM_PLL_REG1 */ +#define PHY_28NM_PLL_PU_BY_REG BIT(1) + +#define PHY_28NM_PLL_PU_PLL BIT(0) + +/* PHY_28NM_CAL_REG */ +#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) + +#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) + +#define PHY_28NM_PLL_KVCO_SHIFT 16 +#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) + +#define PHY_28NM_PLL_CAL12_SHIFT 20 +#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) + +#define PHY_28NM_IMPCAL_VTH_SHIFT 8 +#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) + +#define PHY_28NM_PLLCAL_START_SHIFT 22 +#define PHY_28NM_IMPCAL_START_SHIFT 13 + +/* PHY_28NM_TX_REG0 */ +#define PHY_28NM_TX_PU_BY_REG BIT(25) + +#define PHY_28NM_TX_PU_ANA BIT(24) + +#define PHY_28NM_TX_AMP_SHIFT 20 +#define PHY_28NM_TX_AMP_MASK (0x7 << 20) + +/* PHY_28NM_RX_REG0 */ +#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 +#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) + +/* PHY_28NM_RX_REG1 */ +#define PHY_28NM_RX_SQCAL_DONE BIT(31) + +/* PHY_28NM_DIG_REG0 */ +#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) +#define PHY_28NM_DIG_SYNC_ERR BIT(30) + +#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 +#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) + +#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 +#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) + +#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 +#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) + +#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) + +/* PHY_28NM_OTG_REG */ +#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) +#define PHY_28NM_OTG_PU_OTG BIT(4) + +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 +#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 +#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 +#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 +#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 +#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 +#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 +#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 +#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 +#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 + +#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 +#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 + +#define PHY_28NM_CTRL3_OVERWRITE BIT(0) +#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) +#define PHY_28NM_CTRL3_AVALID BIT(5) +#define PHY_28NM_CTRL3_BVALID BIT(6) + +struct mv_usb2_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_usb2_phy_28nm_init(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + int ret; + + clk_prepare_enable(mv_phy->clk); + + /* PHY_28NM_PLL_REG0 */ + reg = readl(base + PHY_28NM_PLL_REG0) & + ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK + | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); + writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT + | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT + | 0x3 << PHY_28NM_PLL_ICP_SHIFT + | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), + base + PHY_28NM_PLL_REG0); + + /* PHY_28NM_PLL_REG1 */ + reg = readl(base + PHY_28NM_PLL_REG1); + writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, + base + PHY_28NM_PLL_REG1); + + /* PHY_28NM_TX_REG0 */ + reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; + writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | + PHY_28NM_TX_PU_ANA, + base + PHY_28NM_TX_REG0); + + /* PHY_28NM_RX_REG0 */ + reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; + writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, + base + PHY_28NM_RX_REG0); + + /* PHY_28NM_DIG_REG0 */ + reg = readl(base + PHY_28NM_DIG_REG0) & + ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | + PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | + PHY_28NM_DIG_SYNC_NUM_MASK); + writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | + PHY_28NM_PLL_LOCK_BYPASS), + base + PHY_28NM_DIG_REG0); + + /* PHY_28NM_OTG_REG */ + reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; + writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_CAL_REG, + PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, + HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + if (!wait_for_reg(base + PHY_28NM_RX_REG1, + PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + /* Make sure PHY PLL is ready */ + if (!wait_for_reg(base + PHY_28NM_PLL_REG0, + PHY_28NM_PLL_READY, HZ / 10)) { + dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + + return 0; +err_clk: + clk_disable_unprepare(mv_phy->clk); + return ret; +} + +static int mv_usb2_phy_28nm_power_on(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | + PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_power_off(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID + | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_exit(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + unsigned int val; + + val = readw(base + PHY_28NM_PLL_REG1); + val &= ~PHY_28NM_PLL_PU_PLL; + writew(val, base + PHY_28NM_PLL_REG1); + + /* power down PHY Analog part */ + val = readw(base + PHY_28NM_TX_REG0); + val &= ~PHY_28NM_TX_PU_ANA; + writew(val, base + PHY_28NM_TX_REG0); + + /* power down PHY OTG part */ + val = readw(base + PHY_28NM_OTG_REG); + val &= ~PHY_28NM_OTG_PU_OTG; + writew(val, base + PHY_28NM_OTG_REG); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + +static const struct phy_ops usb_ops = { + .init = mv_usb2_phy_28nm_init, + .power_on = mv_usb2_phy_28nm_power_on, + .power_off = mv_usb2_phy_28nm_power_off, + .exit = mv_usb2_phy_28nm_exit, + .owner = THIS_MODULE, +}; + +static int mv_usb2_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_usb2_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_usbphy_dt_match[] = { + { .compatible = "marvell,pxa1928-usb-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); + +static struct platform_driver mv_usb2_phy_driver = { + .probe = mv_usb2_phy_probe, + .driver = { + .name = "mv-usb2-phy", + .of_match_table = of_match_ptr(mv_usbphy_dt_match), + }, +}; +module_platform_driver(mv_usb2_phy_driver); + +MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); +MODULE_DESCRIPTION("Marvell USB2 phy driver"); +MODULE_LICENSE("GPL v2"); |