mirror of https://gitee.com/openkylin/linux.git
usb: ehci: add ehci_port_power interface
The current EHCI implementation is prepared to toggle the PORT_POWER bit to enable or disable a USB-Port. In some cases this port power can not be just toggled by the PORT_POWER bit, and the gpio-regulator is needed to be toggled too. This patch defines a port power control interface ehci_port_power for ehci core use, it toggles PORT_POWER bit as well as calls platform defined .port_power if it is defined. Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Signed-off-by: Peter Chen <peter.chen@freescale.com> Acked-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
e28e2f2f7c
commit
11a7e59405
|
@ -311,6 +311,7 @@ static void unlink_empty_async_suspended(struct ehci_hcd *ehci);
|
||||||
static void ehci_work(struct ehci_hcd *ehci);
|
static void ehci_work(struct ehci_hcd *ehci);
|
||||||
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
|
||||||
|
static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable);
|
||||||
|
|
||||||
#include "ehci-timer.c"
|
#include "ehci-timer.c"
|
||||||
#include "ehci-hub.c"
|
#include "ehci-hub.c"
|
||||||
|
@ -329,9 +330,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
|
||||||
{
|
{
|
||||||
int port = HCS_N_PORTS(ehci->hcs_params);
|
int port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
|
|
||||||
while (port--)
|
while (port--) {
|
||||||
ehci_writel(ehci, PORT_RWC_BITS,
|
ehci_writel(ehci, PORT_RWC_BITS,
|
||||||
&ehci->regs->port_status[port]);
|
&ehci->regs->port_status[port]);
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
ehci_port_power(ehci, port, false);
|
||||||
|
spin_lock_irq(&ehci->lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1233,6 +1238,8 @@ void ehci_init_driver(struct hc_driver *drv,
|
||||||
drv->hcd_priv_size += over->extra_priv_size;
|
drv->hcd_priv_size += over->extra_priv_size;
|
||||||
if (over->reset)
|
if (over->reset)
|
||||||
drv->reset = over->reset;
|
drv->reset = over->reset;
|
||||||
|
if (over->port_power)
|
||||||
|
drv->port_power = over->port_power;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ehci_init_driver);
|
EXPORT_SYMBOL_GPL(ehci_init_driver);
|
||||||
|
|
|
@ -69,10 +69,8 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
|
||||||
if (test_bit(port, &ehci->owned_ports)) {
|
if (test_bit(port, &ehci->owned_ports)) {
|
||||||
reg = &ehci->regs->port_status[port];
|
reg = &ehci->regs->port_status[port];
|
||||||
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
|
||||||
if (!(status & PORT_POWER)) {
|
if (!(status & PORT_POWER))
|
||||||
status |= PORT_POWER;
|
ehci_port_power(ehci, port, true);
|
||||||
ehci_writel(ehci, status, reg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,9 +950,11 @@ int ehci_hub_control(
|
||||||
clear_bit(wIndex, &ehci->port_c_suspend);
|
clear_bit(wIndex, &ehci->port_c_suspend);
|
||||||
break;
|
break;
|
||||||
case USB_PORT_FEAT_POWER:
|
case USB_PORT_FEAT_POWER:
|
||||||
if (HCS_PPC (ehci->hcs_params))
|
if (HCS_PPC(ehci->hcs_params)) {
|
||||||
ehci_writel(ehci, temp & ~PORT_POWER,
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
status_reg);
|
ehci_port_power(ehci, wIndex, false);
|
||||||
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case USB_PORT_FEAT_C_CONNECTION:
|
case USB_PORT_FEAT_C_CONNECTION:
|
||||||
ehci_writel(ehci, temp | PORT_CSC, status_reg);
|
ehci_writel(ehci, temp | PORT_CSC, status_reg);
|
||||||
|
@ -1004,9 +1004,9 @@ int ehci_hub_control(
|
||||||
*/
|
*/
|
||||||
if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle))
|
if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle))
|
||||||
&& HCS_PPC(ehci->hcs_params)) {
|
&& HCS_PPC(ehci->hcs_params)) {
|
||||||
ehci_writel(ehci,
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
temp & ~(PORT_RWC_BITS | PORT_POWER),
|
ehci_port_power(ehci, wIndex, false);
|
||||||
status_reg);
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
temp = ehci_readl(ehci, status_reg);
|
temp = ehci_readl(ehci, status_reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1187,9 +1187,11 @@ int ehci_hub_control(
|
||||||
set_bit(wIndex, &ehci->suspended_ports);
|
set_bit(wIndex, &ehci->suspended_ports);
|
||||||
break;
|
break;
|
||||||
case USB_PORT_FEAT_POWER:
|
case USB_PORT_FEAT_POWER:
|
||||||
if (HCS_PPC (ehci->hcs_params))
|
if (HCS_PPC(ehci->hcs_params)) {
|
||||||
ehci_writel(ehci, temp | PORT_POWER,
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||||
status_reg);
|
ehci_port_power(ehci, wIndex, true);
|
||||||
|
spin_lock_irqsave(&ehci->lock, flags);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case USB_PORT_FEAT_RESET:
|
case USB_PORT_FEAT_RESET:
|
||||||
if (temp & (PORT_SUSPEND|PORT_RESUME))
|
if (temp & (PORT_SUSPEND|PORT_RESUME))
|
||||||
|
@ -1297,3 +1299,20 @@ static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)
|
||||||
reg = &ehci->regs->port_status[portnum - 1];
|
reg = &ehci->regs->port_status[portnum - 1];
|
||||||
return ehci_readl(ehci, reg) & PORT_OWNER;
|
return ehci_readl(ehci, reg) & PORT_OWNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ehci_port_power(struct ehci_hcd *ehci, int portnum, bool enable)
|
||||||
|
{
|
||||||
|
struct usb_hcd *hcd = ehci_to_hcd(ehci);
|
||||||
|
u32 __iomem *status_reg = &ehci->regs->port_status[portnum];
|
||||||
|
u32 temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
ehci_writel(ehci, temp | PORT_POWER, status_reg);
|
||||||
|
else
|
||||||
|
ehci_writel(ehci, temp & ~PORT_POWER, status_reg);
|
||||||
|
|
||||||
|
if (hcd->driver->port_power)
|
||||||
|
hcd->driver->port_power(hcd, portnum, enable);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -859,6 +859,8 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
|
||||||
struct ehci_driver_overrides {
|
struct ehci_driver_overrides {
|
||||||
size_t extra_priv_size;
|
size_t extra_priv_size;
|
||||||
int (*reset)(struct usb_hcd *hcd);
|
int (*reset)(struct usb_hcd *hcd);
|
||||||
|
int (*port_power)(struct usb_hcd *hcd,
|
||||||
|
int portnum, bool enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void ehci_init_driver(struct hc_driver *drv,
|
extern void ehci_init_driver(struct hc_driver *drv,
|
||||||
|
|
|
@ -379,6 +379,9 @@ struct hc_driver {
|
||||||
int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
|
int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
|
||||||
struct usb_device *, enum usb3_link_state state);
|
struct usb_device *, enum usb3_link_state state);
|
||||||
int (*find_raw_port_number)(struct usb_hcd *, int);
|
int (*find_raw_port_number)(struct usb_hcd *, int);
|
||||||
|
/* Call for power on/off the port if necessary */
|
||||||
|
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
|
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
|
||||||
|
|
Loading…
Reference in New Issue