diff options
author | Giuseppe CAVALLARO <peppe.cavallaro@st.com> | 2013-11-28 06:24:00 +0100 |
---|---|---|
committer | Peter Griffin <peter.griffin@linaro.org> | 2014-06-26 15:12:59 +0100 |
commit | aea64967e7870fb00fb1d354941198caa005dce3 (patch) | |
tree | 86a2e8dfd87f5b44c8dd56a9f984b262aed8bd8d | |
parent | 28fefd622450c4e735f129046c694309f290aa14 (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/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/phy/phy-stih407-usb3.c | 249 |
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"); |