From 432166cbd5206a897066ba23f0d3f69a6e830adb Mon Sep 17 00:00:00 2001 From: William Wu Date: Wed, 7 Jun 2017 21:39:33 +0800 Subject: [PATCH] phy: rockchip-inno-usb2: support to force otg mode This patch creates an usb2 phy attribute group and provides an attribute "otg_mode" for otg port to force otg mode independently of the voltage of otg id pin. In order to implement the force mode function, we can select otg plug indicator output (AKA iddig) from GRF, and set GRF USB otg plug indicator to "0" or "1" to control iddig status. We only support rk322x/rk3328 to force otg mode for the time being. And we need to disable usb auto suspend function if we want to force otg mode. Add 'usbcore.autosuspend=-1' in cmdline to disable usb auto suspend. Usage: [1] Force host mode echo host > /sys/devices/platform//mode [2] Force peripheral mode echo peripheral > /sys/devices/platform//mode [3] Force otg mode echo otg > /sys/devices/platform//mode Legacy Usage: [1] Force host mode echo 1 > /sys/devices/platform//mode [2] Force peripheral mode echo 2 > /sys/devices/platform//mode [3] Force otg mode echo 0 > /sys/devices/platform//mode Change-Id: I875b60b0390e3bd9af34b740cba8f5d53e1df752 Signed-off-by: William Wu --- drivers/phy/phy-rockchip-inno-usb2.c | 180 ++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 16 deletions(-) diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c index 6a04a4fb302f..020855c0576a 100644 --- a/drivers/phy/phy-rockchip-inno-usb2.c +++ b/drivers/phy/phy-rockchip-inno-usb2.c @@ -132,6 +132,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. @@ -153,6 +156,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; @@ -595,8 +600,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) { @@ -624,6 +628,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, @@ -633,6 +638,129 @@ 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; + 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(rphy, &rport->port_cfg->iddig_output, false); + property_enable(rphy, &rport->port_cfg->iddig_en, true); + break; + case USB_DR_MODE_PERIPHERAL: + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_DEVICE); + property_enable(rphy, &rport->port_cfg->iddig_output, true); + property_enable(rphy, &rport->port_cfg->iddig_en, true); + break; + case USB_DR_MODE_OTG: + rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_OTG); + property_enable(rphy, &rport->port_cfg->iddig_output, false); + property_enable(rphy, &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 = @@ -751,9 +879,8 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) rockchip_usb2phy_power_off(rport->phy); mutex_lock(&rport->mutex); wake_unlock(&rport->wakelock); - } else { - sch_work = true; } + sch_work = true; break; case OTG_STATE_A_HOST: if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { @@ -761,8 +888,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; @@ -1433,7 +1562,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); @@ -1601,10 +1743,12 @@ static const struct rockchip_usb2phy_cfg rk322x_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 }, @@ -1617,9 +1761,10 @@ static const struct rockchip_usb2phy_cfg rk322x_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 } @@ -1644,13 +1789,13 @@ static const struct rockchip_usb2phy_cfg rk322x_phy_cfgs[] = { .clkout_ctl = { 0x0808, 4, 4, 1, 0 }, .port_cfgs = { [USB2PHY_PORT_OTG] = { - .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, 15, 0, 0, 0x1d1 }, + .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 } @@ -1668,10 +1813,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 }, @@ -1685,9 +1832,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 }, @@ -1719,7 +1867,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 }, @@ -1738,7 +1886,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 } @@ -1756,7 +1904,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 }, @@ -1804,7 +1952,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 }, -- 2.34.1