phy: rockchip-inno-usb2: add usb2 phy support for rk3568

RK3568 has two USB 2.0 PHYs, and each PHY has two ports, the
OTG port of PHY0 support OTG mode with charging detection
function, they are similar to previous Rockchip SoCs.

However, there are three different designs for RK3568 USB 2.0 PHY.
1. RK3568 uses independent USB GRF module for each USB 2.0 PHY.
2. RK3568 accesses the registers of USB 2.0 PHY IP directly by APB.
3. The two ports of USB 2.0 PHY share one interrupt.

Signed-off-by: Ren Jianing <jianing.ren@rock-chips.com>
Change-Id: Ia33d3de222a6c7f263290f4098d0a5e557a9d568
This commit is contained in:
Ren Jianing 2020-10-27 10:58:12 +08:00 committed by Jianhong Chen
parent 3aaa96e8af
commit e475bd5dfd
1 changed files with 133 additions and 35 deletions

View File

@ -8,8 +8,10 @@
#include <dm.h>
#include <dm/lists.h>
#include <generic-phy.h>
#include <syscon.h>
#include <linux/ioport.h>
#include <power/regulator.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
@ -150,18 +152,18 @@ struct rockchip_usb2phy_cfg {
struct rockchip_usb2phy {
u8 dcd_retries;
u8 primary_retries;
void __iomem *grf_base;
void __iomem *usbgrf_base;
struct regmap *grf_base;
struct regmap *usbgrf_base;
struct udevice *vbus_supply[USB2PHY_NUM_PORTS];
const struct rockchip_usb2phy_cfg *phy_cfg;
};
static inline void __iomem *get_reg_base(struct rockchip_usb2phy *rphy)
static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy)
{
return !rphy->usbgrf_base ? rphy->grf_base : rphy->usbgrf_base;
}
static inline int property_enable(void __iomem *base,
static inline int property_enable(struct regmap *base,
const struct usb2phy_reg *reg, bool en)
{
u32 val, mask, tmp;
@ -170,16 +172,16 @@ static inline int property_enable(void __iomem *base,
mask = GENMASK(reg->bitend, reg->bitstart);
val = (tmp << reg->bitstart) | (mask << U2PHY_BIT_WRITEABLE_SHIFT);
return writel(val, base + reg->offset);
return regmap_write(base, reg->offset, val);
}
static inline bool property_enabled(void __iomem *base,
static inline bool property_enabled(struct regmap *base,
const struct usb2phy_reg *reg)
{
u32 tmp, orig;
u32 mask = GENMASK(reg->bitend, reg->bitstart);
orig = readl(base + reg->offset);
regmap_read(base, reg->offset, &orig);
tmp = (orig & mask) >> reg->bitstart;
@ -205,7 +207,7 @@ static const char *chg_to_string(enum power_supply_type chg_type)
static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy,
bool en)
{
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
property_enable(base, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en);
property_enable(base, &rphy->phy_cfg->chg_det.idp_src_en, en);
@ -214,7 +216,7 @@ static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy,
static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy,
bool en)
{
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
property_enable(base, &rphy->phy_cfg->chg_det.vdp_src_en, en);
property_enable(base, &rphy->phy_cfg->chg_det.idm_sink_en, en);
@ -223,7 +225,7 @@ static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy,
static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy,
bool en)
{
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
property_enable(base, &rphy->phy_cfg->chg_det.vdm_src_en, en);
property_enable(base, &rphy->phy_cfg->chg_det.idp_sink_en, en);
@ -232,13 +234,13 @@ static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy,
static bool rockchip_chg_primary_det_retry(struct rockchip_usb2phy *rphy)
{
bool vout = false;
struct regmap *base = get_reg_base(rphy);
while (rphy->primary_retries--) {
/* voltage source on DP, probe on DM */
rockchip_chg_enable_primary_det(rphy, true);
mdelay(CHG_PRIMARY_DET_TIME);
vout = property_enabled(rphy->grf_base,
&rphy->phy_cfg->chg_det.cp_det);
vout = property_enabled(base, &rphy->phy_cfg->chg_det.cp_det);
if (vout)
break;
}
@ -253,7 +255,7 @@ int rockchip_chg_get_type(void)
enum power_supply_type chg_type;
struct rockchip_usb2phy *rphy;
struct udevice *udev;
void __iomem *base;
struct regmap *base;
bool is_dcd, vout;
int ret;
@ -287,8 +289,7 @@ int rockchip_chg_get_type(void)
mdelay(CHG_DCD_POLL_TIME);
/* get data contact detection status */
is_dcd = property_enabled(rphy->grf_base,
&rphy->phy_cfg->chg_det.dp_det);
is_dcd = property_enabled(base, &rphy->phy_cfg->chg_det.dp_det);
if (is_dcd || !rphy->dcd_retries) {
/*
@ -302,8 +303,7 @@ int rockchip_chg_get_type(void)
}
mdelay(CHG_PRIMARY_DET_TIME);
vout = property_enabled(rphy->grf_base,
&rphy->phy_cfg->chg_det.cp_det);
vout = property_enabled(base, &rphy->phy_cfg->chg_det.cp_det);
rockchip_chg_enable_primary_det(rphy, false);
if (vout) {
/* stage 3, voltage source on DM, probe on DP */
@ -331,8 +331,7 @@ int rockchip_chg_get_type(void)
}
mdelay(CHG_SECONDARY_DET_TIME);
vout = property_enabled(rphy->grf_base,
&rphy->phy_cfg->chg_det.dcp_det);
vout = property_enabled(base, &rphy->phy_cfg->chg_det.dcp_det);
/* stage 4, turn off voltage source */
rockchip_chg_enable_secondary_det(rphy, false);
if (vout)
@ -365,7 +364,7 @@ void otg_phy_init(struct dwc2_udc *dev)
const struct rockchip_usb2phy_port_cfg *port_cfg;
struct rockchip_usb2phy *rphy;
struct udevice *udev;
void __iomem *base;
struct regmap *base;
int ret;
ret = uclass_get_device_by_name(UCLASS_PHY, "usb2-phy", &udev);
@ -393,7 +392,7 @@ static struct udevice *rockchip_usb2phy_check_vbus(struct phy *phy)
struct udevice *parent = phy->dev->parent;
struct rockchip_usb2phy *rphy = dev_get_priv(parent);
const struct rockchip_usb2phy_port_cfg *port_cfg;
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
struct udevice *vbus = NULL;
bool iddig = true;
@ -416,7 +415,7 @@ static int rockchip_usb2phy_init(struct phy *phy)
struct udevice *parent = phy->dev->parent;
struct rockchip_usb2phy *rphy = dev_get_priv(parent);
const struct rockchip_usb2phy_port_cfg *port_cfg;
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
if (phy->id == USB2PHY_PORT_OTG) {
port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
@ -440,7 +439,7 @@ static int rockchip_usb2phy_exit(struct phy *phy)
struct udevice *parent = phy->dev->parent;
struct rockchip_usb2phy *rphy = dev_get_priv(parent);
const struct rockchip_usb2phy_port_cfg *port_cfg;
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
if (phy->id == USB2PHY_PORT_OTG) {
port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
@ -546,31 +545,55 @@ static int rockchip_usb2phy_probe(struct udevice *dev)
const struct rockchip_usb2phy_cfg *phy_cfgs;
struct rockchip_usb2phy *rphy = dev_get_priv(dev);
struct udevice *parent = dev->parent;
struct udevice *syscon;
struct resource res;
u32 reg, index;
int ret;
if (!strncmp(parent->name, "root_driver", 11) &&
dev_read_bool(dev, "rockchip,grf"))
rphy->grf_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
else
rphy->grf_base = (void __iomem *)dev_read_addr(parent);
dev_read_bool(dev, "rockchip,grf")) {
ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
"rockchip,grf", &syscon);
if (ret) {
dev_err(dev, "get syscon grf failed\n");
return ret;
}
rphy->grf_base = syscon_get_regmap(syscon);
} else {
rphy->grf_base = syscon_get_regmap(parent);
}
if (rphy->grf_base <= 0) {
dev_err(dev, "get syscon grf failed\n");
dev_err(dev, "get syscon grf regmap failed\n");
return -EINVAL;
}
if (dev_read_bool(dev, "rockchip,usbgrf")) {
rphy->usbgrf_base =
syscon_get_first_range(ROCKCHIP_SYSCON_USBGRF);
if (rphy->usbgrf_base <= 0) {
ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
"rockchip,usbgrf", &syscon);
if (ret) {
dev_err(dev, "get syscon usbgrf failed\n");
return ret;
}
rphy->usbgrf_base = syscon_get_regmap(syscon);
if (rphy->usbgrf_base <= 0) {
dev_err(dev, "get syscon usbgrf regmap failed\n");
return -EINVAL;
}
} else {
rphy->usbgrf_base = NULL;
}
if (ofnode_read_u32(dev_ofnode(dev), "reg", &reg)) {
if (!strncmp(parent->name, "root_driver", 11)) {
ret = dev_read_resource(dev, 0, &res);
reg = res.start;
} else {
ret = ofnode_read_u32(dev_ofnode(dev), "reg", &reg);
}
if (ret) {
dev_err(dev, "could not read reg\n");
return -EINVAL;
}
@ -605,12 +628,12 @@ static int rockchip_usb2phy_probe(struct udevice *dev)
static int rk322x_usb2phy_tuning(struct rockchip_usb2phy *rphy)
{
void __iomem *base = get_reg_base(rphy);
struct regmap *base = get_reg_base(rphy);
int ret = 0;
/* Open pre-emphasize in non-chirp state for PHY0 otg port */
if (rphy->phy_cfg->reg == 0x760)
ret = writel(0x00070004, base + 0x76c);
ret = regmap_write(base, 0x76c, 0x00070004);
return ret;
}
@ -1028,6 +1051,80 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
{ /* sentinel */ }
};
static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
{
.reg = 0xfe8a0000,
.num_ports = 2,
.clkout_ctl = { 0x0008, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0x0000, 8, 0, 0, 0x1d1 },
.bvalid_det_en = { 0x0080, 2, 2, 0, 1 },
.bvalid_det_st = { 0x0084, 2, 2, 0, 1 },
.bvalid_det_clr = { 0x0088, 2, 2, 0, 1 },
.iddig_output = { 0x0000, 10, 10, 0, 1 },
.iddig_en = { 0x0000, 9, 9, 0, 1 },
.idfall_det_en = { 0x0080, 5, 5, 0, 1 },
.idfall_det_st = { 0x0084, 5, 5, 0, 1 },
.idfall_det_clr = { 0x008c, 5, 5, 0, 1 },
.idrise_det_en = { 0x0080, 4, 4, 0, 1 },
.idrise_det_st = { 0x0084, 4, 4, 0, 1 },
.idrise_det_clr = { 0x008c, 4, 4, 0, 1 },
.ls_det_en = { 0x0080, 0, 0, 0, 1 },
.ls_det_st = { 0x0084, 0, 0, 0, 1 },
.ls_det_clr = { 0x008c, 0, 0, 0, 1 },
.utmi_avalid = { 0x00c0, 10, 10, 0, 1 },
.utmi_bvalid = { 0x00c0, 9, 9, 0, 1 },
.utmi_iddig = { 0x00c0, 6, 6, 0, 1 },
.utmi_ls = { 0x00c0, 5, 4, 0, 1 },
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0x0004, 8, 0, 0, 0x1d1 },
.ls_det_en = { 0x0080, 1, 1, 0, 1 },
.ls_det_st = { 0x0084, 1, 1, 0, 1 },
.ls_det_clr = { 0x008c, 1, 1, 0, 1 },
.utmi_ls = { 0x00c0, 17, 16, 0, 1 },
.utmi_hstdet = { 0x00c0, 19, 19, 0, 1 }
}
},
.chg_det = {
.opmode = { 0x0000, 3, 0, 5, 1 },
.cp_det = { 0x00c0, 24, 24, 0, 1 },
.dcp_det = { 0x00c0, 23, 23, 0, 1 },
.dp_det = { 0x00c0, 25, 25, 0, 1 },
.idm_sink_en = { 0x0008, 8, 8, 0, 1 },
.idp_sink_en = { 0x0008, 7, 7, 0, 1 },
.idp_src_en = { 0x0008, 9, 9, 0, 1 },
.rdm_pdwn_en = { 0x0008, 10, 10, 0, 1 },
.vdm_src_en = { 0x0008, 12, 12, 0, 1 },
.vdp_src_en = { 0x0008, 11, 11, 0, 1 },
},
},
{
.reg = 0xfe8b0000,
.num_ports = 2,
.clkout_ctl = { 0x0008, 4, 4, 1, 0 },
.port_cfgs = {
[USB2PHY_PORT_OTG] = {
.phy_sus = { 0x0000, 8, 0, 0, 0x1d1 },
.ls_det_en = { 0x0080, 0, 0, 0, 1 },
.ls_det_st = { 0x0084, 0, 0, 0, 1 },
.ls_det_clr = { 0x008c, 0, 0, 0, 1 },
.utmi_ls = { 0x00c0, 5, 4, 0, 1 },
.utmi_hstdet = { 0x00c0, 7, 7, 0, 1 }
},
[USB2PHY_PORT_HOST] = {
.phy_sus = { 0x0004, 8, 0, 0, 0x1d1 },
.ls_det_en = { 0x0080, 1, 1, 0, 1 },
.ls_det_st = { 0x0084, 1, 1, 0, 1 },
.ls_det_clr = { 0x008c, 1, 1, 0, 1 },
.utmi_ls = { 0x00c0, 17, 16, 0, 1 },
.utmi_hstdet = { 0x00c0, 19, 19, 0, 1 }
}
},
},
{ /* sentinel */ }
};
static const struct udevice_id rockchip_usb2phy_ids[] = {
{ .compatible = "rockchip,rk1808-usb2phy", .data = (ulong)&rk1808_phy_cfgs },
{ .compatible = "rockchip,rk3128-usb2phy", .data = (ulong)&rk312x_phy_cfgs },
@ -1036,6 +1133,7 @@ static const struct udevice_id rockchip_usb2phy_ids[] = {
{ .compatible = "rockchip,rk3328-usb2phy", .data = (ulong)&rk3328_phy_cfgs },
{ .compatible = "rockchip,rk3368-usb2phy", .data = (ulong)&rk3368_phy_cfgs },
{ .compatible = "rockchip,rk3399-usb2phy", .data = (ulong)&rk3399_phy_cfgs },
{ .compatible = "rockchip,rk3568-usb2phy", .data = (ulong)&rk3568_phy_cfgs },
{ .compatible = "rockchip,rv1108-usb2phy", .data = (ulong)&rv1108_phy_cfgs },
{ }
};