phy: rockchip-inno-usb2: support to force otg mode
authorWilliam Wu <william.wu@rock-chips.com>
Wed, 7 Jun 2017 13:39:33 +0000 (21:39 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Mon, 12 Jun 2017 02:56:37 +0000 (10:56 +0800)
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/<u2phy dev name>/mode

[2] Force peripheral mode
    echo peripheral > /sys/devices/platform/<u2phy dev name>/mode

[3] Force otg mode
    echo otg > /sys/devices/platform/<u2phy dev name>/mode

Legacy Usage:
[1] Force host mode
    echo 1 > /sys/devices/platform/<u2phy dev name>/mode

[2] Force peripheral mode
    echo 2 > /sys/devices/platform/<u2phy dev name>/mode

[3] Force otg mode
    echo 0 > /sys/devices/platform/<u2phy dev name>/mode

Change-Id: I875b60b0390e3bd9af34b740cba8f5d53e1df752
Signed-off-by: William Wu <william.wu@rock-chips.com>
drivers/phy/phy-rockchip-inno-usb2.c

index 6a04a4fb302f130cb5dabb0c3b6e18071e5f7fe0..020855c0576a8d7ab530e1c5a26f91ea6178ecbc 100644 (file)
@@ -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 },