mirror of https://github.com/OpenIPC/firmware.git
363 lines
8.1 KiB
Diff
363 lines
8.1 KiB
Diff
--- 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 <linux/clk-provider.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/phy/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/pm_runtime.h>
|
|
+#include <linux/reset.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/usb/ch9.h>
|
|
+
|
|
+#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..>");
|