mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-15 01:24:33 +00:00
usb: chipidea: add vbus interrupt handler
We add vbus interrupt handler at ci_otg_work, it uses OTGSC_BSV(at otgsc) to know it is connect or disconnet event. Meanwhile, we introduce two flags id_event and b_sess_valid_event to indicate it is an id interrupt or a vbus interrupt. Tested-by: Marek Vasut <marex@denx.de> Signed-off-by: Peter Chen <peter.chen@freescale.com> Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
cbec6bd55a
commit
a107f8c505
@ -132,6 +132,9 @@ struct hw_bank {
|
||||
* @transceiver: pointer to USB PHY, if any
|
||||
* @hcd: pointer to usb_hcd for ehci host driver
|
||||
* @debugfs: root dentry for this controller in debugfs
|
||||
* @id_event: indicates there is an id event, and handled at ci_otg_work
|
||||
* @b_sess_valid_event: indicates there is a vbus event, and handled
|
||||
* at ci_otg_work
|
||||
*/
|
||||
struct ci_hdrc {
|
||||
struct device *dev;
|
||||
@ -168,6 +171,8 @@ struct ci_hdrc {
|
||||
struct usb_phy *transceiver;
|
||||
struct usb_hcd *hcd;
|
||||
struct dentry *debugfs;
|
||||
bool id_event;
|
||||
bool b_sess_valid_event;
|
||||
};
|
||||
|
||||
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
|
||||
|
@ -303,16 +303,34 @@ static irqreturn_t ci_irq(int irq, void *data)
|
||||
if (ci->is_otg)
|
||||
otgsc = hw_read(ci, OP_OTGSC, ~0);
|
||||
|
||||
if (ci->role != CI_ROLE_END)
|
||||
ret = ci_role(ci)->irq(ci);
|
||||
|
||||
if (ci->is_otg && (otgsc & OTGSC_IDIS)) {
|
||||
hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
|
||||
/*
|
||||
* Handle id change interrupt, it indicates device/host function
|
||||
* switch.
|
||||
*/
|
||||
if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
|
||||
ci->id_event = true;
|
||||
ci_clear_otg_interrupt(ci, OTGSC_IDIS);
|
||||
disable_irq_nosync(ci->irq);
|
||||
queue_work(ci->wq, &ci->work);
|
||||
ret = IRQ_HANDLED;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle vbus change interrupt, it indicates device connection
|
||||
* and disconnection events.
|
||||
*/
|
||||
if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
|
||||
ci->b_sess_valid_event = true;
|
||||
ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
|
||||
disable_irq_nosync(ci->irq);
|
||||
queue_work(ci->wq, &ci->work);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Handle device/host interrupt */
|
||||
if (ci->role != CI_ROLE_END)
|
||||
ret = ci_role(ci)->irq(ci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -37,13 +37,23 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* ci_role_work - perform role changing based on ID pin
|
||||
* @work: work struct
|
||||
*/
|
||||
static void ci_role_work(struct work_struct *work)
|
||||
void ci_handle_vbus_change(struct ci_hdrc *ci)
|
||||
{
|
||||
u32 otgsc;
|
||||
|
||||
if (!ci->is_otg)
|
||||
return;
|
||||
|
||||
otgsc = hw_read(ci, OP_OTGSC, ~0);
|
||||
|
||||
if (otgsc & OTGSC_BSV)
|
||||
usb_gadget_vbus_connect(&ci->gadget);
|
||||
else
|
||||
usb_gadget_vbus_disconnect(&ci->gadget);
|
||||
}
|
||||
|
||||
static void ci_handle_id_switch(struct ci_hdrc *ci)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
|
||||
enum ci_role role = ci_otg_role(ci);
|
||||
|
||||
if (role != ci->role) {
|
||||
@ -53,17 +63,35 @@ static void ci_role_work(struct work_struct *work)
|
||||
ci_role_stop(ci);
|
||||
ci_role_start(ci, role);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ci_otg_work - perform otg (vbus/id) event handle
|
||||
* @work: work struct
|
||||
*/
|
||||
static void ci_otg_work(struct work_struct *work)
|
||||
{
|
||||
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
|
||||
|
||||
if (ci->id_event) {
|
||||
ci->id_event = false;
|
||||
ci_handle_id_switch(ci);
|
||||
} else if (ci->b_sess_valid_event) {
|
||||
ci->b_sess_valid_event = false;
|
||||
ci_handle_vbus_change(ci);
|
||||
} else
|
||||
dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
|
||||
|
||||
enable_irq(ci->irq);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ci_hdrc_otg_init - initialize otg struct
|
||||
* ci: the controller
|
||||
*/
|
||||
int ci_hdrc_otg_init(struct ci_hdrc *ci)
|
||||
{
|
||||
INIT_WORK(&ci->work, ci_role_work);
|
||||
INIT_WORK(&ci->work, ci_otg_work);
|
||||
ci->wq = create_singlethread_workqueue("ci_otg");
|
||||
if (!ci->wq) {
|
||||
dev_err(ci->dev, "can't create workqueue\n");
|
||||
|
@ -30,5 +30,6 @@ static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits)
|
||||
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);
|
||||
void ci_handle_vbus_change(struct ci_hdrc *ci);
|
||||
|
||||
#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
|
||||
|
@ -85,8 +85,10 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
|
||||
/* interrupt, error, port change, reset, sleep/suspend */
|
||||
hw_write(ci, OP_USBINTR, ~0,
|
||||
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
|
||||
} else {
|
||||
hw_write(ci, OP_USBINTR, ~0, 0);
|
||||
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1460,6 +1462,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
pm_runtime_get_sync(&_gadget->dev);
|
||||
hw_device_reset(ci, USBMODE_CM_DC);
|
||||
hw_device_state(ci, ci->ep0out->qh.dma);
|
||||
dev_dbg(ci->dev, "Connected to host\n");
|
||||
} else {
|
||||
hw_device_state(ci, 0);
|
||||
if (ci->platdata->notify_event)
|
||||
@ -1467,6 +1470,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
CI_HDRC_CONTROLLER_STOPPED_EVENT);
|
||||
_gadget_stop_activity(&ci->gadget);
|
||||
pm_runtime_put_sync(&_gadget->dev);
|
||||
dev_dbg(ci->dev, "Disconnected from host\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1822,6 +1826,9 @@ static int udc_start(struct ci_hdrc *ci)
|
||||
pm_runtime_no_callbacks(&ci->gadget.dev);
|
||||
pm_runtime_enable(&ci->gadget.dev);
|
||||
|
||||
/* Update ci->vbus_active */
|
||||
ci_handle_vbus_change(ci);
|
||||
|
||||
return retval;
|
||||
|
||||
remove_trans:
|
||||
|
Loading…
x
Reference in New Issue
Block a user