From 0c33bf781a0da4bdab207ccc323c9afa940852af Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 23 Apr 2014 15:56:38 +0800 Subject: usb: chipidea: operate on otgsc register in a general way Use a more general way to read and write otgsc register. Signed-off-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/otg.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/usb/chipidea/otg.c') diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 39bd7ec8bf75..c6943401a0b3 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -23,14 +23,32 @@ #include "bits.h" #include "otg.h" +/** + * hw_read_otgsc returns otgsc register bits value. + * @mask: bitfield mask + */ +u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) +{ + return hw_read(ci, OP_OTGSC, mask); +} + +/** + * hw_write_otgsc updates target bits of OTGSC register. + * @mask: bitfield mask + * @data: to be written + */ +void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) +{ + hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data); +} + /** * ci_otg_role - pick role based on ID pin state * @ci: the controller */ enum ci_role ci_otg_role(struct ci_hdrc *ci) { - u32 sts = hw_read(ci, OP_OTGSC, ~0); - enum ci_role role = sts & OTGSC_ID + enum ci_role role = hw_read_otgsc(ci, OTGSC_ID) ? CI_ROLE_GADGET : CI_ROLE_HOST; @@ -39,14 +57,10 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci) 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) + if (hw_read_otgsc(ci, OTGSC_BSV)) usb_gadget_vbus_connect(&ci->gadget); else usb_gadget_vbus_disconnect(&ci->gadget); @@ -115,6 +129,7 @@ void ci_hdrc_otg_destroy(struct ci_hdrc *ci) flush_workqueue(ci->wq); destroy_workqueue(ci->wq); } - ci_disable_otg_interrupt(ci, OTGSC_INT_EN_BITS); - ci_clear_otg_interrupt(ci, OTGSC_INT_STATUS_BITS); + /* Disable all OTG irq and clear status */ + hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, + OTGSC_INT_STATUS_BITS); } -- cgit v1.2.3 From 57677be5ef838743561f4c1d16821dda0438d362 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 23 Apr 2014 15:56:44 +0800 Subject: usb: chipidea: usb OTG fsm initialization. This patch adds OTG fsm related initialization when do otg init, add a seperate file for OTG fsm related utilities. Signed-off-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/Makefile | 1 + drivers/usb/chipidea/ci.h | 17 ++++++++++++ drivers/usb/chipidea/otg.c | 4 +++ drivers/usb/chipidea/otg_fsm.c | 63 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg_fsm.h | 29 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 drivers/usb/chipidea/otg_fsm.c create mode 100644 drivers/usb/chipidea/otg_fsm.h (limited to 'drivers/usb/chipidea/otg.c') diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 480bd4d5710a..2f099c7df7b5 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -6,6 +6,7 @@ ci_hdrc-y := core.o otg.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC) += udc.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST) += host.o ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o +ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o # Glue/Bridge layers go here diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 7ae8cb680321..bd3529f29e86 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -17,6 +17,7 @@ #include #include #include +#include /****************************************************************************** * DEFINE @@ -139,6 +140,7 @@ struct hw_bank { * @roles: array of supported roles for this controller * @role: current role * @is_otg: if the device is otg-capable + * @fsm: otg finite state machine * @work: work for role changing * @wq: workqueue thread * @qh_pool: allocation pool for queue heads @@ -174,6 +176,7 @@ struct ci_hdrc { struct ci_role_driver *roles[CI_ROLE_END]; enum ci_role role; bool is_otg; + struct otg_fsm fsm; struct work_struct work; struct workqueue_struct *wq; @@ -319,6 +322,20 @@ static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg, return (val & mask) >> __ffs(mask); } +/** + * ci_otg_is_fsm_mode: runtime check if otg controller + * is in otg fsm mode. + */ +static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci) +{ +#ifdef CONFIG_USB_OTG_FSM + return ci->is_otg && ci->roles[CI_ROLE_HOST] && + ci->roles[CI_ROLE_GADGET]; +#else + return false; +#endif +} + u32 hw_read_intr_enable(struct ci_hdrc *ci); u32 hw_read_intr_status(struct ci_hdrc *ci); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index c6943401a0b3..d76db51ecda7 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -22,6 +22,7 @@ #include "ci.h" #include "bits.h" #include "otg.h" +#include "otg_fsm.h" /** * hw_read_otgsc returns otgsc register bits value. @@ -116,6 +117,9 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci) return -ENODEV; } + if (ci_otg_is_fsm_mode(ci)) + return ci_hdrc_otg_fsm_init(ci); + return 0; } diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c new file mode 100644 index 000000000000..d9dfaa3cf40c --- /dev/null +++ b/drivers/usb/chipidea/otg_fsm.c @@ -0,0 +1,63 @@ +/* + * otg_fsm.c - ChipIdea USB IP core OTG FSM driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Jun Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * This file mainly handles OTG fsm, it includes OTG fsm operations + * for HNP and SRP. + */ + +#include +#include +#include +#include + +#include "ci.h" +#include "bits.h" +#include "otg.h" +#include "otg_fsm.h" + +int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) +{ + struct usb_otg *otg; + + otg = devm_kzalloc(ci->dev, + sizeof(struct usb_otg), GFP_KERNEL); + if (!otg) { + dev_err(ci->dev, + "Failed to allocate usb_otg structure for ci hdrc otg!\n"); + return -ENOMEM; + } + + otg->phy = ci->transceiver; + otg->gadget = &ci->gadget; + ci->fsm.otg = otg; + ci->transceiver->otg = ci->fsm.otg; + ci->fsm.power_up = 1; + ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0; + ci->transceiver->state = OTG_STATE_UNDEFINED; + + mutex_init(&ci->fsm.lock); + + /* Enable A vbus valid irq */ + hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE); + + if (ci->fsm.id) { + ci->fsm.b_ssend_srp = + hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1; + ci->fsm.b_sess_vld = + hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0; + /* Enable BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE); + } + + return 0; +} diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h new file mode 100644 index 000000000000..bf20a851b601 --- /dev/null +++ b/drivers/usb/chipidea/otg_fsm.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Jun Li + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_USB_CHIPIDEA_OTG_FSM_H +#define __DRIVERS_USB_CHIPIDEA_OTG_FSM_H + +#include + +#ifdef CONFIG_USB_OTG_FSM + +int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); + +#else + +static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) +{ + return 0; +} + +#endif + +#endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ -- cgit v1.2.3 From 4dcf720c5d40b27c916e7115ad75b335c9c1e264 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 23 Apr 2014 15:56:50 +0800 Subject: usb: chipidea: OTG HNP and SRP fsm implementation USB OTG interrupt handling and fsm transitions according to USB OTG and EH 2.0. Signed-off-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 24 +++- drivers/usb/chipidea/otg.c | 9 +- drivers/usb/chipidea/otg_fsm.c | 243 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg_fsm.h | 18 +++ drivers/usb/chipidea/udc.c | 8 ++ 5 files changed, 294 insertions(+), 8 deletions(-) (limited to 'drivers/usb/chipidea/otg.c') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 6a6379a8f1f5..128b92ba58a8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -42,7 +42,6 @@ * - Not Supported: 15 & 16 (ISO) * * TODO List - * - OTG * - Interrupt Traffic * - GET_STATUS(device) - always reports 0 * - Gadget API (majority of optional features) @@ -74,6 +73,7 @@ #include "host.h" #include "debug.h" #include "otg.h" +#include "otg_fsm.h" /* Controller register map */ static const u8 ci_regs_nolpm[] = { @@ -411,8 +411,14 @@ static irqreturn_t ci_irq(int irq, void *data) irqreturn_t ret = IRQ_NONE; u32 otgsc = 0; - if (ci->is_otg) + 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; + } + } /* * Handle id change interrupt, it indicates device/host function @@ -691,10 +697,13 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ci->role == CI_ROLE_GADGET) ci_handle_vbus_change(ci); - ret = ci_role_start(ci, ci->role); - if (ret) { - dev_err(dev, "can't start %s role\n", ci_role(ci)->name); - goto stop; + if (!ci_otg_is_fsm_mode(ci)) { + ret = ci_role_start(ci, ci->role); + if (ret) { + dev_err(dev, "can't start %s role\n", + ci_role(ci)->name); + goto stop; + } } platform_set_drvdata(pdev, ci); @@ -703,6 +712,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; + if (ci_otg_is_fsm_mode(ci)) + ci_hdrc_otg_fsm_start(ci); + ret = dbg_create_files(ci); if (!ret) return 0; diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index d76db51ecda7..38e340cba1f9 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -11,8 +11,8 @@ */ /* - * This file mainly handles otgsc register, it may include OTG operation - * in the future. + * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP + * are also included. */ #include @@ -91,6 +91,11 @@ static void ci_otg_work(struct work_struct *work) { struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); + if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) { + enable_irq(ci->irq); + return; + } + if (ci->id_event) { ci->id_event = false; ci_handle_id_switch(ci); diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index bb64fb486eef..67902a16cb74 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -13,6 +13,10 @@ /* * This file mainly handles OTG fsm, it includes OTG fsm operations * for HNP and SRP. + * + * TODO List + * - ADP + * - OTG test device */ #include @@ -93,6 +97,33 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) hw_write_otgsc(ci, OTGSC_1MSIE, 0); } +/* + * Reduce timer count by 1, and find timeout conditions. + * Called by otg 1ms timer interrupt + */ +static inline int ci_otg_tick_timer(struct ci_hdrc *ci) +{ + struct ci_otg_fsm_timer *tmp_timer, *del_tmp; + struct list_head *active_timers = &ci->fsm_timer->active_timers; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(ci, tmp_timer->data); + expired = 1; + } + } + + /* disable 1ms irq if there is no any timer active */ + if ((expired == 1) && list_empty(active_timers)) + hw_write_otgsc(ci, OTGSC_1MSIE, 0); + + return expired; +} + /* The timeout callback function to set time out bit */ static void set_tmout(void *ptr, unsigned long indicator) { @@ -394,6 +425,218 @@ static struct otg_fsm_ops ci_otg_ops = { .start_gadget = ci_otg_start_gadget, }; +int ci_otg_fsm_work(struct ci_hdrc *ci) +{ + /* + * Don't do fsm transition for B device + * when there is no gadget class driver + */ + if (ci->fsm.id && !(ci->driver) && + ci->transceiver->state < OTG_STATE_A_IDLE) + return 0; + + if (otg_statemachine(&ci->fsm)) { + if (ci->transceiver->state == OTG_STATE_A_IDLE) { + /* + * Further state change for cases: + * a_idle to b_idle; or + * a_idle to a_wait_vrise due to ID change(1->0), so + * B-dev becomes A-dev can try to start new session + * consequently; or + * a_idle to a_wait_vrise when power up + */ + if ((ci->fsm.id) || (ci->id_event) || + (ci->fsm.power_up)) { + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + if (ci->id_event) + ci->id_event = false; + } else if (ci->transceiver->state == OTG_STATE_B_IDLE) { + if (ci->fsm.b_sess_vld) { + ci->fsm.power_up = 0; + /* + * Further transite to b_periphearl state + * when register gadget driver with vbus on + */ + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + } + } + return 0; +} + +/* + * Update fsm variables in each state if catching expected interrupts, + * called by otg fsm isr. + */ +static void ci_otg_fsm_event(struct ci_hdrc *ci) +{ + u32 intr_sts, otg_bsess_vld, port_conn; + struct otg_fsm *fsm = &ci->fsm; + + intr_sts = hw_read_intr_status(ci); + otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV); + port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS); + + switch (ci->transceiver->state) { + case OTG_STATE_A_WAIT_BCON: + if (port_conn) { + fsm->b_conn = 1; + fsm->a_bus_req = 1; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + break; + case OTG_STATE_B_IDLE: + if (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) { + fsm->b_sess_vld = 1; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + break; + case OTG_STATE_B_PERIPHERAL: + if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) { + fsm->a_bus_suspend = 1; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } else if (intr_sts & USBi_PCI) { + if (fsm->a_bus_suspend == 1) + fsm->a_bus_suspend = 0; + } + break; + case OTG_STATE_B_HOST: + if ((intr_sts & USBi_PCI) && !port_conn) { + fsm->a_conn = 0; + fsm->b_bus_req = 0; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + ci_otg_add_timer(ci, B_SESS_VLD); + } + break; + case OTG_STATE_A_PERIPHERAL: + if (intr_sts & USBi_SLI) { + fsm->b_bus_suspend = 1; + /* + * Init a timer to know how long this suspend + * will contine, if time out, indicates B no longer + * wants to be host role + */ + ci_otg_add_timer(ci, A_BIDL_ADIS); + } + + if (intr_sts & USBi_URI) + ci_otg_del_timer(ci, A_BIDL_ADIS); + + if (intr_sts & USBi_PCI) { + if (fsm->b_bus_suspend == 1) { + ci_otg_del_timer(ci, A_BIDL_ADIS); + fsm->b_bus_suspend = 0; + } + } + break; + case OTG_STATE_A_SUSPEND: + if ((intr_sts & USBi_PCI) && !port_conn) { + fsm->b_conn = 0; + + /* if gadget driver is binded */ + if (ci->driver) { + /* A device to be peripheral mode */ + ci->gadget.is_a_peripheral = 1; + } + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + break; + case OTG_STATE_A_HOST: + if ((intr_sts & USBi_PCI) && !port_conn) { + fsm->b_conn = 0; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + break; + case OTG_STATE_B_WAIT_ACON: + if ((intr_sts & USBi_PCI) && port_conn) { + fsm->a_conn = 1; + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + } + break; + default: + break; + } +} + +/* + * ci_otg_irq - otg fsm related irq handling + * and also update otg fsm variable by monitoring usb host and udc + * state change interrupts. + * @ci: ci_hdrc + */ +irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) +{ + irqreturn_t retval = IRQ_NONE; + u32 otgsc, otg_int_src = 0; + struct otg_fsm *fsm = &ci->fsm; + + otgsc = hw_read_otgsc(ci, ~0); + otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8); + fsm->id = (otgsc & OTGSC_ID) ? 1 : 0; + + if (otg_int_src) { + if (otg_int_src & OTGSC_1MSIS) { + hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS); + retval = ci_otg_tick_timer(ci); + return IRQ_HANDLED; + } else if (otg_int_src & OTGSC_DPIS) { + hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS); + fsm->a_srp_det = 1; + fsm->a_bus_drop = 0; + } else if (otg_int_src & OTGSC_IDIS) { + hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); + if (fsm->id == 0) { + fsm->a_bus_drop = 0; + fsm->a_bus_req = 1; + ci->id_event = true; + } + } else if (otg_int_src & OTGSC_BSVIS) { + hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); + if (otgsc & OTGSC_BSV) { + fsm->b_sess_vld = 1; + ci_otg_del_timer(ci, B_SSEND_SRP); + ci_otg_del_timer(ci, B_SRP_FAIL); + fsm->b_ssend_srp = 0; + } else { + fsm->b_sess_vld = 0; + if (fsm->id) + ci_otg_add_timer(ci, B_SSEND_SRP); + } + } else if (otg_int_src & OTGSC_AVVIS) { + hw_write_otgsc(ci, OTGSC_AVVIS, OTGSC_AVVIS); + if (otgsc & OTGSC_AVV) { + fsm->a_vbus_vld = 1; + } else { + fsm->a_vbus_vld = 0; + fsm->b_conn = 0; + } + } + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + return IRQ_HANDLED; + } + + ci_otg_fsm_event(ci); + + return retval; +} + +void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) +{ + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); +} + int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) { int retval = 0; diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 420f081e7e6a..6ec8247d8179 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h @@ -92,6 +92,9 @@ struct ci_otg_fsm_timer_list { #ifdef CONFIG_USB_OTG_FSM int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); +int ci_otg_fsm_work(struct ci_hdrc *ci); +irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci); +void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci); #else @@ -100,6 +103,21 @@ static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) return 0; } +static inline int ci_otg_fsm_work(struct ci_hdrc *ci) +{ + return -ENXIO; +} + +static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) +{ + return IRQ_NONE; +} + +static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index cba7fd63d6e3..150592f10b3d 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -28,6 +28,7 @@ #include "bits.h" #include "debug.h" #include "otg.h" +#include "otg_fsm.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -1644,6 +1645,13 @@ static int ci_udc_start(struct usb_gadget *gadget, return retval; ci->driver = driver; + + /* Start otg fsm for B-device */ + if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) { + ci_hdrc_otg_fsm_start(ci); + return retval; + } + pm_runtime_get_sync(&ci->gadget.dev); if (ci->vbus_active) { spin_lock_irqsave(&ci->lock, flags); -- cgit v1.2.3 From 15f75defacd3da412d4c9823d4a9c9f410923766 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Wed, 23 Apr 2014 15:56:51 +0800 Subject: usb: chipidea: add sys inputs for OTG fsm input This patch adds sys input to control and show OTG fsm inputs by application, user can do host and preipheral role switch by change these inputs. Signed-off-by: Peter Chen Signed-off-by: Li Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/otg.c | 2 + drivers/usb/chipidea/otg_fsm.c | 173 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/chipidea/otg_fsm.h | 6 ++ 3 files changed, 181 insertions(+) (limited to 'drivers/usb/chipidea/otg.c') diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 38e340cba1f9..a048b08b9d4d 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -141,4 +141,6 @@ void ci_hdrc_otg_destroy(struct ci_hdrc *ci) /* Disable all OTG irq and clear status */ hw_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_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 67902a16cb74..8d4c33dea12e 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -46,6 +46,167 @@ static struct ci_otg_fsm_timer *otg_timer_initializer return timer; } +/* Add for otg: interact with user space app */ +static ssize_t +get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + next = buf; + size = PAGE_SIZE; + t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '0') { + ci->fsm.a_bus_req = 0; + } else if (buf[0] == '1') { + /* If a_bus_drop is TRUE, a_bus_req can't be set */ + if (ci->fsm.a_bus_drop) { + mutex_unlock(&ci->fsm.lock); + return count; + } + ci->fsm.a_bus_req = 1; + } + + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req); + +static ssize_t +get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + next = buf; + size = PAGE_SIZE; + t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_drop); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_a_bus_drop(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '0') { + ci->fsm.a_bus_drop = 0; + } else if (buf[0] == '1') { + ci->fsm.a_bus_drop = 1; + ci->fsm.a_bus_req = 0; + } + + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, + set_a_bus_drop); + +static ssize_t +get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +{ + char *next; + unsigned size, t; + struct ci_hdrc *ci = dev_get_drvdata(dev); + + next = buf; + size = PAGE_SIZE; + t = scnprintf(next, size, "%d\n", ci->fsm.b_bus_req); + size -= t; + next += t; + + return PAGE_SIZE - size; +} + +static ssize_t +set_b_bus_req(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '0') + ci->fsm.b_bus_req = 0; + else if (buf[0] == '1') + ci->fsm.b_bus_req = 1; + + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req); + +static ssize_t +set_a_clr_err(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ci_hdrc *ci = dev_get_drvdata(dev); + + if (count > 2) + return -1; + + mutex_lock(&ci->fsm.lock); + if (buf[0] == '1') + ci->fsm.a_clr_err = 1; + + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + mutex_unlock(&ci->fsm.lock); + + return count; +} +static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); + +static struct attribute *inputs_attrs[] = { + &dev_attr_a_bus_req.attr, + &dev_attr_a_bus_drop.attr, + &dev_attr_b_bus_req.attr, + &dev_attr_a_clr_err.attr, + NULL, +}; + +static struct attribute_group inputs_attr_group = { + .name = "inputs", + .attrs = inputs_attrs, +}; + /* * Add timer to active timer list */ @@ -676,6 +837,13 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) return retval; } + retval = sysfs_create_group(&ci->dev->kobj, &inputs_attr_group); + if (retval < 0) { + dev_dbg(ci->dev, + "Can't register sysfs attr group: %d\n", retval); + return retval; + } + /* Enable A vbus valid irq */ hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE); @@ -690,3 +858,8 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) return 0; } + +void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci) +{ + sysfs_remove_group(&ci->dev->kobj, &inputs_attr_group); +} diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 6ec8247d8179..94c085f456a9 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h @@ -95,6 +95,7 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); int ci_otg_fsm_work(struct ci_hdrc *ci); irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci); void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci); +void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci); #else @@ -118,6 +119,11 @@ static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) } +static inline void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ -- cgit v1.2.3