18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB block power/access management abstraction. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Au1000+: The OHCI block control register is at the far end of the OHCI memory 68c2ecf20Sopenharmony_ci * area. Au1550 has OHCI on different base address. No need to handle 78c2ecf20Sopenharmony_ci * UDC here. 88c2ecf20Sopenharmony_ci * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG 98c2ecf20Sopenharmony_ci * as well as the PHY for EHCI and UDC. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/export.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 198c2ecf20Sopenharmony_ci#include <asm/cpu.h> 208c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* control register offsets */ 238c2ecf20Sopenharmony_ci#define AU1000_OHCICFG 0x7fffc 248c2ecf20Sopenharmony_ci#define AU1550_OHCICFG 0x07ffc 258c2ecf20Sopenharmony_ci#define AU1200_USBCFG 0x04 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Au1000 USB block config bits */ 288c2ecf20Sopenharmony_ci#define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */ 298c2ecf20Sopenharmony_ci#define USBHEN_CE (1 << 3) /* OHCI block clock enable */ 308c2ecf20Sopenharmony_ci#define USBHEN_E (1 << 2) /* OHCI block enable */ 318c2ecf20Sopenharmony_ci#define USBHEN_C (1 << 1) /* OHCI block coherency bit */ 328c2ecf20Sopenharmony_ci#define USBHEN_BE (1 << 0) /* OHCI Big-Endian */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Au1200 USB config bits */ 358c2ecf20Sopenharmony_ci#define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */ 368c2ecf20Sopenharmony_ci#define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */ 378c2ecf20Sopenharmony_ci#define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */ 388c2ecf20Sopenharmony_ci#define USBCFG_SSD (1 << 23) /* serial short detect en */ 398c2ecf20Sopenharmony_ci#define USBCFG_PPE (1 << 19) /* HS PHY PLL */ 408c2ecf20Sopenharmony_ci#define USBCFG_UCE (1 << 18) /* UDC clock enable */ 418c2ecf20Sopenharmony_ci#define USBCFG_ECE (1 << 17) /* EHCI clock enable */ 428c2ecf20Sopenharmony_ci#define USBCFG_OCE (1 << 16) /* OHCI clock enable */ 438c2ecf20Sopenharmony_ci#define USBCFG_FLA(x) (((x) & 0x3f) << 8) 448c2ecf20Sopenharmony_ci#define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */ 458c2ecf20Sopenharmony_ci#define USBCFG_GME (1 << 6) /* OTG mem access */ 468c2ecf20Sopenharmony_ci#define USBCFG_DBE (1 << 5) /* UDC busmaster enable */ 478c2ecf20Sopenharmony_ci#define USBCFG_DME (1 << 4) /* UDC mem enable */ 488c2ecf20Sopenharmony_ci#define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */ 498c2ecf20Sopenharmony_ci#define USBCFG_EME (1 << 2) /* EHCI mem enable */ 508c2ecf20Sopenharmony_ci#define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */ 518c2ecf20Sopenharmony_ci#define USBCFG_OME (1 << 0) /* OHCI mem enable */ 528c2ecf20Sopenharmony_ci#define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\ 538c2ecf20Sopenharmony_ci USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \ 548c2ecf20Sopenharmony_ci USBCFG_GME | USBCFG_DBE | USBCFG_DME | \ 558c2ecf20Sopenharmony_ci USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \ 568c2ecf20Sopenharmony_ci USBCFG_OME) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Au1300 USB config registers */ 598c2ecf20Sopenharmony_ci#define USB_DWC_CTRL1 0x00 608c2ecf20Sopenharmony_ci#define USB_DWC_CTRL2 0x04 618c2ecf20Sopenharmony_ci#define USB_VBUS_TIMER 0x10 628c2ecf20Sopenharmony_ci#define USB_SBUS_CTRL 0x14 638c2ecf20Sopenharmony_ci#define USB_MSR_ERR 0x18 648c2ecf20Sopenharmony_ci#define USB_DWC_CTRL3 0x1C 658c2ecf20Sopenharmony_ci#define USB_DWC_CTRL4 0x20 668c2ecf20Sopenharmony_ci#define USB_OTG_STATUS 0x28 678c2ecf20Sopenharmony_ci#define USB_DWC_CTRL5 0x2C 688c2ecf20Sopenharmony_ci#define USB_DWC_CTRL6 0x30 698c2ecf20Sopenharmony_ci#define USB_DWC_CTRL7 0x34 708c2ecf20Sopenharmony_ci#define USB_PHY_STATUS 0xC0 718c2ecf20Sopenharmony_ci#define USB_INT_STATUS 0xC4 728c2ecf20Sopenharmony_ci#define USB_INT_ENABLE 0xC8 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ 758c2ecf20Sopenharmony_ci#define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ 768c2ecf20Sopenharmony_ci#define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ 798c2ecf20Sopenharmony_ci#define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ 808c2ecf20Sopenharmony_ci#define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19) 838c2ecf20Sopenharmony_ci#define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18) 848c2ecf20Sopenharmony_ci#define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17) 858c2ecf20Sopenharmony_ci#define USB_DWC_CTRL3_OTG0_CKEN (1 << 16) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define USB_INTEN_FORCE 0x20 908c2ecf20Sopenharmony_ci#define USB_INTEN_PHY 0x10 918c2ecf20Sopenharmony_ci#define USB_INTEN_UDC 0x08 928c2ecf20Sopenharmony_ci#define USB_INTEN_EHCI 0x04 938c2ecf20Sopenharmony_ci#define USB_INTEN_OHCI1 0x02 948c2ecf20Sopenharmony_ci#define USB_INTEN_OHCI0 0x01 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(alchemy_usb_lock); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline void __au1300_usb_phyctl(void __iomem *base, int enable) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci unsigned long r, s; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL2); 1038c2ecf20Sopenharmony_ci s = __raw_readl(base + USB_DWC_CTRL3); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN | 1068c2ecf20Sopenharmony_ci USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (enable) { 1098c2ecf20Sopenharmony_ci /* simply enable all PHYs */ 1108c2ecf20Sopenharmony_ci r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | 1118c2ecf20Sopenharmony_ci USB_DWC_CTRL2_PHYRS; 1128c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL2); 1138c2ecf20Sopenharmony_ci wmb(); 1148c2ecf20Sopenharmony_ci } else if (!s) { 1158c2ecf20Sopenharmony_ci /* no USB block active, do disable all PHYs */ 1168c2ecf20Sopenharmony_ci r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | 1178c2ecf20Sopenharmony_ci USB_DWC_CTRL2_PHYRS); 1188c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL2); 1198c2ecf20Sopenharmony_ci wmb(); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic inline void __au1300_ohci_control(void __iomem *base, int enable, int id) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned long r; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (enable) { 1288c2ecf20Sopenharmony_ci __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */ 1298c2ecf20Sopenharmony_ci wmb(); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */ 1328c2ecf20Sopenharmony_ci r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN 1338c2ecf20Sopenharmony_ci : USB_DWC_CTRL3_OHCI1_CKEN; 1348c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 1358c2ecf20Sopenharmony_ci wmb(); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); /* power up the PHYs */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 1408c2ecf20Sopenharmony_ci r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1; 1418c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 1428c2ecf20Sopenharmony_ci wmb(); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* reset the OHCI start clock bit */ 1458c2ecf20Sopenharmony_ci __raw_writel(0, base + USB_DWC_CTRL7); 1468c2ecf20Sopenharmony_ci wmb(); 1478c2ecf20Sopenharmony_ci } else { 1488c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 1498c2ecf20Sopenharmony_ci r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1); 1508c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 1518c2ecf20Sopenharmony_ci wmb(); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 1548c2ecf20Sopenharmony_ci r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN 1558c2ecf20Sopenharmony_ci : USB_DWC_CTRL3_OHCI1_CKEN); 1568c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 1578c2ecf20Sopenharmony_ci wmb(); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic inline void __au1300_ehci_control(void __iomem *base, int enable) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci unsigned long r; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (enable) { 1688c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 1698c2ecf20Sopenharmony_ci r |= USB_DWC_CTRL3_EHCI0_CKEN; 1708c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 1718c2ecf20Sopenharmony_ci wmb(); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 1748c2ecf20Sopenharmony_ci r |= USB_DWC_CTRL1_HSTRS; 1758c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 1768c2ecf20Sopenharmony_ci wmb(); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 1818c2ecf20Sopenharmony_ci r |= USB_INTEN_EHCI; 1828c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 1838c2ecf20Sopenharmony_ci wmb(); 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 1868c2ecf20Sopenharmony_ci r &= ~USB_INTEN_EHCI; 1878c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 1888c2ecf20Sopenharmony_ci wmb(); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 1918c2ecf20Sopenharmony_ci r &= ~USB_DWC_CTRL1_HSTRS; 1928c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 1938c2ecf20Sopenharmony_ci wmb(); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 1968c2ecf20Sopenharmony_ci r &= ~USB_DWC_CTRL3_EHCI0_CKEN; 1978c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 1988c2ecf20Sopenharmony_ci wmb(); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic inline void __au1300_udc_control(void __iomem *base, int enable) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci unsigned long r; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (enable) { 2098c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 2108c2ecf20Sopenharmony_ci r |= USB_DWC_CTRL1_DCRS; 2118c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 2128c2ecf20Sopenharmony_ci wmb(); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 2178c2ecf20Sopenharmony_ci r |= USB_INTEN_UDC; 2188c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 2198c2ecf20Sopenharmony_ci wmb(); 2208c2ecf20Sopenharmony_ci } else { 2218c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_INT_ENABLE); 2228c2ecf20Sopenharmony_ci r &= ~USB_INTEN_UDC; 2238c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_INT_ENABLE); 2248c2ecf20Sopenharmony_ci wmb(); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 2278c2ecf20Sopenharmony_ci r &= ~USB_DWC_CTRL1_DCRS; 2288c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 2298c2ecf20Sopenharmony_ci wmb(); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic inline void __au1300_otg_control(void __iomem *base, int enable) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci unsigned long r; 2388c2ecf20Sopenharmony_ci if (enable) { 2398c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 2408c2ecf20Sopenharmony_ci r |= USB_DWC_CTRL3_OTG0_CKEN; 2418c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 2428c2ecf20Sopenharmony_ci wmb(); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 2458c2ecf20Sopenharmony_ci r &= ~USB_DWC_CTRL1_OTGD; 2468c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 2478c2ecf20Sopenharmony_ci wmb(); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 2508c2ecf20Sopenharmony_ci } else { 2518c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL1); 2528c2ecf20Sopenharmony_ci r |= USB_DWC_CTRL1_OTGD; 2538c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL1); 2548c2ecf20Sopenharmony_ci wmb(); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci r = __raw_readl(base + USB_DWC_CTRL3); 2578c2ecf20Sopenharmony_ci r &= ~USB_DWC_CTRL3_OTG0_CKEN; 2588c2ecf20Sopenharmony_ci __raw_writel(r, base + USB_DWC_CTRL3); 2598c2ecf20Sopenharmony_ci wmb(); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci __au1300_usb_phyctl(base, enable); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic inline int au1300_usb_control(int block, int enable) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci void __iomem *base = 2688c2ecf20Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); 2698c2ecf20Sopenharmony_ci int ret = 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci switch (block) { 2728c2ecf20Sopenharmony_ci case ALCHEMY_USB_OHCI0: 2738c2ecf20Sopenharmony_ci __au1300_ohci_control(base, enable, 0); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case ALCHEMY_USB_OHCI1: 2768c2ecf20Sopenharmony_ci __au1300_ohci_control(base, enable, 1); 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci case ALCHEMY_USB_EHCI0: 2798c2ecf20Sopenharmony_ci __au1300_ehci_control(base, enable); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case ALCHEMY_USB_UDC0: 2828c2ecf20Sopenharmony_ci __au1300_udc_control(base, enable); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case ALCHEMY_USB_OTG0: 2858c2ecf20Sopenharmony_ci __au1300_otg_control(base, enable); 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci default: 2888c2ecf20Sopenharmony_ci ret = -ENODEV; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic inline void au1300_usb_init(void) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci void __iomem *base = 2968c2ecf20Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 2998c2ecf20Sopenharmony_ci * here at all: Port 2 routing (EHCI or UDC) must be set either 3008c2ecf20Sopenharmony_ci * by boot firmware or platform init code; I can't autodetect 3018c2ecf20Sopenharmony_ci * a sane setting. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */ 3048c2ecf20Sopenharmony_ci wmb(); 3058c2ecf20Sopenharmony_ci __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */ 3068c2ecf20Sopenharmony_ci wmb(); 3078c2ecf20Sopenharmony_ci __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */ 3088c2ecf20Sopenharmony_ci wmb(); 3098c2ecf20Sopenharmony_ci __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */ 3108c2ecf20Sopenharmony_ci wmb(); 3118c2ecf20Sopenharmony_ci /* set coherent access bit */ 3128c2ecf20Sopenharmony_ci __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL); 3138c2ecf20Sopenharmony_ci wmb(); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic inline void __au1200_ohci_control(void __iomem *base, int enable) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci unsigned long r = __raw_readl(base + AU1200_USBCFG); 3198c2ecf20Sopenharmony_ci if (enable) { 3208c2ecf20Sopenharmony_ci __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG); 3218c2ecf20Sopenharmony_ci wmb(); 3228c2ecf20Sopenharmony_ci udelay(2000); 3238c2ecf20Sopenharmony_ci } else { 3248c2ecf20Sopenharmony_ci __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG); 3258c2ecf20Sopenharmony_ci wmb(); 3268c2ecf20Sopenharmony_ci udelay(1000); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic inline void __au1200_ehci_control(void __iomem *base, int enable) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci unsigned long r = __raw_readl(base + AU1200_USBCFG); 3338c2ecf20Sopenharmony_ci if (enable) { 3348c2ecf20Sopenharmony_ci __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG); 3358c2ecf20Sopenharmony_ci wmb(); 3368c2ecf20Sopenharmony_ci udelay(1000); 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci if (!(r & USBCFG_UCE)) /* UDC also off? */ 3398c2ecf20Sopenharmony_ci r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ 3408c2ecf20Sopenharmony_ci __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG); 3418c2ecf20Sopenharmony_ci wmb(); 3428c2ecf20Sopenharmony_ci udelay(1000); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic inline void __au1200_udc_control(void __iomem *base, int enable) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci unsigned long r = __raw_readl(base + AU1200_USBCFG); 3498c2ecf20Sopenharmony_ci if (enable) { 3508c2ecf20Sopenharmony_ci __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG); 3518c2ecf20Sopenharmony_ci wmb(); 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci if (!(r & USBCFG_ECE)) /* EHCI also off? */ 3548c2ecf20Sopenharmony_ci r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ 3558c2ecf20Sopenharmony_ci __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG); 3568c2ecf20Sopenharmony_ci wmb(); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic inline int au1200_usb_control(int block, int enable) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci void __iomem *base = 3638c2ecf20Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci switch (block) { 3668c2ecf20Sopenharmony_ci case ALCHEMY_USB_OHCI0: 3678c2ecf20Sopenharmony_ci __au1200_ohci_control(base, enable); 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case ALCHEMY_USB_UDC0: 3708c2ecf20Sopenharmony_ci __au1200_udc_control(base, enable); 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case ALCHEMY_USB_EHCI0: 3738c2ecf20Sopenharmony_ci __au1200_ehci_control(base, enable); 3748c2ecf20Sopenharmony_ci break; 3758c2ecf20Sopenharmony_ci default: 3768c2ecf20Sopenharmony_ci return -ENODEV; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/* initialize USB block(s) to a known working state */ 3838c2ecf20Sopenharmony_cistatic inline void au1200_usb_init(void) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci void __iomem *base = 3868c2ecf20Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); 3878c2ecf20Sopenharmony_ci __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG); 3888c2ecf20Sopenharmony_ci wmb(); 3898c2ecf20Sopenharmony_ci udelay(1000); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic inline int au1000_usb_init(unsigned long rb, int reg) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg); 3958c2ecf20Sopenharmony_ci unsigned long r = __raw_readl(base); 3968c2ecf20Sopenharmony_ci struct clk *c; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 48MHz check. Don't init if no one can provide it */ 3998c2ecf20Sopenharmony_ci c = clk_get(NULL, "usbh_clk"); 4008c2ecf20Sopenharmony_ci if (IS_ERR(c)) 4018c2ecf20Sopenharmony_ci return -ENODEV; 4028c2ecf20Sopenharmony_ci if (clk_round_rate(c, 48000000) != 48000000) { 4038c2ecf20Sopenharmony_ci clk_put(c); 4048c2ecf20Sopenharmony_ci return -ENODEV; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci if (clk_set_rate(c, 48000000)) { 4078c2ecf20Sopenharmony_ci clk_put(c); 4088c2ecf20Sopenharmony_ci return -ENODEV; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci clk_put(c); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN) 4138c2ecf20Sopenharmony_ci r |= USBHEN_BE; 4148c2ecf20Sopenharmony_ci#endif 4158c2ecf20Sopenharmony_ci r |= USBHEN_C; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci __raw_writel(r, base); 4188c2ecf20Sopenharmony_ci wmb(); 4198c2ecf20Sopenharmony_ci udelay(1000); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci void __iomem *base = (void __iomem *)KSEG1ADDR(rb); 4288c2ecf20Sopenharmony_ci unsigned long r = __raw_readl(base + creg); 4298c2ecf20Sopenharmony_ci struct clk *c = clk_get(NULL, "usbh_clk"); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (IS_ERR(c)) 4328c2ecf20Sopenharmony_ci return; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (enable) { 4358c2ecf20Sopenharmony_ci if (clk_prepare_enable(c)) 4368c2ecf20Sopenharmony_ci goto out; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci __raw_writel(r | USBHEN_CE, base + creg); 4398c2ecf20Sopenharmony_ci wmb(); 4408c2ecf20Sopenharmony_ci udelay(1000); 4418c2ecf20Sopenharmony_ci __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg); 4428c2ecf20Sopenharmony_ci wmb(); 4438c2ecf20Sopenharmony_ci udelay(1000); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* wait for reset complete (read reg twice: au1500 erratum) */ 4468c2ecf20Sopenharmony_ci while (__raw_readl(base + creg), 4478c2ecf20Sopenharmony_ci !(__raw_readl(base + creg) & USBHEN_RD)) 4488c2ecf20Sopenharmony_ci udelay(1000); 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg); 4518c2ecf20Sopenharmony_ci wmb(); 4528c2ecf20Sopenharmony_ci clk_disable_unprepare(c); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ciout: 4558c2ecf20Sopenharmony_ci clk_put(c); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic inline int au1000_usb_control(int block, int enable, unsigned long rb, 4598c2ecf20Sopenharmony_ci int creg) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci int ret = 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci switch (block) { 4648c2ecf20Sopenharmony_ci case ALCHEMY_USB_OHCI0: 4658c2ecf20Sopenharmony_ci __au1xx0_ohci_control(enable, rb, creg); 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci default: 4688c2ecf20Sopenharmony_ci ret = -ENODEV; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* 4748c2ecf20Sopenharmony_ci * alchemy_usb_control - control Alchemy on-chip USB blocks 4758c2ecf20Sopenharmony_ci * @block: USB block to target 4768c2ecf20Sopenharmony_ci * @enable: set 1 to enable a block, 0 to disable 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ciint alchemy_usb_control(int block, int enable) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci unsigned long flags; 4818c2ecf20Sopenharmony_ci int ret; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci spin_lock_irqsave(&alchemy_usb_lock, flags); 4848c2ecf20Sopenharmony_ci switch (alchemy_get_cputype()) { 4858c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 4868c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 4878c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1100: 4888c2ecf20Sopenharmony_ci ret = au1000_usb_control(block, enable, 4898c2ecf20Sopenharmony_ci AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 4928c2ecf20Sopenharmony_ci ret = au1000_usb_control(block, enable, 4938c2ecf20Sopenharmony_ci AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1200: 4968c2ecf20Sopenharmony_ci ret = au1200_usb_control(block, enable); 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 4998c2ecf20Sopenharmony_ci ret = au1300_usb_control(block, enable); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci default: 5028c2ecf20Sopenharmony_ci ret = -ENODEV; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&alchemy_usb_lock, flags); 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(alchemy_usb_control); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic unsigned long alchemy_usb_pmdata[2]; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic void au1000_usb_pm(unsigned long br, int creg, int susp) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci void __iomem *base = (void __iomem *)KSEG1ADDR(br); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (susp) { 5178c2ecf20Sopenharmony_ci alchemy_usb_pmdata[0] = __raw_readl(base + creg); 5188c2ecf20Sopenharmony_ci /* There appears to be some undocumented reset register.... */ 5198c2ecf20Sopenharmony_ci __raw_writel(0, base + 0x04); 5208c2ecf20Sopenharmony_ci wmb(); 5218c2ecf20Sopenharmony_ci __raw_writel(0, base + creg); 5228c2ecf20Sopenharmony_ci wmb(); 5238c2ecf20Sopenharmony_ci } else { 5248c2ecf20Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[0], base + creg); 5258c2ecf20Sopenharmony_ci wmb(); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void au1200_usb_pm(int susp) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci void __iomem *base = 5328c2ecf20Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR); 5338c2ecf20Sopenharmony_ci if (susp) { 5348c2ecf20Sopenharmony_ci /* save OTG_CAP/MUX registers which indicate port routing */ 5358c2ecf20Sopenharmony_ci /* FIXME: write an OTG driver to do that */ 5368c2ecf20Sopenharmony_ci alchemy_usb_pmdata[0] = __raw_readl(base + 0x00); 5378c2ecf20Sopenharmony_ci alchemy_usb_pmdata[1] = __raw_readl(base + 0x04); 5388c2ecf20Sopenharmony_ci } else { 5398c2ecf20Sopenharmony_ci /* restore access to all MMIO areas */ 5408c2ecf20Sopenharmony_ci au1200_usb_init(); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* restore OTG_CAP/MUX registers */ 5438c2ecf20Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[0], base + 0x00); 5448c2ecf20Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[1], base + 0x04); 5458c2ecf20Sopenharmony_ci wmb(); 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void au1300_usb_pm(int susp) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci void __iomem *base = 5528c2ecf20Sopenharmony_ci (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); 5538c2ecf20Sopenharmony_ci /* remember Port2 routing */ 5548c2ecf20Sopenharmony_ci if (susp) { 5558c2ecf20Sopenharmony_ci alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4); 5568c2ecf20Sopenharmony_ci } else { 5578c2ecf20Sopenharmony_ci au1300_usb_init(); 5588c2ecf20Sopenharmony_ci __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4); 5598c2ecf20Sopenharmony_ci wmb(); 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void alchemy_usb_pm(int susp) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci switch (alchemy_get_cputype()) { 5668c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 5678c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 5688c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1100: 5698c2ecf20Sopenharmony_ci au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp); 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 5728c2ecf20Sopenharmony_ci au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp); 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1200: 5758c2ecf20Sopenharmony_ci au1200_usb_pm(susp); 5768c2ecf20Sopenharmony_ci break; 5778c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 5788c2ecf20Sopenharmony_ci au1300_usb_pm(susp); 5798c2ecf20Sopenharmony_ci break; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int alchemy_usb_suspend(void) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci alchemy_usb_pm(1); 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic void alchemy_usb_resume(void) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci alchemy_usb_pm(0); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic struct syscore_ops alchemy_usb_pm_ops = { 5958c2ecf20Sopenharmony_ci .suspend = alchemy_usb_suspend, 5968c2ecf20Sopenharmony_ci .resume = alchemy_usb_resume, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic int __init alchemy_usb_init(void) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci int ret = 0; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci switch (alchemy_get_cputype()) { 6048c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1000: 6058c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1500: 6068c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1100: 6078c2ecf20Sopenharmony_ci ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR, 6088c2ecf20Sopenharmony_ci AU1000_OHCICFG); 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1550: 6118c2ecf20Sopenharmony_ci ret = au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR, 6128c2ecf20Sopenharmony_ci AU1550_OHCICFG); 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1200: 6158c2ecf20Sopenharmony_ci au1200_usb_init(); 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case ALCHEMY_CPU_AU1300: 6188c2ecf20Sopenharmony_ci au1300_usb_init(); 6198c2ecf20Sopenharmony_ci break; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (!ret) 6238c2ecf20Sopenharmony_ci register_syscore_ops(&alchemy_usb_pm_ops); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return ret; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ciarch_initcall(alchemy_usb_init); 628