usb: dwc3: ep0: add LPM handling
On device loading the driver enables LPM and the acceptance of U1 and U2 states. The [Set|Clear]Feature requests for "U1/U2" are forwarded directly to the hardware and allow / forbid the initiation of the low power links. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
		
							parent
							
								
									51249dca62
								
							
						
					
					
						commit
						e6a3b5e288
					
				| @ -194,6 +194,7 @@ | |||||||
| #define DWC3_GHWPARAMS1_EN_PWROPT_CLK	1 | #define DWC3_GHWPARAMS1_EN_PWROPT_CLK	1 | ||||||
| 
 | 
 | ||||||
| /* Device Configuration Register */ | /* Device Configuration Register */ | ||||||
|  | #define DWC3_DCFG_LPM_CAP	(1 << 22) | ||||||
| #define DWC3_DCFG_DEVADDR(addr)	((addr) << 3) | #define DWC3_DCFG_DEVADDR(addr)	((addr) << 3) | ||||||
| #define DWC3_DCFG_DEVADDR_MASK	DWC3_DCFG_DEVADDR(0x7f) | #define DWC3_DCFG_DEVADDR_MASK	DWC3_DCFG_DEVADDR(0x7f) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -261,6 +261,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, | |||||||
| { | { | ||||||
| 	struct dwc3_ep		*dep; | 	struct dwc3_ep		*dep; | ||||||
| 	u32			recip; | 	u32			recip; | ||||||
|  | 	u32			reg; | ||||||
| 	u16			usb_status = 0; | 	u16			usb_status = 0; | ||||||
| 	__le16			*response_pkt; | 	__le16			*response_pkt; | ||||||
| 
 | 
 | ||||||
| @ -268,10 +269,18 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, | |||||||
| 	switch (recip) { | 	switch (recip) { | ||||||
| 	case USB_RECIP_DEVICE: | 	case USB_RECIP_DEVICE: | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * We are self-powered. U1/U2/LTM will be set later | 		 * LTM will be set once we know how to set this in HW. | ||||||
| 		 * once we handle this states. RemoteWakeup is 0 on SS |  | ||||||
| 		 */ | 		 */ | ||||||
| 		usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; | 		usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; | ||||||
|  | 
 | ||||||
|  | 		if (dwc->speed == DWC3_DSTS_SUPERSPEED) { | ||||||
|  | 			reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||||||
|  | 			if (reg & DWC3_DCTL_INITU1ENA) | ||||||
|  | 				usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; | ||||||
|  | 			if (reg & DWC3_DCTL_INITU2ENA) | ||||||
|  | 				usb_status |= 1 << USB_DEV_STAT_U2_ENABLED; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case USB_RECIP_INTERFACE: | 	case USB_RECIP_INTERFACE: | ||||||
| @ -312,6 +321,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, | |||||||
| 	u32			recip; | 	u32			recip; | ||||||
| 	u32			wValue; | 	u32			wValue; | ||||||
| 	u32			wIndex; | 	u32			wIndex; | ||||||
|  | 	u32			reg; | ||||||
| 	int			ret; | 	int			ret; | ||||||
| 
 | 
 | ||||||
| 	wValue = le16_to_cpu(ctrl->wValue); | 	wValue = le16_to_cpu(ctrl->wValue); | ||||||
| @ -320,29 +330,43 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, | |||||||
| 	switch (recip) { | 	switch (recip) { | ||||||
| 	case USB_RECIP_DEVICE: | 	case USB_RECIP_DEVICE: | ||||||
| 
 | 
 | ||||||
|  | 		switch (wValue) { | ||||||
|  | 		case USB_DEVICE_REMOTE_WAKEUP: | ||||||
|  | 			break; | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * 9.4.1 says only only for SS, in AddressState only for | 		 * 9.4.1 says only only for SS, in AddressState only for | ||||||
| 		 * default control pipe | 		 * default control pipe | ||||||
| 		 */ | 		 */ | ||||||
| 		switch (wValue) { |  | ||||||
| 		case USB_DEVICE_U1_ENABLE: | 		case USB_DEVICE_U1_ENABLE: | ||||||
| 		case USB_DEVICE_U2_ENABLE: |  | ||||||
| 		case USB_DEVICE_LTM_ENABLE: |  | ||||||
| 			if (dwc->dev_state != DWC3_CONFIGURED_STATE) | 			if (dwc->dev_state != DWC3_CONFIGURED_STATE) | ||||||
| 				return -EINVAL; | 				return -EINVAL; | ||||||
| 			if (dwc->speed != DWC3_DSTS_SUPERSPEED) | 			if (dwc->speed != DWC3_DSTS_SUPERSPEED) | ||||||
| 				return -EINVAL; | 				return -EINVAL; | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		/* XXX add U[12] & LTM */ | 			reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||||||
| 		switch (wValue) { | 			if (set) | ||||||
| 		case USB_DEVICE_REMOTE_WAKEUP: | 				reg |= DWC3_DCTL_INITU1ENA; | ||||||
| 			break; | 			else | ||||||
| 		case USB_DEVICE_U1_ENABLE: | 				reg &= ~DWC3_DCTL_INITU1ENA; | ||||||
|  | 			dwc3_writel(dwc->regs, DWC3_DCTL, reg); | ||||||
| 			break; | 			break; | ||||||
|  | 
 | ||||||
| 		case USB_DEVICE_U2_ENABLE: | 		case USB_DEVICE_U2_ENABLE: | ||||||
|  | 			if (dwc->dev_state != DWC3_CONFIGURED_STATE) | ||||||
|  | 				return -EINVAL; | ||||||
|  | 			if (dwc->speed != DWC3_DSTS_SUPERSPEED) | ||||||
|  | 				return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 			reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||||||
|  | 			if (set) | ||||||
|  | 				reg |= DWC3_DCTL_INITU2ENA; | ||||||
|  | 			else | ||||||
|  | 				reg &= ~DWC3_DCTL_INITU2ENA; | ||||||
|  | 			dwc3_writel(dwc->regs, DWC3_DCTL, reg); | ||||||
| 			break; | 			break; | ||||||
|  | 
 | ||||||
| 		case USB_DEVICE_LTM_ENABLE: | 		case USB_DEVICE_LTM_ENABLE: | ||||||
|  | 			return -EINVAL; | ||||||
| 			break; | 			break; | ||||||
| 
 | 
 | ||||||
| 		case USB_DEVICE_TEST_MODE: | 		case USB_DEVICE_TEST_MODE: | ||||||
|  | |||||||
| @ -1933,6 +1933,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) | |||||||
| 
 | 
 | ||||||
| 	reg = dwc3_readl(dwc->regs, DWC3_DCTL); | 	reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||||||
| 	reg &= ~DWC3_DCTL_TSTCTRL_MASK; | 	reg &= ~DWC3_DCTL_TSTCTRL_MASK; | ||||||
|  | 	reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA); | ||||||
| 	dwc3_writel(dwc->regs, DWC3_DCTL, reg); | 	dwc3_writel(dwc->regs, DWC3_DCTL, reg); | ||||||
| 	dwc->test_mode = false; | 	dwc->test_mode = false; | ||||||
| 
 | 
 | ||||||
| @ -2330,6 +2331,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) | |||||||
| 		goto err5; | 		goto err5; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	reg = dwc3_readl(dwc->regs, DWC3_DCFG); | ||||||
|  | 	reg |= DWC3_DCFG_LPM_CAP; | ||||||
|  | 	dwc3_writel(dwc->regs, DWC3_DCFG, reg); | ||||||
|  | 
 | ||||||
|  | 	reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||||||
|  | 	reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA; | ||||||
|  | 	dwc3_writel(dwc->regs, DWC3_DCTL, reg); | ||||||
|  | 
 | ||||||
| 	/* Enable all but Start and End of Frame IRQs */ | 	/* Enable all but Start and End of Frame IRQs */ | ||||||
| 	reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | | 	reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | | ||||||
| 			DWC3_DEVTEN_EVNTOVERFLOWEN | | 			DWC3_DEVTEN_EVNTOVERFLOWEN | | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user