diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index eddc6c57b485..591b984dc31b 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -131,6 +131,9 @@ struct rockchip_chg_det_reg { * @ls_det_en: linestate detection enable register. * @ls_det_st: linestate detection state register. * @ls_det_clr: linestate detection clear register. + * @iddig_output: iddig output from grf. + * @iddig_en: utmi iddig select between grf and phy, + * 0: from phy; 1: from grf * @idfall_det_en: id fall detection enable register. * @idfall_det_st: id fall detection state register. * @idfall_det_clr: id fall detection clear register. @@ -152,6 +155,8 @@ struct rockchip_usb2phy_port_cfg { struct usb2phy_reg ls_det_en; struct usb2phy_reg ls_det_st; struct usb2phy_reg ls_det_clr; + struct usb2phy_reg iddig_output; + struct usb2phy_reg iddig_en; struct usb2phy_reg idfall_det_en; struct usb2phy_reg idfall_det_st; struct usb2phy_reg idfall_det_clr; @@ -604,8 +609,7 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode) bool vbus_det_en; int ret = 0; - if (rport->port_id != USB2PHY_PORT_OTG || - !rport->vbus_always_on) + if (rport->port_id != USB2PHY_PORT_OTG) return ret; switch (mode) { @@ -633,6 +637,7 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode) return ret; } + static const struct phy_ops rockchip_usb2phy_ops = { .init = rockchip_usb2phy_init, .exit = rockchip_usb2phy_exit, @@ -642,6 +647,130 @@ static const struct phy_ops rockchip_usb2phy_ops = { .owner = THIS_MODULE, }; +/* Show & store the current value of otg mode for otg port */ +static ssize_t otg_mode_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct rockchip_usb2phy *rphy = dev_get_drvdata(device); + struct rockchip_usb2phy_port *rport = NULL; + int index; + + for (index = 0; index < rphy->phy_cfg->num_ports; index++) { + rport = &rphy->ports[index]; + if (rport->port_id == USB2PHY_PORT_OTG) + break; + } + + if (!rport) { + dev_err(rphy->dev, "Fail to get otg port\n"); + return -EINVAL; + } else if (rport->port_id != USB2PHY_PORT_OTG) { + dev_err(rphy->dev, "No support otg\n"); + return -EINVAL; + } + + switch (rport->mode) { + case USB_DR_MODE_HOST: + return sprintf(buf, "host\n"); + case USB_DR_MODE_PERIPHERAL: + return sprintf(buf, "peripheral\n"); + case USB_DR_MODE_OTG: + return sprintf(buf, "otg\n"); + case USB_DR_MODE_UNKNOWN: + return sprintf(buf, "UNKNOWN\n"); + } + + return -EINVAL; +} + +static ssize_t otg_mode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rockchip_usb2phy *rphy = dev_get_drvdata(device); + struct rockchip_usb2phy_port *rport = NULL; + struct regmap *base = get_reg_base(rphy); + enum usb_dr_mode new_dr_mode; + int index, rc = count; + + for (index = 0; index < rphy->phy_cfg->num_ports; index++) { + rport = &rphy->ports[index]; + if (rport->port_id == USB2PHY_PORT_OTG) + break; + } + + if (!rport) { + dev_err(rphy->dev, "Fail to get otg port\n"); + rc = -EINVAL; + goto err0; + } else if (rport->port_id != USB2PHY_PORT_OTG || + rport->mode == USB_DR_MODE_UNKNOWN) { + dev_err(rphy->dev, "No support otg\n"); + rc = -EINVAL; + goto err0; + } + + mutex_lock(&rport->mutex); + + if (!strncmp(buf, "0", 1) || !strncmp(buf, "otg", 3)) { + new_dr_mode = USB_DR_MODE_OTG; + } else if (!strncmp(buf, "1", 1) || !strncmp(buf, "host", 4)) { + new_dr_mode = USB_DR_MODE_HOST; + } else if (!strncmp(buf, "2", 1) || !strncmp(buf, "peripheral", 10)) { + new_dr_mode = USB_DR_MODE_PERIPHERAL; + } else { + dev_err(rphy->dev, "Error mode! Input 'otg' or 'host' or 'peripheral'\n"); + rc = -EINVAL; + goto err1; + } + + if (rport->mode == new_dr_mode) { + dev_warn(rphy->dev, "Same as current mode\n"); + goto err1; + } + + rport->mode = new_dr_mode; + + switch (rport->mode) { + case USB_DR_MODE_HOST: + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_HOST); + property_enable(base, &rport->port_cfg->iddig_output, false); + property_enable(base, &rport->port_cfg->iddig_en, true); + break; + case USB_DR_MODE_PERIPHERAL: + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_DEVICE); + property_enable(base, &rport->port_cfg->iddig_output, true); + property_enable(base, &rport->port_cfg->iddig_en, true); + break; + case USB_DR_MODE_OTG: + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_OTG); + property_enable(base, &rport->port_cfg->iddig_output, false); + property_enable(base, &rport->port_cfg->iddig_en, false); + break; + default: + break; + } + +err1: + mutex_unlock(&rport->mutex); + +err0: + return rc; +} +static DEVICE_ATTR_RW(otg_mode); + +/* Group all the usb2 phy attributes */ +static struct attribute *usb2_phy_attrs[] = { + &dev_attr_otg_mode.attr, + NULL, +}; + +static struct attribute_group usb2_phy_attr_group = { + .name = NULL, /* we want them in the same directory */ + .attrs = usb2_phy_attrs, +}; + static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) { struct rockchip_usb2phy_port *rport = @@ -747,6 +876,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) } else { sch_work = true; } + sch_work = true; break; case OTG_STATE_A_HOST: if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) { @@ -754,8 +884,10 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) rport->state = OTG_STATE_B_IDLE; mutex_unlock(&rport->mutex); rockchip_usb2phy_power_off(rport->phy); - return; + mutex_lock(&rport->mutex); + sch_work = true; } + break; default: mutex_unlock(&rport->mutex); return; @@ -1455,7 +1587,20 @@ next_child: } provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(provider); + if (IS_ERR(provider)) { + dev_err(dev, "Failed to register phy provider\n"); + ret = PTR_ERR(provider); + goto put_child; + } + + /* Attributes */ + ret = sysfs_create_group(&dev->kobj, &usb2_phy_attr_group); + if (ret) { + dev_err(dev, "Cannot create sysfs group: %d\n", ret); + goto put_child; + } + + return 0; put_child: of_node_put(child_np); @@ -1603,10 +1748,12 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { .clkout_ctl = { 0x0768, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { - .phy_sus = { 0x0760, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x0760, 8, 0, 0, 0x1d1 }, .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, + .iddig_output = { 0x0760, 10, 10, 0, 1 }, + .iddig_en = { 0x0760, 9, 9, 0, 1 }, .idfall_det_en = { 0x0680, 6, 6, 0, 1 }, .idfall_det_st = { 0x0690, 6, 6, 0, 1 }, .idfall_det_clr = { 0x06a0, 6, 6, 0, 1 }, @@ -1619,9 +1766,10 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { .utmi_bvalid = { 0x0480, 4, 4, 0, 1 }, .utmi_iddig = { 0x0480, 1, 1, 0, 1 }, .utmi_ls = { 0x0480, 3, 2, 0, 1 }, + .vbus_det_en = { 0x0788, 15, 15, 1, 0 }, }, [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x0764, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x0764, 8, 0, 0, 0x1d1 }, .ls_det_en = { 0x0680, 4, 4, 0, 1 }, .ls_det_st = { 0x0690, 4, 4, 0, 1 }, .ls_det_clr = { 0x06a0, 4, 4, 0, 1 } @@ -1646,16 +1794,16 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { .clkout_ctl = { 0x0808, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { - .phy_sus = { 0x800, 15, 0, 0, 0x1d1 }, - .ls_det_en = { 0x0684, 0, 0, 0, 1 }, - .ls_det_st = { 0x0694, 0, 0, 0, 1 }, - .ls_det_clr = { 0x06a4, 0, 0, 0, 1 } - }, - [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x804, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x804, 8, 0, 0, 0x1d1 }, .ls_det_en = { 0x0684, 1, 1, 0, 1 }, .ls_det_st = { 0x0694, 1, 1, 0, 1 }, .ls_det_clr = { 0x06a4, 1, 1, 0, 1 } + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x800, 8, 0, 0, 0x1d1 }, + .ls_det_en = { 0x0684, 0, 0, 0, 1 }, + .ls_det_st = { 0x0694, 0, 0, 0, 1 }, + .ls_det_clr = { 0x06a4, 0, 0, 0, 1 } } }, }, @@ -1669,10 +1817,12 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { .clkout_ctl = { 0x108, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { - .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x0100, 8, 0, 0, 0x1d1 }, .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, + .iddig_output = { 0x0100, 10, 10, 0, 1 }, + .iddig_en = { 0x0100, 9, 9, 0, 1 }, .idfall_det_en = { 0x0110, 5, 5, 0, 1 }, .idfall_det_st = { 0x0114, 5, 5, 0, 1 }, .idfall_det_clr = { 0x0118, 5, 5, 0, 1 }, @@ -1686,9 +1836,10 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, .utmi_iddig = { 0x0120, 6, 6, 0, 1 }, .utmi_ls = { 0x0120, 5, 4, 0, 1 }, + .vbus_det_en = { 0x001c, 15, 15, 1, 0 }, }, [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x104, 8, 0, 0, 0x1d1 }, .ls_det_en = { 0x110, 1, 1, 0, 1 }, .ls_det_st = { 0x114, 1, 1, 0, 1 }, .ls_det_clr = { 0x118, 1, 1, 0, 1 }, @@ -1720,7 +1871,7 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x0728, 8, 0, 0, 0x1d1 }, .ls_det_en = { 0x0680, 4, 4, 0, 1 }, .ls_det_st = { 0x0690, 4, 4, 0, 1 }, .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, @@ -1739,7 +1890,7 @@ static const struct rockchip_usb2phy_cfg rk3368_phy_cfgs[] = { .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, + .phy_sus = { 0x0728, 8, 0, 0, 0x1d1 }, .ls_det_en = { 0x0680, 4, 4, 0, 1 }, .ls_det_st = { 0x0690, 4, 4, 0, 1 }, .ls_det_clr = { 0x06a0, 4, 4, 0, 1 } @@ -1757,7 +1908,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { - .phy_sus = { 0xe454, 15, 0, 0x1452, 0x15d1 }, + .phy_sus = { 0xe454, 8, 0, 0x052, 0x1d1 }, .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, @@ -1805,7 +1956,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { - .phy_sus = { 0xe464, 15, 0, 0x1452, 0x15d1 }, + .phy_sus = { 0xe464, 8, 0, 0x052, 0x1d1 }, .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },