From 9afedca5f654ee792ed3291ec41101feafb7dd53 Mon Sep 17 00:00:00 2001 From: Meng Dongyang Date: Sun, 11 Sep 2016 16:30:04 +0800 Subject: [PATCH] usb: u2phy: add support for otg function In the case of platform designed in usb2.0 only mode, which the dwc3 controller connect without fusb302 and type-c phy does not work, the u2phy need to support hot plug and detect otg mode, this patch add support of otg function in this mode. Change-Id: I428a4f6d17d847c6114d124733e62c0a6236b94e Signed-off-by: Meng Dongyang Signed-off-by: Frank Wang --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 181 +++++++++++++++--- 1 file changed, 150 insertions(+), 31 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 6ef2584916cc..7a086ec2a706 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -130,6 +130,12 @@ 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. + * @idfall_det_en: id fall detection enable register. + * @idfall_det_st: id fall detection state register. + * @idfall_det_clr: id fall detection clear register. + * @idrise_det_en: id rise detection enable register. + * @idrise_det_st: id rise detection state register. + * @idrise_det_clr: id rise detection clear register. * @utmi_avalid: utmi vbus avalid status register. * @utmi_bvalid: utmi vbus bvalid status register. * @utmi_ls: utmi linestate state register. @@ -143,6 +149,12 @@ 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 idfall_det_en; + struct usb2phy_reg idfall_det_st; + struct usb2phy_reg idfall_det_clr; + struct usb2phy_reg idrise_det_en; + struct usb2phy_reg idrise_det_st; + struct usb2phy_reg idrise_det_clr; struct usb2phy_reg utmi_avalid; struct usb2phy_reg utmi_bvalid; struct usb2phy_reg utmi_ls; @@ -169,6 +181,7 @@ struct rockchip_usb2phy_cfg { /** * struct rockchip_usb2phy_port: usb-phy port data. * @port_id: flag for otg port or host port. + * @perip_connected: flag for periphyeral connect status. * @suspended: phy suspended flag. * @utmi_avalid: utmi avalid status usage flag. * true - use avalid to get vbus status @@ -177,6 +190,7 @@ struct rockchip_usb2phy_cfg { * @vbus_always_on: otg vbus is always powered on. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @ls_irq: IRQ number assigned for linestate detection. + * @id_irq: IRQ number assigned for id fall or rise detection. * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate * irqs to one irq in otg-port. * @mutex: for register updating in sm_work. @@ -191,12 +205,14 @@ struct rockchip_usb2phy_cfg { struct rockchip_usb2phy_port { struct phy *phy; unsigned int port_id; + bool perip_connected; bool suspended; bool utmi_avalid; bool vbus_attached; bool vbus_always_on; int bvalid_irq; int ls_irq; + int id_irq; int otg_mux_irq; struct mutex mutex; struct delayed_work chg_work; @@ -234,6 +250,7 @@ struct rockchip_usb2phy { enum power_supply_type chg_type; u8 dcd_retries; u8 primary_retries; + bool edev_self; struct extcon_dev *edev; const struct rockchip_usb2phy_cfg *phy_cfg; struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; @@ -410,6 +427,8 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) dev_err(rphy->dev, "failed to register extcon device\n"); return ret; } + + rphy->edev_self = true; } rphy->edev = edev; @@ -442,6 +461,36 @@ static int rockchip_usb2phy_init(struct phy *phy) if (ret) goto out; + if (rphy->edev_self) { + ret = property_enable(rphy->grf, + &rport->port_cfg-> + idfall_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, + &rport->port_cfg-> + idfall_det_en, + true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, + &rport->port_cfg-> + idrise_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy->grf, + &rport->port_cfg-> + idrise_det_en, + true); + if (ret) + goto out; + } + schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY * 3); } else { @@ -548,25 +597,25 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); static unsigned int cable; unsigned long delay; - bool vbus_attach, sch_work, notify_charger; + bool sch_work; if (rport->utmi_avalid) - vbus_attach = property_enabled(rphy->grf, - &rport->port_cfg->utmi_avalid); + rport->vbus_attached = + property_enabled(rphy->grf, &rport->port_cfg->utmi_avalid); else - vbus_attach = property_enabled(rphy->grf, - &rport->port_cfg->utmi_bvalid); + rport->vbus_attached = + property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid); sch_work = false; - notify_charger = false; delay = OTG_SCHEDULE_DELAY; + dev_dbg(&rport->phy->dev, "%s otg sm work\n", usb_otg_state_string(rport->state)); switch (rport->state) { case OTG_STATE_UNDEFINED: rport->state = OTG_STATE_B_IDLE; - if (!vbus_attach) + if (!rport->vbus_attached) rockchip_usb2phy_power_off(rport->phy); /* fall through */ case OTG_STATE_B_IDLE: @@ -575,7 +624,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) rport->state = OTG_STATE_A_HOST; rockchip_usb2phy_power_on(rport->phy); return; - } else if (vbus_attach) { + } else if (rport->vbus_attached) { dev_dbg(&rport->phy->dev, "vbus_attach\n"); switch (rphy->chg_state) { case USB_CHG_STATE_UNDEFINED: @@ -585,26 +634,25 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) switch (rphy->chg_type) { case POWER_SUPPLY_TYPE_USB: dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); + cable = EXTCON_CHG_USB_SDP; rockchip_usb2phy_power_on(rport->phy); rport->state = OTG_STATE_B_PERIPHERAL; - notify_charger = true; + rport->perip_connected = true; sch_work = true; - cable = EXTCON_CHG_USB_SDP; break; case POWER_SUPPLY_TYPE_USB_DCP: dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); - rockchip_usb2phy_power_off(rport->phy); - notify_charger = true; - sch_work = true; cable = EXTCON_CHG_USB_DCP; + rockchip_usb2phy_power_off(rport->phy); + sch_work = true; break; case POWER_SUPPLY_TYPE_USB_CDP: dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); + cable = EXTCON_CHG_USB_CDP; rockchip_usb2phy_power_on(rport->phy); rport->state = OTG_STATE_B_PERIPHERAL; - notify_charger = true; + rport->perip_connected = true; sch_work = true; - cable = EXTCON_CHG_USB_CDP; break; default: break; @@ -614,34 +662,22 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) break; } } else { - notify_charger = true; rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; } - - if (rport->vbus_attached != vbus_attach) { - rport->vbus_attached = vbus_attach; - - if (notify_charger && rphy->edev) { - extcon_set_state_sync(rphy->edev, - cable, vbus_attach); - if (cable == EXTCON_CHG_USB_SDP) - extcon_set_state_sync(rphy->edev, - EXTCON_USB, - vbus_attach); - } - } break; case OTG_STATE_B_PERIPHERAL: - if (!vbus_attach) { + if (!rport->vbus_attached) { dev_dbg(&rport->phy->dev, "usb disconnect\n"); rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; rport->state = OTG_STATE_B_IDLE; + rport->perip_connected = false; delay = 0; rockchip_usb2phy_power_off(rport->phy); + } else { + sch_work = true; } - sch_work = true; break; case OTG_STATE_A_HOST: if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) { @@ -654,6 +690,17 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) break; } + if (extcon_get_state(rphy->edev, cable) != rport->vbus_attached) + extcon_set_state_sync(rphy->edev, + cable, rport->vbus_attached); + + if (rphy->edev_self && + (extcon_get_state(rphy->edev, EXTCON_USB) != + rport->perip_connected)) + extcon_set_state_sync(rphy->edev, + EXTCON_USB, + rport->perip_connected); + if (sch_work) schedule_delayed_work(&rport->otg_sm_work, delay); } @@ -976,6 +1023,35 @@ static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) return IRQ_NONE; } +static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) +{ + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + + if (!property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st) && + !property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* clear id fall or rise detect irq pending status */ + if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) { + property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr, + true); + extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); + } else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) { + property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr, + true); + extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); + } + + extcon_sync(rphy->edev, EXTCON_USB_HOST); + + mutex_unlock(&rport->mutex); + + return IRQ_HANDLED; +} + static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) @@ -1036,6 +1112,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, */ rport->suspended = true; rport->vbus_attached = false; + rport->perip_connected = false; mutex_init(&rport->mutex); @@ -1110,6 +1187,23 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, } } + if (rphy->edev_self) { + rport->id_irq = of_irq_get_byname(child_np, "otg-id"); + if (rport->id_irq < 0) { + dev_err(rphy->dev, "no otg id irq provided\n"); + return rport->id_irq; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->id_irq, NULL, + rockchip_usb2phy_id_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_id", rport); + if (ret) { + dev_err(rphy->dev, "failed to request otg-id irq handle\n"); + return ret; + } + } + if (!IS_ERR(rphy->edev)) { rport->event_nb.notifier_call = rockchip_otg_event; @@ -1172,6 +1266,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) phy_cfgs = match->data; rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rphy->edev_self = false; platform_set_drvdata(pdev, rphy); ret = rockchip_usb2phy_extcon_register(rphy); @@ -1414,6 +1509,12 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, .bvalid_det_clr = { 0x06a0, 3, 3, 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 }, + .idrise_det_en = { 0x0680, 5, 5, 0, 1 }, + .idrise_det_st = { 0x0690, 5, 5, 0, 1 }, + .idrise_det_clr = { 0x06a0, 5, 5, 0, 1 }, .ls_det_en = { 0x0680, 2, 2, 0, 1 }, .ls_det_st = { 0x0690, 2, 2, 0, 1 }, .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, @@ -1473,6 +1574,12 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, .bvalid_det_clr = { 0x0118, 2, 2, 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 }, + .idrise_det_en = { 0x0110, 4, 4, 0, 1 }, + .idrise_det_st = { 0x0114, 4, 4, 0, 1 }, + .idrise_det_clr = { 0x0118, 4, 4, 0, 1 }, .ls_det_en = { 0x0110, 0, 0, 0, 1 }, .ls_det_st = { 0x0114, 0, 0, 0, 1 }, .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, @@ -1554,6 +1661,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, + .idfall_det_en = { 0xe3c0, 5, 5, 0, 1 }, + .idfall_det_st = { 0xe3e0, 5, 5, 0, 1 }, + .idfall_det_clr = { 0xe3d0, 5, 5, 0, 1 }, + .idrise_det_en = { 0xe3c0, 4, 4, 0, 1 }, + .idrise_det_st = { 0xe3e0, 4, 4, 0, 1 }, + .idrise_det_clr = { 0xe3d0, 4, 4, 0, 1 }, .ls_det_en = { 0xe3c0, 2, 2, 0, 1 }, .ls_det_st = { 0xe3e0, 2, 2, 0, 1 }, .ls_det_clr = { 0xe3d0, 2, 2, 0, 1 }, @@ -1594,6 +1707,12 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, + .idfall_det_en = { 0xe3c0, 10, 10, 0, 1 }, + .idfall_det_st = { 0xe3e0, 10, 10, 0, 1 }, + .idfall_det_clr = { 0xe3d0, 10, 10, 0, 1 }, + .idrise_det_en = { 0xe3c0, 9, 9, 0, 1 }, + .idrise_det_st = { 0xe3e0, 9, 9, 0, 1 }, + .idrise_det_clr = { 0xe3d0, 9, 9, 0, 1 }, .ls_det_en = { 0xe3c0, 7, 7, 0, 1 }, .ls_det_st = { 0xe3e0, 7, 7, 0, 1 }, .ls_det_clr = { 0xe3d0, 7, 7, 0, 1 },