aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiuseppe CAVALLARO <peppe.cavallaro@st.com>2013-11-28 06:24:00 +0100
committerPeter Griffin <peter.griffin@linaro.org>2014-06-26 15:12:59 +0100
commitaea64967e7870fb00fb1d354941198caa005dce3 (patch)
tree86a2e8dfd87f5b44c8dd56a9f984b262aed8bd8d
parent28fefd622450c4e735f129046c694309f290aa14 (diff)
usb: phy: add MiPHY2 for STiH407sti_v3.10.27_1.14-intst-sti-internal
This patch adds the MiPHY2 driver to be used by DWC3 HC available on STiH407. This is to use USB3 devices. v2: use devm_reset_control_get Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com> Conflicts: drivers/usb/phy/Makefile
-rw-r--r--drivers/usb/phy/Makefile2
-rw-r--r--drivers/usb/phy/phy-stih407-usb3.c249
2 files changed, 250 insertions, 1 deletions
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index b84eff4ee94b..4747e67656a1 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -29,5 +29,5 @@ obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
-obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb2.o
+obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb2.o phy-stih407-usb3.o
diff --git a/drivers/usb/phy/phy-stih407-usb3.c b/drivers/usb/phy/phy-stih407-usb3.c
new file mode 100644
index 000000000000..510feef8340c
--- /dev/null
+++ b/drivers/usb/phy/phy-stih407-usb3.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics
+ *
+ * STMicroelectronics PHY driver for STiH407 USB3 MiPHY2
+ *
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@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/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/usb/phy.h>
+
+#define phy_to_priv(x) container_of((x), struct stih407_usb3_miphy, phy)
+
+/* MiPHY_2 Control */
+#define SYSCFG5071 0x11c
+#define MIPHY2_PX_TX_POLARITY 1
+#define MIPHY2_PX_RX_POLARITY 2
+#define MIPHY2_PX_SYNC_DETECT_ENABLE 3
+
+/**
+ * struct stih407_usb3_cfg - SoC specific PHY register mapping
+ * @syscfg: Offset in syscfg registers bank
+ * @cfg_mask: Bits mask for PHY configuration
+ * @cfg: Static configuration value for PHY
+ */
+struct stih407_usb3_cfg {
+ u32 syscfg;
+ u32 cfg_mask;
+ u32 cfg;
+};
+
+struct stih407_usb3_miphy {
+ struct usb_phy phy;
+ struct device *dev;
+ struct regmap *regmap;
+ const struct stih407_usb3_cfg *cfg;
+ struct reset_control *rstc;
+ void __iomem *usb3_base;
+ void __iomem *pipe_base;
+};
+
+static struct stih407_usb3_cfg stih407_usb3_miphy_cfg = {
+ .syscfg = SYSCFG5071,
+ .cfg_mask = 0x7,
+ .cfg = MIPHY2_PX_SYNC_DETECT_ENABLE,
+};
+
+struct miphy_initval {
+ u16 reg;
+ u16 val;
+};
+
+/* That is a magic sequence of register settings provided at
+ * verification level to setup the MiPHY2 for USB3 DWC3 device on STiH407.
+ */
+static const struct miphy_initval initvals[] = {
+ /* Putting Macro in reset */
+ {0x00, 0x01}, {0x00, 0x03},
+ /* Wait for a while */
+ {0x00, 0x01}, {0x04, 0x14},
+
+ /* PLL calibration */
+ {0xEB, 0x1D}, {0x0D, 0x1E}, {0x0F, 0x00}, {0xC2, 0x1B},
+ {0xC4, 0x20}, {0xC9, 0x02}, {0xCA, 0x02}, {0xCB, 0x02},
+ {0xCC, 0x0A},
+
+ /* Writing The PLL Ratio */
+ {0xD4, 0xA6}, {0xD5, 0xAA}, {0xD6, 0xAA}, {0xD7, 0x04},
+ {0xD3, 0x00}, {0x0F, 0x02}, {0x0E, 0x0A}, {0x0F, 0x01},
+ {0x0E, 0x0A}, {0x0F, 0x00}, {0x0E, 0x0A},
+
+ /* Channel compensation and calibration */
+ {0x97, 0xF2}, {0x99, 0x5F}, {0x9A, 0x22},
+
+ {0x0A, 0x41}, {0x0F, 0x02}, {0x0E, 0x0A}, {0x63, 0x00},
+ {0x64, 0xA7}, {0x0F, 0x01}, {0x0E, 0x0A}, {0x63, 0x00},
+ {0x64, 0xA7}, {0x0F, 0x00}, {0x0E, 0x0A}, {0x63, 0x00},
+ {0x64, 0xA7}, {0x0F, 0x00}, {0x49, 0x07}, {0x0F, 0x01},
+ {0x49, 0x07}, {0x0F, 0x02}, {0x49, 0x07}, {0x0F, 0x03},
+ {0x49, 0x07}, {0x0F, 0x00}, {0x4A, 0x70}, {0x4B, 0x60},
+ {0x78, 0xC2}, {0x78, 0xCA}, {0x7A, 0x00}, {0x7B, 0x00},
+ {0x7A, 0x00}, {0x7F, 0x7D}, {0x7A, 0x00}, {0x7F, 0x7C},
+ {0x80, 0x0F}, {0xCD, 0x21}, {0x00, 0x01}, {0x00, 0x00},
+ {0x01, 0x04}, {0x01, 0x05}, {0xE9, 0x00}, {0x0D, 0x1E},
+ {0x3A, 0x40}, {0x01, 0x01}, {0x01, 0x00}, {0xE9, 0x40},
+ {0x0A, 0x41}, {0x0F, 0x00}, {0x0B, 0x00}, {0x62, 0x00},
+ {0x0F, 0x00}, {0xE3, 0x02}, {0x26, 0x27}, {0x26, 0x00},
+ {0x0F, 0x00},
+};
+
+static void stih407_usb3_miphy28lp(struct stih407_usb3_miphy *phy_dev)
+{
+ int i;
+
+ dev_info(phy_dev->dev, "MiPHY28LP setup\n");
+
+ for (i = 0; i < ARRAY_SIZE(initvals); i++) {
+ dev_dbg(phy_dev->dev, "reg: 0x%x=0x%x\n", initvals[i].reg,
+ initvals[i].val);
+ writeb_relaxed(initvals[i].val,
+ phy_dev->usb3_base + initvals[i].reg);
+ }
+
+ /* PIPE Wrapper Configuration */
+ writeb_relaxed(0X68, phy_dev->pipe_base + 0x23);
+ writeb_relaxed(0X61, phy_dev->pipe_base + 0x24);
+ writeb_relaxed(0X68, phy_dev->pipe_base + 0x26);
+ writeb_relaxed(0X61, phy_dev->pipe_base + 0x27);
+ writeb_relaxed(0X68, phy_dev->pipe_base + 0x28);
+ writeb_relaxed(0X60, phy_dev->pipe_base + 0x2A);
+}
+
+static int stih407_usb3_miphy_init(struct usb_phy *phy)
+{
+ struct stih407_usb3_miphy *phy_dev = phy_to_priv(phy);
+ int ret;
+
+ ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
+ phy_dev->cfg->cfg_mask, phy_dev->cfg->cfg);
+ if (ret)
+ return ret;
+
+ /* Program the MiPHY2 internal registers */
+ stih407_usb3_miphy28lp(phy_dev);
+
+ return 0;
+}
+
+static const struct of_device_id stih407_usb3_miphy_of_match[];
+
+static int stih407_usb3_miphy_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ struct stih407_usb3_miphy *phy_dev;
+ struct device *dev = &pdev->dev;
+ struct usb_phy *phy;
+ struct resource *res;
+
+ phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
+ if (!phy_dev)
+ return -ENOMEM;
+
+ match = of_match_device(stih407_usb3_miphy_of_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ phy_dev->cfg = match->data;
+ phy_dev->dev = dev;
+
+ phy_dev->rstc = devm_reset_control_get(dev, NULL);
+ if (IS_ERR(phy_dev->rstc)) {
+ dev_err(dev, "failed to ctrl MiPHY2 USB3 reset\n");
+ return PTR_ERR(phy_dev->rstc);
+ }
+
+ dev_info(dev, "reset MiPHY\n");
+ reset_control_deassert(phy_dev->rstc);
+
+ phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(phy_dev->regmap)) {
+ dev_err(dev, "No syscfg phandle specified\n");
+ return PTR_ERR(phy_dev->regmap);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb3-uport");
+ if (res) {
+ phy_dev->usb3_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!phy_dev->usb3_base) {
+ dev_err(&pdev->dev, "Unable to map base registers\n");
+ return -ENOMEM;
+ }
+ }
+ /* Check for PIPE registers */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipew");
+ if (res) {
+ phy_dev->pipe_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!phy_dev->pipe_base) {
+ dev_err(&pdev->dev, "Unable to map PIPE registers\n");
+ return -ENOMEM;
+ }
+ }
+
+ dev_info(dev, "usb3 ioaddr 0x%p, pipew ioaddr 0x%p\n",
+ phy_dev->usb3_base, phy_dev->pipe_base);
+
+ phy = &phy_dev->phy;
+ phy->dev = dev;
+ phy->label = "STiH407 USB3 MiPHY2";
+ phy->init = stih407_usb3_miphy_init;
+ phy->type = USB_PHY_TYPE_USB3;
+
+ usb_add_phy_dev(phy);
+
+ platform_set_drvdata(pdev, phy_dev);
+
+ dev_info(dev, "USB3 MiPHY2 probed\n");
+
+ return 0;
+}
+
+static int stih407_usb2_picophy_remove(struct platform_device *pdev)
+{
+ struct stih407_usb3_miphy *phy_dev = platform_get_drvdata(pdev);
+
+ reset_control_assert(phy_dev->rstc);
+
+ usb_remove_phy(&phy_dev->phy);
+
+ return 0;
+}
+
+static const struct of_device_id stih407_usb3_miphy_of_match[] = {
+ {
+ .compatible = "st,stih407-usb3phy",
+ .data = &stih407_usb3_miphy_cfg},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, stih407_usb3_miphy_of_match);
+
+static struct platform_driver stih407_usb3_miphy_driver = {
+ .probe = stih407_usb3_miphy_probe,
+ .remove = stih407_usb2_picophy_remove,
+ .driver = {
+ .name = "stih407-usb3-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = stih407_usb3_miphy_of_match,
+ }
+};
+
+module_platform_driver(stih407_usb3_miphy_driver);
+
+MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics USB3 MiPHY2 driver for STiH407 SoC");
+MODULE_LICENSE("GPL v2");