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..>");
 |