X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fphy%2Fphy-rockchip-inno-usb2.c;h=e9a5f51c91463cf8f3e1bf52ce6681eeeeec5f8f;hb=25a6f3608b293bffb079b883754ce299b35a1494;hp=4a798bd6624aaeb7ff63fde9544a8bb852331527;hpb=425dd637bb81aa66033adb93356976c393fa021a;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c index 4a798bd6624a..e9a5f51c9146 100644 --- a/drivers/phy/phy-rockchip-inno-usb2.c +++ b/drivers/phy/phy-rockchip-inno-usb2.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,7 @@ enum usb_chg_state { static const unsigned int rockchip_usb2phy_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, + EXTCON_USB_VBUS_EN, EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP, @@ -130,7 +132,15 @@ 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. - * @utmi_bvalid: utmi vbus valid status 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_iddig: otg port id pin status register. * @utmi_ls: utmi linestate state register. * @utmi_hstdet: utmi host disconnect register. */ @@ -142,7 +152,15 @@ 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_iddig; struct usb2phy_reg utmi_ls; struct usb2phy_reg utmi_hstdet; }; @@ -167,10 +185,15 @@ 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 + * flase - use bvalid to get vbus status * @vbus_attached: otg device vbus status. * @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. * @mutex: for register updating in sm_work. * @chg_work: charge detect work. * @otg_sm_work: OTG state machine work. @@ -180,14 +203,18 @@ struct rockchip_usb2phy_cfg { * @wakelock: wake lock struct to prevent system suspend * when USB is active. * @state: define OTG enumeration states before device reset. + * @mode: the dr_mode of the controller. */ struct rockchip_usb2phy_port { struct phy *phy; unsigned int port_id; + bool perip_connected; bool suspended; + bool utmi_avalid; bool vbus_attached; int bvalid_irq; int ls_irq; + int id_irq; struct mutex mutex; struct delayed_work chg_work; struct delayed_work otg_sm_work; @@ -196,6 +223,7 @@ struct rockchip_usb2phy_port { struct notifier_block event_nb; struct wake_lock wakelock; enum usb_otg_state state; + enum usb_dr_mode mode; }; /** @@ -208,6 +236,7 @@ struct rockchip_usb2phy_port { * @chg_type: USB charger types. * @dcd_retries: The retry count used to track Data contact * detection process. + * @edev_self: represent the source of extcon. * @edev: extcon device for notification registration * @phy_cfg: phy register configuration, assigned by driver data. * @ports: phy port instance. @@ -221,6 +250,8 @@ struct rockchip_usb2phy { enum usb_chg_state chg_state; 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]; @@ -266,7 +297,7 @@ static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw) return ret; /* waitting for the clk become stable */ - mdelay(1); + udelay(1200); } return 0; @@ -389,6 +420,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; @@ -400,48 +433,79 @@ static int rockchip_usb2phy_init(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - int ret; + int ret = 0; mutex_lock(&rport->mutex); if (rport->port_id == USB2PHY_PORT_OTG) { - /* clear bvalid status and enable bvalid detect irq */ - ret = property_enable(rphy, - &rport->port_cfg->bvalid_det_clr, true); - if (ret) { - mutex_unlock(&rport->mutex); - return ret; - } - - ret = property_enable(rphy, - &rport->port_cfg->bvalid_det_en, true); - if (ret) { - mutex_unlock(&rport->mutex); - return ret; - } + if (rport->mode != USB_DR_MODE_HOST) { + /* clear bvalid status and enable bvalid detect irq */ + ret = property_enable(rphy, + &rport->port_cfg-> + bvalid_det_clr, + true); + if (ret) + goto out; - mutex_unlock(&rport->mutex); - schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); + ret = property_enable(rphy, + &rport->port_cfg-> + bvalid_det_en, + true); + if (ret) + goto out; + + if (rphy->edev_self) { + ret = property_enable(rphy, + &rport->port_cfg-> + idfall_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy, + &rport->port_cfg-> + idfall_det_en, + true); + if (ret) + goto out; + + ret = property_enable(rphy, + &rport->port_cfg-> + idrise_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy, + &rport->port_cfg-> + idrise_det_en, + true); + if (ret) + goto out; + } + schedule_delayed_work(&rport->otg_sm_work, + OTG_SCHEDULE_DELAY); + } else { + /* If OTG works in host only mode, do nothing. */ + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); + } } else if (rport->port_id == USB2PHY_PORT_HOST) { /* clear linestate and enable linestate detect irq */ ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); - if (ret) { - mutex_unlock(&rport->mutex); - return ret; - } + if (ret) + goto out; ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); - if (ret) { - mutex_unlock(&rport->mutex); - return ret; - } + if (ret) + goto out; - mutex_unlock(&rport->mutex); schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); } - return 0; +out: + mutex_unlock(&rport->mutex); + return ret; } static int rockchip_usb2phy_power_on(struct phy *phy) @@ -492,10 +556,10 @@ static int rockchip_usb2phy_exit(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); - if (rport->port_id == USB2PHY_PORT_OTG) { + if (rport->port_id == USB2PHY_PORT_OTG && + rport->mode != USB_DR_MODE_HOST) cancel_delayed_work_sync(&rport->chg_work); - cancel_delayed_work_sync(&rport->otg_sm_work); - } else if (rport->port_id == USB2PHY_PORT_HOST) + else if (rport->port_id == USB2PHY_PORT_HOST) cancel_delayed_work_sync(&rport->sm_work); return 0; @@ -517,19 +581,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) + rport->vbus_attached = + property_enabled(rphy, &rport->port_cfg->utmi_avalid); + else + rport->vbus_attached = + property_enabled(rphy, &rport->port_cfg->utmi_bvalid); - vbus_attach = property_enabled(rphy, &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: @@ -538,7 +608,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: @@ -550,37 +620,35 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) dev_dbg(&rport->phy->dev, "sdp cable is connecetd\n"); wake_lock(&rport->wakelock); + 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 connecetd\n"); + cable = EXTCON_CHG_USB_DCP; rockchip_usb2phy_power_off(rport->phy); - notify_charger = true; sch_work = true; - cable = EXTCON_CHG_USB_DCP; break; case POWER_SUPPLY_TYPE_USB_CDP: dev_dbg(&rport->phy->dev, "cdp cable is connecetd\n"); wake_lock(&rport->wakelock); + 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; case POWER_SUPPLY_TYPE_USB_FLOATING: dev_dbg(&rport->phy->dev, "floating cable is connecetd\n"); + cable = EXTCON_CHG_USB_SLOW; rockchip_usb2phy_power_off(rport->phy); - notify_charger = true; sch_work = true; - cable = EXTCON_CHG_USB_SLOW; break; default: break; @@ -590,30 +658,23 @@ 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_cable_state_(rphy->edev, - cable, 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); 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) { @@ -621,11 +682,22 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) rport->state = OTG_STATE_B_IDLE; rockchip_usb2phy_power_off(rport->phy); } - break; + return; default: - break; + return; } + if (extcon_get_state(rphy->edev, cable) != rport->vbus_attached) + extcon_set_cable_state_(rphy->edev, + cable, rport->vbus_attached); + + if (rphy->edev_self && + (extcon_get_state(rphy->edev, EXTCON_USB) != + rport->perip_connected)) + extcon_set_cable_state_(rphy->edev, + EXTCON_USB, + rport->perip_connected); + if (sch_work) schedule_delayed_work(&rport->otg_sm_work, delay); } @@ -691,6 +763,7 @@ static void rockchip_chg_detect_work(struct work_struct *work) rockchip_chg_enable_dcd(rphy, true); rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; rphy->dcd_retries = 0; + rphy->primary_retries = 0; delay = CHG_DCD_POLL_TIME; break; case USB_CHG_STATE_WAIT_FOR_DCD: @@ -726,6 +799,19 @@ static void rockchip_chg_detect_work(struct work_struct *work) rphy->chg_state = USB_CHG_STATE_DETECTED; delay = 0; } else { + if (rphy->primary_retries < 2) { + /* Turn off DCD circuitry */ + rockchip_chg_enable_dcd(rphy, false); + /* Voltage Source on DP, Probe on DM */ + rockchip_chg_enable_primary_det(rphy, + true); + delay = CHG_PRIMARY_DET_TIME; + rphy->chg_state = + USB_CHG_STATE_DCD_DONE; + rphy->primary_retries++; + /* break USB_CHG_STATE_DCD_DONE */ + break; + } rphy->chg_type = POWER_SUPPLY_TYPE_USB; rphy->chg_state = USB_CHG_STATE_DETECTED; delay = 0; @@ -910,6 +996,38 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) return IRQ_HANDLED; } +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, &rport->port_cfg->idfall_det_st) && + !property_enabled(rphy, &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, &rport->port_cfg->idfall_det_st)) { + property_enable(rphy, &rport->port_cfg->idfall_det_clr, + true); + extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); + extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, true); + } else if (property_enabled(rphy, &rport->port_cfg->idrise_det_st)) { + property_enable(rphy, &rport->port_cfg->idrise_det_clr, + true); + extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); + extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, false); + } + + extcon_sync(rphy->edev, EXTCON_USB_HOST); + extcon_sync(rphy->edev, EXTCON_USB_VBUS_EN); + + 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) @@ -957,10 +1075,12 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, struct device_node *child_np) { int ret; + int iddig; rport->port_id = USB2PHY_PORT_OTG; rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; rport->state = OTG_STATE_UNDEFINED; + /* * set suspended flag to true, but actually don't * put phy in suspend mode, it aims to enable usb @@ -969,12 +1089,21 @@ 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); + + rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); + if (rport->mode == USB_DR_MODE_HOST) + return 0; + wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg"); INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); + rport->utmi_avalid = + of_property_read_bool(child_np, "rockchip,utmi-avalid"); + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); if (rport->bvalid_irq < 0) { dev_err(rphy->dev, "no vbus valid irq provided\n"); @@ -990,6 +1119,33 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, return ret; } + 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; + } + + iddig = property_enabled(rphy, &rport->port_cfg->utmi_iddig); + if (!iddig) { + extcon_set_state(rphy->edev, EXTCON_USB, false); + extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); + extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, true); + } else { + extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); + extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, false); + } + } + if (!IS_ERR(rphy->edev)) { rport->event_nb.notifier_call = rockchip_otg_event; @@ -1043,6 +1199,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); @@ -1188,7 +1345,15 @@ 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 }, - .utmi_bvalid = { 0xe2ac, 7, 7, 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 }, + .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, + .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, + .utmi_iddig = { 0xe2ac, 8, 8, 0, 1 }, }, [USB2PHY_PORT_HOST] = { .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, @@ -1217,6 +1382,21 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { .num_ports = 2, .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0xe464, 1, 0, 2, 1 }, + .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 }, + .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, + .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, + .utmi_iddig = { 0xe2ac, 11, 11, 0, 1 }, + }, [USB2PHY_PORT_HOST] = { .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, .ls_det_en = { 0xe3c0, 11, 11, 0, 1 },