We have more use cases need to consider for vbus/id handler, below are current use cases we know now: - The vbus/id value can be read from external connectors, eg, gpio - The vbus/id value can be read from register OTGSC - The vbus is always on - There is no ID pin, but need to switch role through debugfs So, in order to cover all cases, we improve the OTG handler like below: - ci->is_otg flag means it is OTG controller, and the register OTGSC can be accessed, but vbus/id value from OTG registers are not mandatory. - ci_otg workqueue is used to get id/vbus value, no matter the values are from extcon or register OTGSC. - When we try to get id/vbus value, we try to get them from extcon first, then from register OTGSC. - Introduce two new APIs ci_write_otgsc and ci_read_otgsc to get vbus and id value. The hw_read_otgsc and hw_read_otgsc are only used to access OTGSC register at OTG FSM mode only. Signed-off-by: Peter Chen <peter.chen@xxxxxxx> cc: Sanchayan Maity <maitysanchayan@xxxxxxxxx> cc: ivan.ivanov@xxxxxxxxxx --- drivers/usb/chipidea/ci.h | 5 +- drivers/usb/chipidea/core.c | 43 ++++++---------- drivers/usb/chipidea/debug.c | 6 +-- drivers/usb/chipidea/otg.c | 116 ++++++++++++++++++++++++++++--------------- drivers/usb/chipidea/otg.h | 2 + drivers/usb/chipidea/udc.c | 10 ++-- 6 files changed, 103 insertions(+), 79 deletions(-) diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index f81dd3b..07141a6 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -162,7 +162,7 @@ struct hw_bank { * @irq: IRQ number * @roles: array of supported roles for this controller * @role: current role - * @is_otg: if the device is otg-capable + * @is_otg: if the device is otg-capable (the register OTGSC can be accessed) * @fsm: otg finite state machine * @otg_fsm_hrtimer: hrtimer for otg fsm timers * @hr_timeouts: time out list for active otg fsm timers @@ -200,6 +200,8 @@ struct hw_bank { * @wakeup_int: if wakeup interrupt occur * @rev: The revision number for controller * @dp_always_pullup: keep dp always pullup at device mode + * @fake_otgsc: record id/vbus status when these two values are not from + * registers */ struct ci_hdrc { struct device *dev; @@ -250,6 +252,7 @@ struct ci_hdrc { bool wakeup_int; enum ci_revision rev; bool dp_always_pullup; + u32 fake_otgsc; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 6f22c6c..b37bc52 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -562,23 +562,21 @@ static irqreturn_t ci_irq(int irq, void *data) return IRQ_HANDLED; } - if (ci->is_otg) { - otgsc = hw_read_otgsc(ci, ~0); - if (ci_otg_is_fsm_mode(ci)) { - ret = ci_otg_fsm_irq(ci); - if (ret == IRQ_HANDLED) - return ret; - } + otgsc = ci_read_otgsc(ci, ~0); + if (ci_otg_is_fsm_mode(ci)) { + ret = ci_otg_fsm_irq(ci); + if (ret == IRQ_HANDLED) + return ret; } /* * Handle id change interrupt, it indicates device/host function * switch. */ - if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { + if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { ci->id_event = true; /* Clear ID change irq status */ - hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); + ci_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); ci_otg_queue_work(ci); return IRQ_HANDLED; } @@ -587,10 +585,10 @@ static irqreturn_t ci_irq(int irq, void *data) * Handle vbus change interrupt, it indicates device connection * and disconnection events. */ - if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { + if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { ci->b_sess_valid_event = true; /* Clear BSV irq */ - hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); + ci_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); ci_otg_queue_work(ci); return IRQ_HANDLED; } @@ -909,12 +907,12 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC | DCCPARAMS_HC) == (DCCPARAMS_DC | DCCPARAMS_HC)); - if (ci->is_otg) { - dev_dbg(ci->dev, "It is OTG capable controller\n"); - /* Disable and clear all OTG irq */ - hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, - OTGSC_INT_STATUS_BITS); - } + /* Disable and clear all OTG irq */ + ci_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, + OTGSC_INT_STATUS_BITS); + + dev_dbg(ci->dev, "It is %sOTG capable controller\n", + ci->is_otg ? "" : "non-"); } static int ci_hdrc_probe(struct platform_device *pdev) @@ -1023,18 +1021,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) } if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { - if (ci->is_otg) { ci->role = ci_otg_role(ci); /* Enable ID change irq */ - hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); - } else { - /* - * If the controller is not OTG capable, but support - * role switch, the defalt role is gadget, and the - * user can switch it through debugfs. - */ - ci->role = CI_ROLE_GADGET; - } + ci_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); } else { ci->role = ci->roles[CI_ROLE_HOST] ? CI_ROLE_HOST diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 6d23eed..91b8c26 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -367,10 +367,8 @@ static int ci_registers_show(struct seq_file *s, void *unused) tmp_reg = hw_read(ci, OP_PORTSC, ~0); seq_printf(s, "PORTSC reg: %08x\n", tmp_reg); - if (ci->is_otg) { - tmp_reg = hw_read_otgsc(ci, ~0); - seq_printf(s, "OTGSC reg: %08x\n", tmp_reg); - } + tmp_reg = ci_read_otgsc(ci, ~0); + seq_printf(s, "OTGSC(may be fake one) reg : %08x\n", tmp_reg); return 0; } diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 58be59e..b02a80e 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -25,53 +25,76 @@ #include "otg_fsm.h" /** - * hw_read_otgsc returns otgsc register bits value. + * hw_read_otgsc returns otgsc register bits value, for OTG FSM only. * @mask: bitfield mask */ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) { - struct ci_hdrc_cable *cable; - u32 val = hw_read(ci, OP_OTGSC, mask); - - /* - * If using extcon framework for VBUS and/or ID signal - * detection overwrite OTGSC register value - */ - cable = &ci->platdata->vbus_extcon; - if (!IS_ERR(cable->edev)) { - if (cable->changed) - val |= OTGSC_BSVIS; - else - val &= ~OTGSC_BSVIS; - - cable->changed = false; - - if (cable->state) - val |= OTGSC_BSV; - else - val &= ~OTGSC_BSV; - } - - cable = &ci->platdata->id_extcon; - if (!IS_ERR(cable->edev)) { - if (cable->changed) - val |= OTGSC_IDIS; - else - val &= ~OTGSC_IDIS; - - cable->changed = false; + return hw_read(ci, OP_OTGSC, mask); +} - if (cable->state) - val |= OTGSC_ID; - else - val &= ~OTGSC_ID; +/** + * ci_read_otgsc returns otgsc bits value. + * The value may from: + * - register OTGSC + * - extcon status + * + * @ci: the controller struct + * @mask: bitfield mask + */ +u32 ci_read_otgsc(struct ci_hdrc *ci, u32 mask) +{ + struct ci_hdrc_cable *cable_vbus; + struct ci_hdrc_cable *cable_id; + u32 val = ci->fake_otgsc; + + cable_vbus = &ci->platdata->vbus_extcon; + cable_id = &ci->platdata->vbus_extcon; + if (!IS_ERR(cable_vbus->edev) || !IS_ERR(cable_id->edev)) { + if (!IS_ERR(cable_vbus->edev)) { + if (cable_vbus->changed) + val |= OTGSC_BSVIS; + else + val &= ~OTGSC_BSVIS; + + cable_vbus->changed = false; + + if (cable_vbus->state) + val |= OTGSC_BSV; + else + val &= ~OTGSC_BSV; + } + + if (!IS_ERR(cable_id->edev)) { + if (cable_id->changed) + val |= OTGSC_IDIS; + else + val &= ~OTGSC_IDIS; + + cable_id->changed = false; + + if (cable_id->state) + val |= OTGSC_ID; + else + val &= ~OTGSC_ID; + } + return val; + } else if (ci->is_otg) { /* read otgsc from register */ + return hw_read_otgsc(ci, mask); + } else if (mask == OTGSC_ID) { + /* + * Can't decide role from external pin, but the controller + * is dual role, we take the default role as gadget, + * and the user can switch it through debugfs. + */ + return OTGSC_ID; + } else { + return 0; } - - return val; } /** - * hw_write_otgsc updates target bits of OTGSC register. + * hw_write_otgsc updates target bits of OTGSC register, for OTG FSM only * @mask: bitfield mask * @data: to be written */ @@ -81,12 +104,23 @@ void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) } /** + * ci_write_otgsc: only update register for OTG controller + * @mask: bitfield mask + * @data: to be written + */ +void ci_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) +{ + if (ci->is_otg) + hw_write_otgsc(ci, mask, data); +} + +/** * ci_otg_role - pick role based on ID pin state * @ci: the controller */ enum ci_role ci_otg_role(struct ci_hdrc *ci) { - enum ci_role role = hw_read_otgsc(ci, OTGSC_ID) + enum ci_role role = ci_read_otgsc(ci, OTGSC_ID) ? CI_ROLE_GADGET : CI_ROLE_HOST; @@ -100,7 +134,7 @@ void ci_handle_vbus_change(struct ci_hdrc *ci) return; } - if (hw_read_otgsc(ci, OTGSC_BSV)) + if (ci_read_otgsc(ci, OTGSC_BSV)) usb_gadget_vbus_connect(&ci->gadget); else usb_gadget_vbus_disconnect(&ci->gadget); @@ -183,7 +217,7 @@ void ci_hdrc_otg_destroy(struct ci_hdrc *ci) destroy_workqueue(ci->wq); } /* Disable all OTG irq and clear status */ - hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, + ci_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, OTGSC_INT_STATUS_BITS); if (ci_otg_is_fsm_mode(ci)) ci_hdrc_otg_fsm_remove(ci); diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 9ecb598..26b6548 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -12,7 +12,9 @@ #define __DRIVERS_USB_CHIPIDEA_OTG_H u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask); +u32 ci_read_otgsc(struct ci_hdrc *ci, u32 mask); void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data); +void ci_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data); int ci_hdrc_otg_init(struct ci_hdrc *ci); void ci_hdrc_otg_destroy(struct ci_hdrc *ci); enum ci_role ci_otg_role(struct ci_hdrc *ci); diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 065f5d9..6986a50 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1956,10 +1956,9 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) static int udc_id_switch_for_device(struct ci_hdrc *ci) { - if (ci->is_otg) - /* Clear and enable BSV irq */ - hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, - OTGSC_BSVIS | OTGSC_BSVIE); + /* Clear and enable BSV irq */ + ci_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE, + OTGSC_BSVIS | OTGSC_BSVIE); return 0; } @@ -1970,8 +1969,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci) * host doesn't care B_SESSION_VALID event * so clear and disbale BSV irq */ - if (ci->is_otg) - hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); + ci_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS); } /** -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html