mirror of https://github.com/OpenIPC/firmware.git
				
				
				
			
		
			
				
	
	
		
			762 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Diff
		
	
	
			
		
		
	
	
			762 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Diff
		
	
	
| --- linux-4.9.37/drivers/phy/goke/phy-goke-usbp2.c	1970-01-01 03:00:00.000000000 +0300
 | |
| +++ linux-4.9.y/drivers/phy/goke/phy-goke-usbp2.c	2021-06-07 13:01:34.000000000 +0300
 | |
| @@ -0,0 +1,758 @@
 | |
| +/*
 | |
| + * 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/module.h>
 | |
| +#include <linux/of_address.h>
 | |
| +#include <linux/phy/phy.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/reset.h>
 | |
| +#include <linux/usb/ch9.h>
 | |
| +
 | |
| +#include "phy-goke-usb.h"
 | |
| +
 | |
| +#define USBP2_PHY_TRIM_OFFSET 0x0008
 | |
| +#define USBP2_PHY_TRIM_MASK   0x1f00
 | |
| +#define USBP2_PHY_TRIM_VAL(a) (((a) << 8) & USBP2_PHY_TRIM_MASK)
 | |
| +
 | |
| +#define USBP2_PHY_SVB_OFFSET 0x0000
 | |
| +#define USBP2_PHY_SVB_MASK   0x0f000000
 | |
| +#define USBP2_PHY_SVB_VAL(a) (((a) << 24) & USBP2_PHY_SVB_MASK)
 | |
| +
 | |
| +struct bsp_usbp2_priv {
 | |
| +	void __iomem *crg_base;
 | |
| +	void __iomem *phy_base;
 | |
| +	void __iomem *pin_base;
 | |
| +	struct phy *phy;
 | |
| +	struct device *dev;
 | |
| +	struct clk **clks;
 | |
| +	int num_clocks;
 | |
| +	u32 phy_pll_offset;
 | |
| +	u32 phy_pll_mask;
 | |
| +	u32 phy_pll_val;
 | |
| +	u32 crg_offset;
 | |
| +	u32 crg_defal_mask;
 | |
| +	u32 crg_defal_val;
 | |
| +	u32 vbus_offset;
 | |
| +	u32 vbus_val;
 | |
| +	int vbus_flag;
 | |
| +	u32 pwren_offset;
 | |
| +	u32 pwren_val;
 | |
| +	int pwren_flag;
 | |
| +	u32 ana_cfg_0_eye_val;
 | |
| +	u32 ana_cfg_0_offset;
 | |
| +	int ana_cfg_0_flag;
 | |
| +	u32 ana_cfg_2_eye_val;
 | |
| +	u32 ana_cfg_2_offset;
 | |
| +	int ana_cfg_2_flag;
 | |
| +	u32 ana_cfg_4_eye_val;
 | |
| +	u32 ana_cfg_4_offset;
 | |
| +	int ana_cfg_4_flag;
 | |
| +	struct reset_control *usb_phy_tpor_rst;
 | |
| +	struct reset_control *usb_phy_por_rst;
 | |
| +	u32 trim_otp_addr;
 | |
| +	u32 trim_otp_mask;
 | |
| +	u32 trim_otp_bit_offset;
 | |
| +	u32 trim_otp_min;
 | |
| +	u32 trim_otp_max;
 | |
| +	int trim_flag;
 | |
| +	u32 svb_otp_addr;
 | |
| +	u32 svb_otp_predev5_min;
 | |
| +	u32 svb_otp_predev5_max;
 | |
| +	u32 svb_phy_predev5_val;
 | |
| +	int svb_predev5_flag;
 | |
| +	u32 svb_otp_predev4_min;
 | |
| +	u32 svb_otp_predev4_max;
 | |
| +	u32 svb_phy_predev4_val;
 | |
| +	int svb_predev4_flag;
 | |
| +	u32 svb_otp_predev3_min;
 | |
| +	u32 svb_otp_predev3_max;
 | |
| +	u32 svb_phy_predev3_val;
 | |
| +	int svb_predev3_flag;
 | |
| +	u32 svb_otp_predev2_min;
 | |
| +	u32 svb_otp_predev2_max;
 | |
| +	u32 svb_phy_predev2_val;
 | |
| +	int svb_predev2_flag;
 | |
| +	int svb_flag;
 | |
| +};
 | |
| +
 | |
| +void bsp_usbp2_def_all_exist(struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	if (priv == NULL)
 | |
| +		return;
 | |
| +
 | |
| +	/* All parameters exist by default */
 | |
| +	priv->vbus_flag = 1;
 | |
| +
 | |
| +	priv->pwren_flag = 1;
 | |
| +
 | |
| +	priv->ana_cfg_0_flag = 1;
 | |
| +
 | |
| +	priv->ana_cfg_2_flag = 1;
 | |
| +
 | |
| +	priv->ana_cfg_4_flag = 1;
 | |
| +
 | |
| +	priv->trim_flag = 1;
 | |
| +
 | |
| +	priv->svb_predev5_flag = 1;
 | |
| +
 | |
| +	priv->svb_predev4_flag = 1;
 | |
| +
 | |
| +	priv->svb_predev3_flag = 1;
 | |
| +
 | |
| +	priv->svb_predev2_flag = 1;
 | |
| +
 | |
| +	priv->svb_flag = 1;
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_get_eye_para(struct device *dev, struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return;
 | |
| +
 | |
| +	/*
 | |
| +	 * Get phy eye parameters,if you want to change them,please open
 | |
| +	 * dtsi file and modify parameters at phy node.
 | |
| +	 */
 | |
| +	ret = of_property_read_u32(dev->of_node, "ana_cfg_0_eye_val",
 | |
| +				   &(priv->ana_cfg_0_eye_val));
 | |
| +	if (ret)
 | |
| +		priv->ana_cfg_0_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "ana_cfg_0_offset",
 | |
| +				   &(priv->ana_cfg_0_offset));
 | |
| +	if (ret)
 | |
| +		priv->ana_cfg_0_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "ana_cfg_2_eye_val",
 | |
| +				   &(priv->ana_cfg_2_eye_val));
 | |
| +	if (ret)
 | |
| +		priv->ana_cfg_2_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "ana_cfg_2_offset",
 | |
| +				   &(priv->ana_cfg_2_offset));
 | |
| +	if (ret)
 | |
| +		priv->ana_cfg_2_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "ana_cfg_4_eye_val",
 | |
| +				   &(priv->ana_cfg_4_eye_val));
 | |
| +	if (ret)
 | |
| +		priv->ana_cfg_4_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "ana_cfg_4_offset",
 | |
| +				   &(priv->ana_cfg_4_offset));
 | |
| +	if (ret)
 | |
| +		priv->ana_cfg_4_flag = 0;
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_phy_eye_config(struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	if (priv == NULL)
 | |
| +		return;
 | |
| +
 | |
| +	if (priv->ana_cfg_0_flag)
 | |
| +		writel(priv->ana_cfg_0_eye_val, priv->phy_base + priv->ana_cfg_0_offset);
 | |
| +
 | |
| +	if (priv->ana_cfg_2_flag)
 | |
| +		writel(priv->ana_cfg_2_eye_val, priv->phy_base + priv->ana_cfg_2_offset);
 | |
| +
 | |
| +	if (priv->ana_cfg_4_flag)
 | |
| +		writel(priv->ana_cfg_4_eye_val, priv->phy_base + priv->ana_cfg_4_offset);
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_get_trim_para(struct device *dev, struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return;
 | |
| +
 | |
| +	/* get phy trim parameters */
 | |
| +	ret = of_property_read_u32(dev->of_node, "trim_otp_addr",
 | |
| +				   &(priv->trim_otp_addr));
 | |
| +	if (ret)
 | |
| +		priv->trim_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "trim_otp_mask",
 | |
| +				   &(priv->trim_otp_mask));
 | |
| +	if (ret)
 | |
| +		priv->trim_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "trim_otp_bit_offset",
 | |
| +				   &(priv->trim_otp_bit_offset));
 | |
| +	if (ret)
 | |
| +		priv->trim_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "trim_otp_min", &(priv->trim_otp_min));
 | |
| +	if (ret)
 | |
| +		priv->trim_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "trim_otp_max", &(priv->trim_otp_max));
 | |
| +	if (ret)
 | |
| +		priv->trim_flag = 0;
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_phy_trim_config(struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int trim_otp_val;
 | |
| +	unsigned int reg;
 | |
| +	void __iomem *phy_trim = NULL;
 | |
| +
 | |
| +	if (priv == NULL)
 | |
| +		return;
 | |
| +
 | |
| +	if (priv->trim_flag) {
 | |
| +		phy_trim = ioremap_nocache(priv->trim_otp_addr, __1K__);
 | |
| +		if (phy_trim == NULL)
 | |
| +			return;
 | |
| +
 | |
| +		reg = readl(phy_trim);
 | |
| +		trim_otp_val = (reg & priv->trim_otp_mask);
 | |
| +		if ((trim_otp_val >= priv->trim_otp_min) &&
 | |
| +		    (trim_otp_val <= priv->trim_otp_max)) {
 | |
| +			/* set trim value to phy */
 | |
| +			reg = readl(priv->phy_base + USBP2_PHY_TRIM_OFFSET);
 | |
| +			reg &= ~USBP2_PHY_TRIM_MASK;
 | |
| +			reg |= USBP2_PHY_TRIM_VAL(trim_otp_val >> priv->trim_otp_bit_offset);
 | |
| +			writel(reg, priv->phy_base + USBP2_PHY_TRIM_OFFSET);
 | |
| +		}
 | |
| +		iounmap(phy_trim);
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_get_svb_para_1(struct device *dev, struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return;
 | |
| +
 | |
| +	/* get phy svb parmteters */
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_addr", &(priv->svb_otp_addr));
 | |
| +	if (ret)
 | |
| +		priv->svb_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev5_min",
 | |
| +				   &(priv->svb_otp_predev5_min));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev5_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev5_max",
 | |
| +				   &(priv->svb_otp_predev5_max));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev5_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_phy_predev5_val",
 | |
| +				   &(priv->svb_phy_predev5_val));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev5_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev4_min",
 | |
| +				   &(priv->svb_otp_predev4_min));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev4_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev4_max",
 | |
| +				   &(priv->svb_otp_predev4_max));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev4_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_phy_predev4_val",
 | |
| +				   &(priv->svb_phy_predev4_val));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev4_flag = 0;
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_get_svb_para_2(struct device *dev, struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev3_min",
 | |
| +				   &(priv->svb_otp_predev3_min));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev3_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev3_max",
 | |
| +				   &(priv->svb_otp_predev3_max));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev3_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_phy_predev3_val",
 | |
| +				   &(priv->svb_phy_predev3_val));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev3_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev2_min",
 | |
| +				   &(priv->svb_otp_predev2_min));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev2_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_otp_predev2_max",
 | |
| +				   &(priv->svb_otp_predev2_max));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev2_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "svb_phy_predev2_val",
 | |
| +				   &(priv->svb_phy_predev2_val));
 | |
| +	if (ret)
 | |
| +		priv->svb_predev2_flag = 0;
 | |
| +}
 | |
| +
 | |
| +void bsp_usbp2_phy_svb_config(struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int reg;
 | |
| +	unsigned int ret;
 | |
| +	void __iomem *phy_svb = NULL;
 | |
| +
 | |
| +	if (priv == NULL)
 | |
| +		return;
 | |
| +
 | |
| +	if (priv->svb_flag) {
 | |
| +		phy_svb = ioremap_nocache(priv->svb_otp_addr, __1K__);
 | |
| +		if (phy_svb == NULL)
 | |
| +			return;
 | |
| +
 | |
| +		ret = readl(phy_svb);
 | |
| +		reg = readl(priv->phy_base + USBP2_PHY_SVB_OFFSET);
 | |
| +		reg &= ~USBP2_PHY_SVB_MASK;
 | |
| +		if ((ret >= priv->svb_otp_predev5_min) &&
 | |
| +		    (ret < priv->svb_otp_predev5_max) && (priv->svb_predev5_flag))
 | |
| +			reg |= USBP2_PHY_SVB_VAL(priv->svb_phy_predev5_val);
 | |
| +		else if ((ret >= priv->svb_otp_predev4_min) &&
 | |
| +			 (ret < priv->svb_otp_predev4_max) && (priv->svb_predev4_flag))
 | |
| +			reg |= USBP2_PHY_SVB_VAL(priv->svb_phy_predev4_val);
 | |
| +		else if ((ret >= priv->svb_otp_predev3_min) &&
 | |
| +			 (ret <= priv->svb_otp_predev3_max) && (priv->svb_predev3_flag))
 | |
| +			reg |= USBP2_PHY_SVB_VAL(priv->svb_phy_predev3_val);
 | |
| +		else if ((ret > priv->svb_otp_predev2_min) &&
 | |
| +			 (ret <= priv->svb_otp_predev2_max) && (priv->svb_predev2_flag))
 | |
| +			reg |= USBP2_PHY_SVB_VAL(priv->svb_phy_predev2_val);
 | |
| +		else
 | |
| +			reg |= USBP2_PHY_SVB_VAL(priv->svb_phy_predev4_val);
 | |
| +
 | |
| +		writel(reg, priv->phy_base + USBP2_PHY_SVB_OFFSET);
 | |
| +		iounmap(phy_svb);
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static void bsp_usb_vbus_and_pwren_config(struct device *dev, struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return;
 | |
| +
 | |
| +	/* Some chips do not have VBUS encapsulation and need to be configured */
 | |
| +	ret = of_property_read_u32(dev->of_node, "vbus_offset", &(priv->vbus_offset));
 | |
| +	if (ret)
 | |
| +		priv->vbus_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "vbus_val", &(priv->vbus_val));
 | |
| +	if (ret)
 | |
| +		priv->vbus_flag = 0;
 | |
| +
 | |
| +	/* Some chips do not have PWREN encapsulation and need to be configured */
 | |
| +	ret = of_property_read_u32(dev->of_node, "pwren_offset", &(priv->pwren_offset));
 | |
| +	if (ret)
 | |
| +		priv->pwren_flag = 0;
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "pwren_val", &(priv->pwren_val));
 | |
| +	if (ret)
 | |
| +		priv->pwren_flag = 0;
 | |
| +
 | |
| +	if (priv->vbus_flag)
 | |
| +		writel(priv->vbus_val, priv->pin_base + priv->vbus_offset);
 | |
| +
 | |
| +	udelay(U_LEVEL2);
 | |
| +
 | |
| +	if (priv->pwren_flag)
 | |
| +		writel(priv->pwren_val, priv->pin_base + priv->pwren_offset);
 | |
| +
 | |
| +	udelay(U_LEVEL2);
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_get_pll_clk(struct device *dev,
 | |
| +		  struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	/* Get phy pll clk config parameters from the phy node of the dtsi file */
 | |
| +	ret = of_property_read_u32(dev->of_node, "phy_pll_offset",
 | |
| +				   &(priv->phy_pll_offset));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get phy_pll_offset failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "phy_pll_mask", &(priv->phy_pll_mask));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get phy_pll_mask failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "phy_pll_val", &(priv->phy_pll_val));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get phy_pll_val failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_set_crg_val(struct device *dev,
 | |
| +		  struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +	unsigned int reg;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	/* Get CRG default value from the phy node of the dtsi file */
 | |
| +	ret = of_property_read_u32(dev->of_node, "crg_offset", &(priv->crg_offset));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get crg_offset failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "crg_defal_mask",
 | |
| +				   &(priv->crg_defal_mask));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get crg_defal_mask failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	ret = of_property_read_u32(dev->of_node, "crg_defal_val",
 | |
| +				   &(priv->crg_defal_val));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get crg_defal_val failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	/* write phy crg default value */
 | |
| +	reg = readl(priv->crg_base + priv->crg_offset);
 | |
| +	reg &= ~priv->crg_defal_mask;
 | |
| +	reg |= priv->crg_defal_val;
 | |
| +	writel(reg, priv->crg_base + priv->crg_offset);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_phy_get_para(struct device *dev,
 | |
| +		  struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	if ((dev == NULL) || (priv == NULL))
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	bsp_usbp2_def_all_exist(priv);
 | |
| +
 | |
| +	ret = bsp_usbp2_get_pll_clk(dev, priv);
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get pll clk failed: %d\n", ret);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	bsp_usbp2_get_trim_para(dev, priv);
 | |
| +	bsp_usbp2_get_eye_para(dev, priv);
 | |
| +	bsp_usbp2_get_svb_para_1(dev, priv);
 | |
| +	bsp_usbp2_get_svb_para_2(dev, priv);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_phy_get_clks(struct bsp_usbp2_priv *priv, int count)
 | |
| +{
 | |
| +	struct device *dev = priv->dev;
 | |
| +	struct device_node *np = dev->of_node;
 | |
| +	int i;
 | |
| +
 | |
| +	priv->num_clocks = count;
 | |
| +
 | |
| +	if (!count)
 | |
| +		return 0;
 | |
| +
 | |
| +	priv->clks =
 | |
| +		devm_kcalloc(dev, priv->num_clocks, sizeof(struct clk *), GFP_KERNEL);
 | |
| +	if (priv->clks == NULL)
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	for (i = 0; i < priv->num_clocks; i++) {
 | |
| +		struct clk *clk;
 | |
| +
 | |
| +		clk = of_clk_get(np, i);
 | |
| +		if (IS_ERR(clk)) {
 | |
| +			while (--i >= 0)
 | |
| +				clk_put(priv->clks[i]);
 | |
| +
 | |
| +			devm_kfree(dev, priv->clks);
 | |
| +			priv->clks = NULL;
 | |
| +			return PTR_ERR(clk);
 | |
| +		}
 | |
| +
 | |
| +		priv->clks[i] = clk;
 | |
| +	}
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_clk_rst_config(struct platform_device *pdev,
 | |
| +		  struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	struct device *dev = &pdev->dev;
 | |
| +	struct device_node *np = pdev->dev.of_node;
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	ret = bsp_usbp2_phy_get_clks(priv, of_clk_get_parent_count(np));
 | |
| +	if (ret) {
 | |
| +		dev_err(dev, "get phy clk failed\n");
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	priv->usb_phy_tpor_rst = devm_reset_control_get(dev, "phy_tpor_reset");
 | |
| +	if (IS_ERR_OR_NULL(priv->usb_phy_tpor_rst)) {
 | |
| +		dev_err(dev, "get phy_tpor_reset failed: %d\n", ret);
 | |
| +		return PTR_ERR(priv->usb_phy_tpor_rst);
 | |
| +	}
 | |
| +
 | |
| +	priv->usb_phy_por_rst = devm_reset_control_get(dev, "phy_por_reset");
 | |
| +	if (IS_ERR_OR_NULL(priv->usb_phy_por_rst)) {
 | |
| +		dev_err(dev, "get phy_por_reset failed: %d\n", ret);
 | |
| +		return PTR_ERR(priv->usb_phy_por_rst);;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_iomap(struct device_node *np,
 | |
| +		  struct bsp_usbp2_priv *priv)
 | |
| +{
 | |
| +	if ((np == NULL) || (priv == NULL))
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	priv->phy_base = of_iomap(np, 0);
 | |
| +	if (IS_ERR(priv->phy_base))
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	priv->crg_base = of_iomap(np, 1);
 | |
| +	if (IS_ERR(priv->crg_base)) {
 | |
| +		iounmap(priv->phy_base);
 | |
| +		return -ENOMEM;
 | |
| +	}
 | |
| +
 | |
| +	priv->pin_base = of_iomap(np, 2);
 | |
| +	if (IS_ERR(priv->pin_base)) {
 | |
| +		iounmap(priv->phy_base);
 | |
| +		iounmap(priv->crg_base);
 | |
| +		return -ENOMEM;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_phy_init(struct phy *phy)
 | |
| +{
 | |
| +	struct bsp_usbp2_priv *priv = phy_get_drvdata(phy);
 | |
| +	int i, ret;
 | |
| +	unsigned int reg;
 | |
| +
 | |
| +	for (i = 0; i < priv->num_clocks; i++) {
 | |
| +		ret = clk_prepare_enable(priv->clks[i]);
 | |
| +		if (ret < 0) {
 | |
| +			while (--i >= 0) {
 | |
| +				clk_disable_unprepare(priv->clks[i]);
 | |
| +				clk_put(priv->clks[i]);
 | |
| +			}
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	udelay(U_LEVEL5);
 | |
| +
 | |
| +	/* undo por reset */
 | |
| +	ret = reset_control_deassert(priv->usb_phy_por_rst);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	/* pll out clk */
 | |
| +	reg = readl(priv->phy_base + priv->phy_pll_offset);
 | |
| +	reg &= ~priv->phy_pll_mask;
 | |
| +	reg |= priv->phy_pll_val;
 | |
| +	writel(reg, priv->phy_base + priv->phy_pll_offset);
 | |
| +
 | |
| +	mdelay(M_LEVEL1);
 | |
| +
 | |
| +	/* undo tpor reset */
 | |
| +	ret = reset_control_deassert(priv->usb_phy_tpor_rst);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	udelay(U_LEVEL6);
 | |
| +
 | |
| +	bsp_usbp2_phy_eye_config(priv);
 | |
| +
 | |
| +	bsp_usbp2_phy_trim_config(priv);
 | |
| +
 | |
| +	bsp_usbp2_phy_svb_config(priv);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_phy_exit(struct phy *phy)
 | |
| +{
 | |
| +	struct bsp_usbp2_priv *priv = phy_get_drvdata(phy);
 | |
| +	int i, ret;
 | |
| +
 | |
| +	for (i = 0; i < priv->num_clocks; i++)
 | |
| +		clk_disable_unprepare(priv->clks[i]);
 | |
| +
 | |
| +	ret = reset_control_assert(priv->usb_phy_por_rst);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	ret = reset_control_assert(priv->usb_phy_tpor_rst);
 | |
| +	if (ret)
 | |
| +		return ret;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static const struct phy_ops bsp_usbp2_phy_ops = {
 | |
| +	.init = bsp_usbp2_phy_init,
 | |
| +	.exit = bsp_usbp2_phy_exit,
 | |
| +	.owner = THIS_MODULE,
 | |
| +};
 | |
| +
 | |
| +static int bsp_usbp2_phy_probe(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct device *dev = &pdev->dev;
 | |
| +	struct phy *phy = NULL;
 | |
| +	struct bsp_usbp2_priv *priv = NULL;
 | |
| +	struct device_node *np = pdev->dev.of_node;
 | |
| +	struct phy_provider *phy_provider = NULL;
 | |
| +	unsigned int ret;
 | |
| +
 | |
| +	phy = devm_phy_create(dev, dev->of_node, &bsp_usbp2_phy_ops);
 | |
| +	if (IS_ERR(phy))
 | |
| +		return PTR_ERR(phy);
 | |
| +
 | |
| +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 | |
| +	if (priv == NULL)
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	ret = bsp_usbp2_iomap(np, priv);
 | |
| +	if (ret) {
 | |
| +		devm_kfree(dev, priv);
 | |
| +		priv = NULL;
 | |
| +
 | |
| +		return -ENOMEM;
 | |
| +	}
 | |
| +
 | |
| +	platform_set_drvdata(pdev, priv);
 | |
| +	priv->dev = dev;
 | |
| +
 | |
| +	ret = bsp_usbp2_clk_rst_config(pdev, priv);
 | |
| +	if (ret)
 | |
| +		goto xvp_unmap;
 | |
| +
 | |
| +	ret = bsp_usbp2_phy_get_para(dev, priv);
 | |
| +	if (ret)
 | |
| +		goto xvp_unmap;
 | |
| +
 | |
| +	bsp_usb_vbus_and_pwren_config(dev, priv);
 | |
| +
 | |
| +	ret = bsp_usbp2_set_crg_val(dev, priv);
 | |
| +	if (ret)
 | |
| +		goto xvp_unmap;
 | |
| +
 | |
| +	platform_set_drvdata(pdev, priv);
 | |
| +	phy_set_drvdata(phy, priv);
 | |
| +
 | |
| +	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 | |
| +	if (IS_ERR(phy_provider)) {
 | |
| +		ret = PTR_ERR(phy_provider);
 | |
| +		goto xvp_unmap;
 | |
| +	}
 | |
| +
 | |
| +	return 0;
 | |
| +xvp_unmap:
 | |
| +	iounmap(priv->phy_base);
 | |
| +	iounmap(priv->crg_base);
 | |
| +	iounmap(priv->pin_base);
 | |
| +
 | |
| +	devm_kfree(dev, priv);
 | |
| +	priv = NULL;
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_phy_remove(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct device *dev = &pdev->dev;
 | |
| +	struct bsp_usbp2_priv *priv = platform_get_drvdata(pdev);
 | |
| +	int i;
 | |
| +
 | |
| +	for (i = 0; i < priv->num_clocks; i++)
 | |
| +		clk_put(priv->clks[i]);
 | |
| +
 | |
| +	iounmap(priv->phy_base);
 | |
| +	iounmap(priv->crg_base);
 | |
| +	iounmap(priv->pin_base);
 | |
| +
 | |
| +	devm_kfree(dev, priv);
 | |
| +	priv = NULL;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +#ifdef CONFIG_PM_SLEEP
 | |
| +static int bsp_usbp2_phy_suspend(struct device *dev)
 | |
| +{
 | |
| +	struct phy *phy = dev_get_drvdata(dev);
 | |
| +
 | |
| +	if (bsp_usbp2_phy_exit(phy))
 | |
| +		return -1;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int bsp_usbp2_phy_resume(struct device *dev)
 | |
| +{
 | |
| +	struct phy *phy = dev_get_drvdata(dev);
 | |
| +
 | |
| +	if (bsp_usbp2_phy_init(phy))
 | |
| +		return -1;
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +#endif /* CONFIG_PM_SLEEP */
 | |
| +
 | |
| +static SIMPLE_DEV_PM_OPS(bsp_usb_pm_ops, bsp_usbp2_phy_suspend,
 | |
| +			bsp_usbp2_phy_resume);
 | |
| +
 | |
| +static const struct of_device_id bsp_usbp2_phy_of_match[] = {
 | |
| +	{ .compatible = "goke,usbp2-phy" },
 | |
| +	{},
 | |
| +};
 | |
| +
 | |
| +static struct platform_driver bsp_usbp2_phy_driver = {
 | |
| +	.probe = bsp_usbp2_phy_probe,
 | |
| +	.remove = bsp_usbp2_phy_remove,
 | |
| +	.driver = {
 | |
| +		.name = "goke-usbp2-phy",
 | |
| +		.pm = &bsp_usb_pm_ops,
 | |
| +		.of_match_table = bsp_usbp2_phy_of_match,
 | |
| +	}
 | |
| +};
 | |
| +module_platform_driver(bsp_usbp2_phy_driver);
 | |
| +MODULE_DESCRIPTION("GOKE USB PHY driver");
 | |
| +MODULE_ALIAS("platform:usbp2-phy");
 | |
| +MODULE_LICENSE("GPL v2");
 |