--- linux-4.9.37/drivers/usb/dwc3/dwc3-goke.c 1970-01-01 03:00:00.000000000 +0300 +++ linux-4.9.y/drivers/usb/dwc3/dwc3-goke.c 2021-06-07 13:01:34.000000000 +0300 @@ -0,0 +1,359 @@ +/* + * Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dwc3-goke.h" + +#define USB3_CTRL 0x190 +#define REG_SYS_STAT 0x8c +#define PCIE_USB3_MODE_MASK (0x3 << 12) +#define USB3_PCLK_OCC_SEL (0x1 << 30) + +#define PERI_USB3_GTXTHRCFG 0x2310000 + +#define REG_GUSB3PIPECTL0 0xc2c0 +#define GTXTHRCFG 0xc108 + +#define PCS_SSP_SOFT_RESET (0x1 << 31) +#define SUSPEND_USB3_SS_PHY (0x1 << 17) + +#define GUSB2PHYCFG_OFFSET 0xc200 +#define GCTL_OFFSET 0xc110 +#define GUCTL_OFFSET 0xc12C +#define GFLADJ_OFFSET 0xc630 + +#define U2_FREECLK_EXISTS (0x1 << 30) +#define SOFITPSYNC (0x1 << 10) +#define REFCLKPER_MASK 0xffc00000 +#define REFCLKPER_VAL 0x29 +#define set_refclkper(a) (((a) << 22) & REFCLKPER_MASK) + +#define PLS1 (0x1 << 31) +#define DECR_MASK 0x7f000000 +#define DECR_VAL 0xa +#define set_decr(a) (((a) << 24) & DECR_MASK) + +#define LPM_SEL (0x1 << 23) +#define FLADJ_MASK 0x003fff00 +#define FLADJ_VAL 0x7f0 +#define set_fladj(a) (((a) << 8) & FLADJ_MASK) + +#define DOUBLE_PCIE_MODE 0x0 +#define P0_PCIE_ADD_P1_USB3 (0x1 << 12) +#define DOUBLE_USB3 (0x2 << 12) + +#define PCIE_X1_MODE (0x0 << 12) +#define USB3_MODE (0x1 << 12) + +static struct bsp_priv *usb_priv = NULL; + + + +int usb_get_max_speed(struct device *dev) +{ + unsigned int ret; + struct device_node *np = dev->of_node; + + if (np == NULL) + return -EINVAL; + + usb_priv = kzalloc(sizeof(*usb_priv), GFP_KERNEL); + if (usb_priv == NULL) + return -ENOMEM; + + usb_priv->peri_crg = of_iomap(np, DEV_NODE_FLAG1); + if (IS_ERR(usb_priv->peri_crg)) { + kfree(usb_priv); + usb_priv = NULL; + return -ENOMEM; + } + + usb_priv->sys_ctrl = of_iomap(np, DEV_NODE_FLAG2); + if (IS_ERR(usb_priv->sys_ctrl)) { + iounmap(usb_priv->peri_crg); + + kfree(usb_priv); + usb_priv = NULL; + return -ENOMEM; + } + + ret = usb_get_maximum_speed(dev); + + iounmap(usb_priv->sys_ctrl); + iounmap(usb_priv->peri_crg); + + return ret; +} +EXPORT_SYMBOL(usb_get_max_speed); + +void bsp_dwc3_exited(void) +{ + kfree(usb_priv); + usb_priv = NULL; +} +EXPORT_SYMBOL(bsp_dwc3_exited); + +static int set_ctrl_crg_val(struct device_node *np, struct dwc3_host *host) +{ + unsigned int ret; + unsigned int reg; + + if ((np == NULL) || (host == NULL)) + return -EINVAL; + + /* get usb ctrl crg para */ + ret = of_property_read_u32(np, "crg_offset", &host->crg_offset); + if (ret) + return ret; + + ret = of_property_read_u32(np, "crg_ctrl_def_mask", &host->crg_ctrl_def_mask); + if (ret) + return ret; + + ret = of_property_read_u32(np, "crg_ctrl_def_val", &host->crg_ctrl_def_val); + if (ret) + return ret; + + /* write usb ctrl crg default value */ + reg = readl(host->crg_base + host->crg_offset); + reg &= ~host->crg_ctrl_def_mask; + reg |= host->crg_ctrl_def_val; + writel(reg, host->crg_base + host->crg_offset); + + return 0; +} + +static int dwc3_bsp_clk_init(struct dwc3_host *host, int count) +{ + struct device *dev = host->dev; + struct device_node *np = dev->of_node; + int i, ret; + + if (!count) + return -EINVAL; + + if (np == NULL) + return -EINVAL; + + host->num_clocks = count; + + host->clks = devm_kcalloc(dev, host->num_clocks, sizeof(struct clk *), GFP_KERNEL); + if (host->clks == NULL) + return -ENOMEM; + + for (i = 0; i < host->num_clocks; i++) { + struct clk *clk; + + clk = of_clk_get(np, i); + if (IS_ERR(clk)) { + while (--i >= 0) + clk_put(host->clks[i]); + + ret = PTR_ERR(clk); + goto clk_free; + } + + ret = clk_prepare_enable(clk); + if (ret < 0) { + while (--i >= 0) { + clk_disable_unprepare(host->clks[i]); + clk_put(host->clks[i]); + } + clk_put(clk); + + goto clk_free; + } + + host->clks[i] = clk; + } + + return 0; +clk_free: + devm_kfree(dev, host->clks); + host->clks = NULL; + + return ret; +} + +static void control_free_clk_config(struct dwc3_host *host) +{ + unsigned int reg; + + if (host == NULL) + return; + + reg = readl(host->ctrl_base + GUSB2PHYCFG_OFFSET); + reg &= ~U2_FREECLK_EXISTS; + writel(reg, host->ctrl_base + GUSB2PHYCFG_OFFSET); + + reg = readl(host->ctrl_base + GCTL_OFFSET); + reg &= ~SOFITPSYNC; + writel(reg, host->ctrl_base + GCTL_OFFSET); + + reg = readl(host->ctrl_base + GUCTL_OFFSET); + reg &= ~REFCLKPER_MASK; + reg |= set_refclkper(REFCLKPER_VAL); + writel(reg, host->ctrl_base + GUCTL_OFFSET); + + reg = readl(host->ctrl_base + GFLADJ_OFFSET); + reg &= ~PLS1; + writel(reg, host->ctrl_base + GFLADJ_OFFSET); + + reg = readl(host->ctrl_base + GFLADJ_OFFSET); + reg &= ~DECR_MASK; + reg |= set_decr(DECR_VAL); + writel(reg, host->ctrl_base + GFLADJ_OFFSET); + + reg = readl(host->ctrl_base + GFLADJ_OFFSET); + reg |= LPM_SEL; + writel(reg, host->ctrl_base + GFLADJ_OFFSET); + + reg = readl(host->ctrl_base + GFLADJ_OFFSET); + reg &= ~FLADJ_MASK; + reg |= set_fladj(FLADJ_VAL); + writel(reg, host->ctrl_base + GFLADJ_OFFSET); +} + +static int dwc3_bsp_iomap(struct device_node *np, struct dwc3_host *host) +{ + if ((np == NULL) || (host == NULL)) + return -EINVAL; + + host->ctrl_base = of_iomap(np, DEV_NODE_FLAG0); + if (IS_ERR(host->ctrl_base)) + return -ENOMEM; + + host->crg_base = of_iomap(np, DEV_NODE_FLAG1); + if (IS_ERR(host->crg_base)) { + iounmap(host->ctrl_base); + return -ENOMEM; + } + + return 0; +} + +static int dwc3_bsp_probe(struct platform_device *pdev) +{ + struct dwc3_host *host = NULL; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret, i; + + host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (host == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, host); + host->dev = dev; + + ret = dwc3_bsp_iomap(np, host); + if (ret) { + devm_kfree(dev, host); + host = NULL; + + return -ENOMEM; + } + + host->port_rst = devm_reset_control_get(dev, "vcc_reset"); + if (IS_ERR_OR_NULL(host->port_rst)) { + ret = PTR_ERR(host->port_rst); + goto dwc3_unmap; + } + + ret = set_ctrl_crg_val(np, host); + if (ret) + goto dwc3_unmap; + + reset_control_assert(host->port_rst); + + ret = dwc3_bsp_clk_init(host, of_clk_get_parent_count(np)); + if (ret) + goto dwc3_unmap; + + reset_control_deassert(host->port_rst); + + control_free_clk_config(host); + + udelay(U_LEVEL2); + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + for (i = 0; i < host->num_clocks; i++) { + clk_disable_unprepare(host->clks[i]); + clk_put(host->clks[i]); + } + goto dwc3_unmap; + } + + return 0; +dwc3_unmap: + iounmap(host->ctrl_base); + iounmap(host->crg_base); + + devm_kfree(dev, host); + host = NULL; + + return ret; +} + +static int dwc3_bsp_remove(struct platform_device *pdev) +{ + struct dwc3_host *host = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + for (i = 0; i < host->num_clocks; i++) { + clk_disable_unprepare(host->clks[i]); + clk_put(host->clks[i]); + } + + reset_control_assert(host->port_rst); + + of_platform_depopulate(dev); + + iounmap(host->ctrl_base); + iounmap(host->crg_base); + + devm_kfree(dev, host); + host = NULL; + + return 0; +} + +static const struct of_device_id bsp_dwc3_match[] = { + { .compatible = "goke,dwusb2" }, + { .compatible = "goke,dwusb3" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bsp_dwc3_match); + +static struct platform_driver dwc3_bsp_driver = { + .probe = dwc3_bsp_probe, + .remove = dwc3_bsp_remove, + .driver = { + .name = "goke-dwc3", + .of_match_table = bsp_dwc3_match, + }, +}; +module_platform_driver(dwc3_bsp_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare USB3 of Goke"); +MODULE_AUTHOR("Goke Technologies Co., Ltd..>");