diff options
author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2012-05-11 17:25:47 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-11 16:52:10 -0700 |
commit | 5f36e231e9dbffb5264612e5b5817ab574a5e5db (patch) | |
tree | a71027cded532334d3d51cbf737925240d34e7df /drivers/usb/chipidea/ci.h | |
parent | e443b333629f82ca0da91a05ca638050943bbedd (diff) | |
download | linux-5f36e231e9dbffb5264612e5b5817ab574a5e5db.tar.gz linux-5f36e231e9dbffb5264612e5b5817ab574a5e5db.tar.bz2 linux-5f36e231e9dbffb5264612e5b5817ab574a5e5db.zip |
usb: chipidea: add support for roles
Add some generic code for roles and implement simple role switching
based on ID pin state and/or a sysfs file. At this, we also rename
the device to ci_hdrc, which is what it is.
The "manual" switch is made into a sysfs file and not debugfs, because
it might be useful even in non-debug context. For some boards, like
sheevaplug, it seems to be the only way to switch roles without modifying
the hardware, since the ID pin is always grounded.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/chipidea/ci.h')
-rw-r--r-- | drivers/usb/chipidea/ci.h | 64 |
1 files changed, 61 insertions, 3 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index f5b3b8538a3b..56cb73b1e903 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -14,6 +14,7 @@ #define __DRIVERS_USB_CHIPIDEA_CI_H #include <linux/list.h> +#include <linux/irqreturn.h> #include <linux/usb/gadget.h> /****************************************************************************** @@ -47,6 +48,26 @@ struct ci13xxx_ep { struct dma_pool *td_pool; }; +enum ci_role { + CI_ROLE_HOST = 0, + CI_ROLE_GADGET, + CI_ROLE_END, +}; + +/** + * struct ci_role_driver - host/gadget role driver + * start: start this role + * stop: stop this role + * irq: irq handler for this role + * name: role name string (host/gadget) + */ +struct ci_role_driver { + int (*start)(struct ci13xxx *); + void (*stop)(struct ci13xxx *); + irqreturn_t (*irq)(struct ci13xxx *); + const char *name; +}; + struct hw_bank { unsigned lpm; /* is LPM? */ void __iomem *abs; /* bus map offset */ @@ -85,8 +106,47 @@ struct ci13xxx { struct ci13xxx_udc_driver *udc_driver; /* device controller driver */ int vbus_active; /* is VBUS active */ struct usb_phy *transceiver; /* Transceiver struct */ + struct ci_role_driver *roles[CI_ROLE_END]; + enum ci_role role; + bool is_otg; + struct work_struct work; + struct workqueue_struct *wq; }; +static inline struct ci_role_driver *ci_role(struct ci13xxx *ci) +{ + BUG_ON(ci->role >= CI_ROLE_END || !ci->roles[ci->role]); + return ci->roles[ci->role]; +} + +static inline int ci_role_start(struct ci13xxx *ci, enum ci_role role) +{ + int ret; + + if (role >= CI_ROLE_END) + return -EINVAL; + + if (!ci->roles[role]) + return -ENXIO; + + ret = ci->roles[role]->start(ci); + if (!ret) + ci->role = role; + return ret; +} + +static inline void ci_role_stop(struct ci13xxx *ci) +{ + enum ci_role role = ci->role; + + if (role == CI_ROLE_END) + return; + + ci->role = CI_ROLE_END; + + ci->roles[role]->stop(ci); +} + /****************************************************************************** * REGISTERS *****************************************************************************/ @@ -107,6 +167,7 @@ enum ci13xxx_regs { OP_ENDPTLISTADDR, OP_PORTSC, OP_DEVLC, + OP_OTGSC, OP_USBMODE, OP_ENDPTSETUPSTAT, OP_ENDPTPRIME, @@ -118,7 +179,6 @@ enum ci13xxx_regs { OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2, }; - /** * ffs_nr: find first (least significant) bit set * @x: the word to search @@ -193,8 +253,6 @@ static inline u32 hw_test_and_write(struct ci13xxx *udc, enum ci13xxx_regs reg, return (val & mask) >> ffs_nr(mask); } -int hw_device_init(struct ci13xxx *udc, void __iomem *base, - uintptr_t cap_offset); int hw_device_reset(struct ci13xxx *ci); int hw_port_test_set(struct ci13xxx *ci, u8 mode); |