162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * USB block power/access management abstraction. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Au1000+: The OHCI block control register is at the far end of the OHCI memory 662306a36Sopenharmony_ci * area. Au1550 has OHCI on different base address. No need to handle 762306a36Sopenharmony_ci * UDC here. 862306a36Sopenharmony_ci * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG 962306a36Sopenharmony_ci * as well as the PHY for EHCI and UDC. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/syscore_ops.h> 1962306a36Sopenharmony_ci#include <asm/cpu.h> 2062306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* control register offsets */ 2362306a36Sopenharmony_ci#define AU1000_OHCICFG 0x7fffc 2462306a36Sopenharmony_ci#define AU1550_OHCICFG 0x07ffc 2562306a36Sopenharmony_ci#define AU1200_USBCFG 0x04 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Au1000 USB block config bits */ 2862306a36Sopenharmony_ci#define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */ 2962306a36Sopenharmony_ci#define USBHEN_CE (1 << 3) /* OHCI block clock enable */ 3062306a36Sopenharmony_ci#define USBHEN_E (1 << 2) /* OHCI block enable */ 3162306a36Sopenharmony_ci#define USBHEN_C (1 << 1) /* OHCI block coherency bit */ 3262306a36Sopenharmony_ci#define USBHEN_BE (1 << 0) /* OHCI Big-Endian */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Au1200 USB config bits */ 3562306a36Sopenharmony_ci#define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */ 3662306a36Sopenharmony_ci#define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */ 3762306a36Sopenharmony_ci#define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */ 3862306a36Sopenharmony_ci#define USBCFG_SSD (1 << 23) /* serial short detect en */ 3962306a36Sopenharmony_ci#define USBCFG_PPE (1 << 19) /* HS PHY PLL */ 4062306a36Sopenharmony_ci#define USBCFG_UCE (1 << 18) /* UDC clock enable */ 4162306a36Sopenharmony_ci#define USBCFG_ECE (1 << 17) /* EHCI clock enable */ 4262306a36Sopenharmony_ci#define USBCFG_OCE (1 << 16) /* OHCI clock enable */ 4362306a36Sopenharmony_ci#define USBCFG_FLA(x) (((x) & 0x3f) << 8) 4462306a36Sopenharmony_ci#define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */ 4562306a36Sopenharmony_ci#define USBCFG_GME (1 << 6) /* OTG mem access */ 4662306a36Sopenharmony_ci#define USBCFG_DBE (1 << 5) /* UDC busmaster enable */ 4762306a36Sopenharmony_ci#define USBCFG_DME (1 << 4) /* UDC mem enable */ 4862306a36Sopenharmony_ci#define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */ 4962306a36Sopenharmony_ci#define USBCFG_EME (1 << 2) /* EHCI mem enable */ 5062306a36Sopenharmony_ci#define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */ 5162306a36Sopenharmony_ci#define USBCFG_OME (1 << 0) /* OHCI mem enable */ 5262306a36Sopenharmony_ci#define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\ 5362306a36Sopenharmony_ci USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \ 5462306a36Sopenharmony_ci USBCFG_GME | USBCFG_DBE | USBCFG_DME | \ 5562306a36Sopenharmony_ci USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \ 5662306a36Sopenharmony_ci USBCFG_OME) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Au1300 USB config registers */ 5962306a36Sopenharmony_ci#define USB_DWC_CTRL1 0x00 6062306a36Sopenharmony_ci#define USB_DWC_CTRL2 0x04 6162306a36Sopenharmony_ci#define USB_VBUS_TIMER 0x10 6262306a36Sopenharmony_ci#define USB_SBUS_CTRL 0x14 6362306a36Sopenharmony_ci#define USB_MSR_ERR 0x18 6462306a36Sopenharmony_ci#define USB_DWC_CTRL3 0x1C 6562306a36Sopenharmony_ci#define USB_DWC_CTRL4 0x20 6662306a36Sopenharmony_ci#define USB_OTG_STATUS 0x28 6762306a36Sopenharmony_ci#define USB_DWC_CTRL5 0x2C 6862306a36Sopenharmony_ci#define USB_DWC_CTRL6 0x30 6962306a36Sopenharmony_ci#define USB_DWC_CTRL7 0x34 7062306a36Sopenharmony_ci#define USB_PHY_STATUS 0xC0 7162306a36Sopenharmony_ci#define USB_INT_STATUS 0xC4 7262306a36Sopenharmony_ci#define USB_INT_ENABLE 0xC8 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ 7562306a36Sopenharmony_ci#define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ 7662306a36Sopenharmony_ci#define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ 7962306a36Sopenharmony_ci#define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ 8062306a36Sopenharmony_ci#define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19) 8362306a36Sopenharmony_ci#define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18) 8462306a36Sopenharmony_ci#define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17) 8562306a36Sopenharmony_ci#define USB_DWC_CTRL3_OTG0_CKEN (1 << 16) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define USB_INTEN_FORCE 0x20 9062306a36Sopenharmony_ci#define USB_INTEN_PHY 0x10 9162306a36Sopenharmony_ci#define USB_INTEN_UDC 0x08 9262306a36Sopenharmony_ci#define USB_INTEN_EHCI 0x04 9362306a36Sopenharmony_ci#define USB_INTEN_OHCI1 0x02 9462306a36Sopenharmony_ci#define USB_INTEN_OHCI0 0x01 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic DEFINE_SPINLOCK(alchemy_usb_lock); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic inline void __au1300_usb_phyctl(void __iomem *base, int enable) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned long r, s; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL2); 10362306a36Sopenharmony_ci s = __raw_readl(base + USB_DWC_CTRL3); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN | 10662306a36Sopenharmony_ci USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (enable) { 10962306a36Sopenharmony_ci /* simply enable all PHYs */ 11062306a36Sopenharmony_ci r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | 11162306a36Sopenharmony_ci USB_DWC_CTRL2_PHYRS; 11262306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL2); 11362306a36Sopenharmony_ci wmb(); 11462306a36Sopenharmony_ci } else if (!s) { 11562306a36Sopenharmony_ci /* no USB block active, do disable all PHYs */ 11662306a36Sopenharmony_ci r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | 11762306a36Sopenharmony_ci USB_DWC_CTRL2_PHYRS); 11862306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL2); 11962306a36Sopenharmony_ci wmb(); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline void __au1300_ohci_control(void __iomem *base, int enable, int id) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci unsigned long r; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (enable) { 12862306a36Sopenharmony_ci __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */ 12962306a36Sopenharmony_ci wmb(); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */ 13262306a36Sopenharmony_ci r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN 13362306a36Sopenharmony_ci : USB_DWC_CTRL3_OHCI1_CKEN; 13462306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 13562306a36Sopenharmony_ci wmb(); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); /* power up the PHYs */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 14062306a36Sopenharmony_ci r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1; 14162306a36Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 14262306a36Sopenharmony_ci wmb(); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* reset the OHCI start clock bit */ 14562306a36Sopenharmony_ci __raw_writel(0, base + USB_DWC_CTRL7); 14662306a36Sopenharmony_ci wmb(); 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 14962306a36Sopenharmony_ci r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1); 15062306a36Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 15162306a36Sopenharmony_ci wmb(); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 15462306a36Sopenharmony_ci r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN 15562306a36Sopenharmony_ci : USB_DWC_CTRL3_OHCI1_CKEN); 15662306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 15762306a36Sopenharmony_ci wmb(); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic inline void __au1300_ehci_control(void __iomem *base, int enable) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci unsigned long r; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (enable) { 16862306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 16962306a36Sopenharmony_ci r |= USB_DWC_CTRL3_EHCI0_CKEN; 17062306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 17162306a36Sopenharmony_ci wmb(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 17462306a36Sopenharmony_ci r |= USB_DWC_CTRL1_HSTRS; 17562306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 17662306a36Sopenharmony_ci wmb(); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 18162306a36Sopenharmony_ci r |= USB_INTEN_EHCI; 18262306a36Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 18362306a36Sopenharmony_ci wmb(); 18462306a36Sopenharmony_ci } else { 18562306a36Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 18662306a36Sopenharmony_ci r &= ~USB_INTEN_EHCI; 18762306a36Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 18862306a36Sopenharmony_ci wmb(); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 19162306a36Sopenharmony_ci r &= ~USB_DWC_CTRL1_HSTRS; 19262306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 19362306a36Sopenharmony_ci wmb(); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 19662306a36Sopenharmony_ci r &= ~USB_DWC_CTRL3_EHCI0_CKEN; 19762306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 19862306a36Sopenharmony_ci wmb(); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline void __au1300_udc_control(void __iomem *base, int enable) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci unsigned long r; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (enable) { 20962306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 21062306a36Sopenharmony_ci r |= USB_DWC_CTRL1_DCRS; 21162306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 21262306a36Sopenharmony_ci wmb(); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 21762306a36Sopenharmony_ci r |= USB_INTEN_UDC; 21862306a36Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 21962306a36Sopenharmony_ci wmb(); 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 22262306a36Sopenharmony_ci r &= ~USB_INTEN_UDC; 22362306a36Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 22462306a36Sopenharmony_ci wmb(); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 22762306a36Sopenharmony_ci r &= ~USB_DWC_CTRL1_DCRS; 22862306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 22962306a36Sopenharmony_ci wmb(); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic inline void __au1300_otg_control(void __iomem *base, int enable) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci unsigned long r; 23862306a36Sopenharmony_ci if (enable) { 23962306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 24062306a36Sopenharmony_ci r |= USB_DWC_CTRL3_OTG0_CKEN; 24162306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 24262306a36Sopenharmony_ci wmb(); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 24562306a36Sopenharmony_ci r &= ~USB_DWC_CTRL1_OTGD; 24662306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 24762306a36Sopenharmony_ci wmb(); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 25262306a36Sopenharmony_ci r |= USB_DWC_CTRL1_OTGD; 25362306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 25462306a36Sopenharmony_ci wmb(); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 25762306a36Sopenharmony_ci r &= ~USB_DWC_CTRL3_OTG0_CKEN; 25862306a36Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 25962306a36Sopenharmony_ci wmb(); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci __au1300_usb_phyctl(base, enable); 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic inline int au1300_usb_control(int block, int enable) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci void __iomem *base = 26862306a36Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); 26962306a36Sopenharmony_ci int ret = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci switch (block) { 27262306a36Sopenharmony_ci case ALCHEMY_USB_OHCI0: 27362306a36Sopenharmony_ci __au1300_ohci_control(base, enable, 0); 27462306a36Sopenharmony_ci break; 27562306a36Sopenharmony_ci case ALCHEMY_USB_OHCI1: 27662306a36Sopenharmony_ci __au1300_ohci_control(base, enable, 1); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci case ALCHEMY_USB_EHCI0: 27962306a36Sopenharmony_ci __au1300_ehci_control(base, enable); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case ALCHEMY_USB_UDC0: 28262306a36Sopenharmony_ci __au1300_udc_control(base, enable); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case ALCHEMY_USB_OTG0: 28562306a36Sopenharmony_ci __au1300_otg_control(base, enable); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci ret = -ENODEV; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic inline void au1300_usb_init(void) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci void __iomem *base = 29662306a36Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 29962306a36Sopenharmony_ci * here at all: Port 2 routing (EHCI or UDC) must be set either 30062306a36Sopenharmony_ci * by boot firmware or platform init code; I can't autodetect 30162306a36Sopenharmony_ci * a sane setting. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */ 30462306a36Sopenharmony_ci wmb(); 30562306a36Sopenharmony_ci __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */ 30662306a36Sopenharmony_ci wmb(); 30762306a36Sopenharmony_ci __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */ 30862306a36Sopenharmony_ci wmb(); 30962306a36Sopenharmony_ci __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */ 31062306a36Sopenharmony_ci wmb(); 31162306a36Sopenharmony_ci /* set coherent access bit */ 31262306a36Sopenharmony_ci __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL); 31362306a36Sopenharmony_ci wmb(); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic inline void __au1200_ohci_control(void __iomem *base, int enable) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci unsigned long r = __raw_readl(base + AU1200_USBCFG); 31962306a36Sopenharmony_ci if (enable) { 32062306a36Sopenharmony_ci __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG); 32162306a36Sopenharmony_ci wmb(); 32262306a36Sopenharmony_ci udelay(2000); 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG); 32562306a36Sopenharmony_ci wmb(); 32662306a36Sopenharmony_ci udelay(1000); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic inline void __au1200_ehci_control(void __iomem *base, int enable) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci unsigned long r = __raw_readl(base + AU1200_USBCFG); 33362306a36Sopenharmony_ci if (enable) { 33462306a36Sopenharmony_ci __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG); 33562306a36Sopenharmony_ci wmb(); 33662306a36Sopenharmony_ci udelay(1000); 33762306a36Sopenharmony_ci } else { 33862306a36Sopenharmony_ci if (!(r & USBCFG_UCE)) /* UDC also off? */ 33962306a36Sopenharmony_ci r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ 34062306a36Sopenharmony_ci __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG); 34162306a36Sopenharmony_ci wmb(); 34262306a36Sopenharmony_ci udelay(1000); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic inline void __au1200_udc_control(void __iomem *base, int enable) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci unsigned long r = __raw_readl(base + AU1200_USBCFG); 34962306a36Sopenharmony_ci if (enable) { 35062306a36Sopenharmony_ci __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG); 35162306a36Sopenharmony_ci wmb(); 35262306a36Sopenharmony_ci } else { 35362306a36Sopenharmony_ci if (!(r & USBCFG_ECE)) /* EHCI also off? */ 35462306a36Sopenharmony_ci r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ 35562306a36Sopenharmony_ci __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG); 35662306a36Sopenharmony_ci wmb(); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic inline int au1200_usb_control(int block, int enable) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci void __iomem *base = 36362306a36Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci switch (block) { 36662306a36Sopenharmony_ci case ALCHEMY_USB_OHCI0: 36762306a36Sopenharmony_ci __au1200_ohci_control(base, enable); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case ALCHEMY_USB_UDC0: 37062306a36Sopenharmony_ci __au1200_udc_control(base, enable); 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci case ALCHEMY_USB_EHCI0: 37362306a36Sopenharmony_ci __au1200_ehci_control(base, enable); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci default: 37662306a36Sopenharmony_ci return -ENODEV; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* initialize USB block(s) to a known working state */ 38362306a36Sopenharmony_cistatic inline void au1200_usb_init(void) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci void __iomem *base = 38662306a36Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); 38762306a36Sopenharmony_ci __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG); 38862306a36Sopenharmony_ci wmb(); 38962306a36Sopenharmony_ci udelay(1000); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic inline int au1000_usb_init(unsigned long rb, int reg) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg); 39562306a36Sopenharmony_ci unsigned long r = __raw_readl(base); 39662306a36Sopenharmony_ci struct clk *c; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 48MHz check. Don't init if no one can provide it */ 39962306a36Sopenharmony_ci c = clk_get(NULL, "usbh_clk"); 40062306a36Sopenharmony_ci if (IS_ERR(c)) 40162306a36Sopenharmony_ci return -ENODEV; 40262306a36Sopenharmony_ci if (clk_round_rate(c, 48000000) != 48000000) { 40362306a36Sopenharmony_ci clk_put(c); 40462306a36Sopenharmony_ci return -ENODEV; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci if (clk_set_rate(c, 48000000)) { 40762306a36Sopenharmony_ci clk_put(c); 40862306a36Sopenharmony_ci return -ENODEV; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci clk_put(c); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci#if defined(__BIG_ENDIAN) 41362306a36Sopenharmony_ci r |= USBHEN_BE; 41462306a36Sopenharmony_ci#endif 41562306a36Sopenharmony_ci r |= USBHEN_C; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci __raw_writel(r, base); 41862306a36Sopenharmony_ci wmb(); 41962306a36Sopenharmony_ci udelay(1000); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci void __iomem *base = (void __iomem *)KSEG1ADDR(rb); 42862306a36Sopenharmony_ci unsigned long r = __raw_readl(base + creg); 42962306a36Sopenharmony_ci struct clk *c = clk_get(NULL, "usbh_clk"); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (IS_ERR(c)) 43262306a36Sopenharmony_ci return; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (enable) { 43562306a36Sopenharmony_ci if (clk_prepare_enable(c)) 43662306a36Sopenharmony_ci goto out; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci __raw_writel(r | USBHEN_CE, base + creg); 43962306a36Sopenharmony_ci wmb(); 44062306a36Sopenharmony_ci udelay(1000); 44162306a36Sopenharmony_ci __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg); 44262306a36Sopenharmony_ci wmb(); 44362306a36Sopenharmony_ci udelay(1000); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* wait for reset complete (read reg twice: au1500 erratum) */ 44662306a36Sopenharmony_ci while (__raw_readl(base + creg), 44762306a36Sopenharmony_ci !(__raw_readl(base + creg) & USBHEN_RD)) 44862306a36Sopenharmony_ci udelay(1000); 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg); 45162306a36Sopenharmony_ci wmb(); 45262306a36Sopenharmony_ci clk_disable_unprepare(c); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ciout: 45562306a36Sopenharmony_ci clk_put(c); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic inline int au1000_usb_control(int block, int enable, unsigned long rb, 45962306a36Sopenharmony_ci int creg) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci int ret = 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci switch (block) { 46462306a36Sopenharmony_ci case ALCHEMY_USB_OHCI0: 46562306a36Sopenharmony_ci __au1xx0_ohci_control(enable, rb, creg); 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci ret = -ENODEV; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci return ret; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/* 47462306a36Sopenharmony_ci * alchemy_usb_control - control Alchemy on-chip USB blocks 47562306a36Sopenharmony_ci * @block: USB block to target 47662306a36Sopenharmony_ci * @enable: set 1 to enable a block, 0 to disable 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ciint alchemy_usb_control(int block, int enable) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci unsigned long flags; 48162306a36Sopenharmony_ci int ret; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci spin_lock_irqsave(&alchemy_usb_lock, flags); 48462306a36Sopenharmony_ci switch (alchemy_get_cputype()) { 48562306a36Sopenharmony_ci case ALCHEMY_CPU_AU1000: 48662306a36Sopenharmony_ci case ALCHEMY_CPU_AU1500: 48762306a36Sopenharmony_ci case ALCHEMY_CPU_AU1100: 48862306a36Sopenharmony_ci ret = au1000_usb_control(block, enable, 48962306a36Sopenharmony_ci AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case ALCHEMY_CPU_AU1550: 49262306a36Sopenharmony_ci ret = au1000_usb_control(block, enable, 49362306a36Sopenharmony_ci AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci case ALCHEMY_CPU_AU1200: 49662306a36Sopenharmony_ci ret = au1200_usb_control(block, enable); 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci case ALCHEMY_CPU_AU1300: 49962306a36Sopenharmony_ci ret = au1300_usb_control(block, enable); 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci default: 50262306a36Sopenharmony_ci ret = -ENODEV; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci spin_unlock_irqrestore(&alchemy_usb_lock, flags); 50562306a36Sopenharmony_ci return ret; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(alchemy_usb_control); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic unsigned long alchemy_usb_pmdata[2]; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void au1000_usb_pm(unsigned long br, int creg, int susp) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci void __iomem *base = (void __iomem *)KSEG1ADDR(br); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (susp) { 51762306a36Sopenharmony_ci alchemy_usb_pmdata[0] = __raw_readl(base + creg); 51862306a36Sopenharmony_ci /* There appears to be some undocumented reset register.... */ 51962306a36Sopenharmony_ci __raw_writel(0, base + 0x04); 52062306a36Sopenharmony_ci wmb(); 52162306a36Sopenharmony_ci __raw_writel(0, base + creg); 52262306a36Sopenharmony_ci wmb(); 52362306a36Sopenharmony_ci } else { 52462306a36Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[0], base + creg); 52562306a36Sopenharmony_ci wmb(); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic void au1200_usb_pm(int susp) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci void __iomem *base = 53262306a36Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR); 53362306a36Sopenharmony_ci if (susp) { 53462306a36Sopenharmony_ci /* save OTG_CAP/MUX registers which indicate port routing */ 53562306a36Sopenharmony_ci /* FIXME: write an OTG driver to do that */ 53662306a36Sopenharmony_ci alchemy_usb_pmdata[0] = __raw_readl(base + 0x00); 53762306a36Sopenharmony_ci alchemy_usb_pmdata[1] = __raw_readl(base + 0x04); 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci /* restore access to all MMIO areas */ 54062306a36Sopenharmony_ci au1200_usb_init(); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* restore OTG_CAP/MUX registers */ 54362306a36Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[0], base + 0x00); 54462306a36Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[1], base + 0x04); 54562306a36Sopenharmony_ci wmb(); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic void au1300_usb_pm(int susp) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci void __iomem *base = 55262306a36Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); 55362306a36Sopenharmony_ci /* remember Port2 routing */ 55462306a36Sopenharmony_ci if (susp) { 55562306a36Sopenharmony_ci alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4); 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci au1300_usb_init(); 55862306a36Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4); 55962306a36Sopenharmony_ci wmb(); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic void alchemy_usb_pm(int susp) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci switch (alchemy_get_cputype()) { 56662306a36Sopenharmony_ci case ALCHEMY_CPU_AU1000: 56762306a36Sopenharmony_ci case ALCHEMY_CPU_AU1500: 56862306a36Sopenharmony_ci case ALCHEMY_CPU_AU1100: 56962306a36Sopenharmony_ci au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp); 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci case ALCHEMY_CPU_AU1550: 57262306a36Sopenharmony_ci au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp); 57362306a36Sopenharmony_ci break; 57462306a36Sopenharmony_ci case ALCHEMY_CPU_AU1200: 57562306a36Sopenharmony_ci au1200_usb_pm(susp); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case ALCHEMY_CPU_AU1300: 57862306a36Sopenharmony_ci au1300_usb_pm(susp); 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int alchemy_usb_suspend(void) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci alchemy_usb_pm(1); 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic void alchemy_usb_resume(void) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci alchemy_usb_pm(0); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic struct syscore_ops alchemy_usb_pm_ops = { 59562306a36Sopenharmony_ci .suspend = alchemy_usb_suspend, 59662306a36Sopenharmony_ci .resume = alchemy_usb_resume, 59762306a36Sopenharmony_ci}; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int __init alchemy_usb_init(void) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci int ret = 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci switch (alchemy_get_cputype()) { 60462306a36Sopenharmony_ci case ALCHEMY_CPU_AU1000: 60562306a36Sopenharmony_ci case ALCHEMY_CPU_AU1500: 60662306a36Sopenharmony_ci case ALCHEMY_CPU_AU1100: 60762306a36Sopenharmony_ci ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR, 60862306a36Sopenharmony_ci AU1000_OHCICFG); 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci case ALCHEMY_CPU_AU1550: 61162306a36Sopenharmony_ci ret = au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR, 61262306a36Sopenharmony_ci AU1550_OHCICFG); 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci case ALCHEMY_CPU_AU1200: 61562306a36Sopenharmony_ci au1200_usb_init(); 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci case ALCHEMY_CPU_AU1300: 61862306a36Sopenharmony_ci au1300_usb_init(); 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!ret) 62362306a36Sopenharmony_ci register_syscore_ops(&alchemy_usb_pm_ops); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ciarch_initcall(alchemy_usb_init); 628