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>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
e443b33362
commit
5f36e231e9
@@ -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);
|
||||
|
Reference in New Issue
Block a user