// SPDX-License-Identifier: GPL-2.0-only /* * USB block power/access management abstraction. * * Au1000+: The OHCI block control register is at the far end of the OHCI memory * area. Au1550 has OHCI on different base address. No need to handle * UDC here. * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG * as well as the PHY for EHCI and UDC. *
*/
#define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ #define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ #define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */
#define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ #define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ #define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */
staticinlinevoid __au1300_usb_phyctl(void __iomem *base, int enable)
{ unsignedlong r, s;
r = __raw_readl(base + USB_DWC_CTRL2);
s = __raw_readl(base + USB_DWC_CTRL3);
s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN |
USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN;
if (enable) { /* simply enable all PHYs */
r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS |
USB_DWC_CTRL2_PHYRS;
__raw_writel(r, base + USB_DWC_CTRL2);
wmb();
} elseif (!s) { /* no USB block active, do disable all PHYs */
r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS |
USB_DWC_CTRL2_PHYRS);
__raw_writel(r, base + USB_DWC_CTRL2);
wmb();
}
}
staticinlinevoid __au1300_ohci_control(void __iomem *base, int enable, int id)
{ unsignedlong r;
if (enable) {
__raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */
wmb();
r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */
r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN
: USB_DWC_CTRL3_OHCI1_CKEN;
__raw_writel(r, base + USB_DWC_CTRL3);
wmb();
__au1300_usb_phyctl(base, enable); /* power up the PHYs */
r = __raw_readl(base + USB_INT_ENABLE);
r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1;
__raw_writel(r, base + USB_INT_ENABLE);
wmb();
/* reset the OHCI start clock bit */
__raw_writel(0, base + USB_DWC_CTRL7);
wmb();
} else {
r = __raw_readl(base + USB_INT_ENABLE);
r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1);
__raw_writel(r, base + USB_INT_ENABLE);
wmb();
r = __raw_readl(base + USB_DWC_CTRL3);
r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN
: USB_DWC_CTRL3_OHCI1_CKEN);
__raw_writel(r, base + USB_DWC_CTRL3);
wmb();
__au1300_usb_phyctl(base, enable);
}
}
staticinlinevoid __au1300_ehci_control(void __iomem *base, int enable)
{ unsignedlong r;
if (enable) {
r = __raw_readl(base + USB_DWC_CTRL3);
r |= USB_DWC_CTRL3_EHCI0_CKEN;
__raw_writel(r, base + USB_DWC_CTRL3);
wmb();
r = __raw_readl(base + USB_DWC_CTRL1);
r |= USB_DWC_CTRL1_HSTRS;
__raw_writel(r, base + USB_DWC_CTRL1);
wmb();
__au1300_usb_phyctl(base, enable);
r = __raw_readl(base + USB_INT_ENABLE);
r |= USB_INTEN_EHCI;
__raw_writel(r, base + USB_INT_ENABLE);
wmb();
} else {
r = __raw_readl(base + USB_INT_ENABLE);
r &= ~USB_INTEN_EHCI;
__raw_writel(r, base + USB_INT_ENABLE);
wmb();
r = __raw_readl(base + USB_DWC_CTRL1);
r &= ~USB_DWC_CTRL1_HSTRS;
__raw_writel(r, base + USB_DWC_CTRL1);
wmb();
r = __raw_readl(base + USB_DWC_CTRL3);
r &= ~USB_DWC_CTRL3_EHCI0_CKEN;
__raw_writel(r, base + USB_DWC_CTRL3);
wmb();
__au1300_usb_phyctl(base, enable);
}
}
staticinlinevoid __au1300_udc_control(void __iomem *base, int enable)
{ unsignedlong r;
if (enable) {
r = __raw_readl(base + USB_DWC_CTRL1);
r |= USB_DWC_CTRL1_DCRS;
__raw_writel(r, base + USB_DWC_CTRL1);
wmb();
__au1300_usb_phyctl(base, enable);
r = __raw_readl(base + USB_INT_ENABLE);
r |= USB_INTEN_UDC;
__raw_writel(r, base + USB_INT_ENABLE);
wmb();
} else {
r = __raw_readl(base + USB_INT_ENABLE);
r &= ~USB_INTEN_UDC;
__raw_writel(r, base + USB_INT_ENABLE);
wmb();
r = __raw_readl(base + USB_DWC_CTRL1);
r &= ~USB_DWC_CTRL1_DCRS;
__raw_writel(r, base + USB_DWC_CTRL1);
wmb();
__au1300_usb_phyctl(base, enable);
}
}
staticinlinevoid __au1300_otg_control(void __iomem *base, int enable)
{ unsignedlong r; if (enable) {
r = __raw_readl(base + USB_DWC_CTRL3);
r |= USB_DWC_CTRL3_OTG0_CKEN;
__raw_writel(r, base + USB_DWC_CTRL3);
wmb();
r = __raw_readl(base + USB_DWC_CTRL1);
r &= ~USB_DWC_CTRL1_OTGD;
__raw_writel(r, base + USB_DWC_CTRL1);
wmb();
__au1300_usb_phyctl(base, enable);
} else {
r = __raw_readl(base + USB_DWC_CTRL1);
r |= USB_DWC_CTRL1_OTGD;
__raw_writel(r, base + USB_DWC_CTRL1);
wmb();
r = __raw_readl(base + USB_DWC_CTRL3);
r &= ~USB_DWC_CTRL3_OTG0_CKEN;
__raw_writel(r, base + USB_DWC_CTRL3);
wmb();
__au1300_usb_phyctl(base, enable);
}
}
staticinlineint au1300_usb_control(int block, int enable)
{ void __iomem *base =
(void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); int ret = 0;
switch (block) { case ALCHEMY_USB_OHCI0:
__au1300_ohci_control(base, enable, 0); break; case ALCHEMY_USB_OHCI1:
__au1300_ohci_control(base, enable, 1); break; case ALCHEMY_USB_EHCI0:
__au1300_ehci_control(base, enable); break; case ALCHEMY_USB_UDC0:
__au1300_udc_control(base, enable); break; case ALCHEMY_USB_OTG0:
__au1300_otg_control(base, enable); break; default:
ret = -ENODEV;
} return ret;
}
/* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 * here at all: Port 2 routing (EHCI or UDC) must be set either * by boot firmware or platform init code; I can't autodetect * a sane setting.
*/
__raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */
wmb();
__raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */
wmb();
__raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */
wmb();
__raw_writel(~0, base + USB_INT_STATUS); /* clear int status */
wmb(); /* set coherent access bit */
__raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL);
wmb();
}
staticinlinevoid __au1200_ohci_control(void __iomem *base, int enable)
{ unsignedlong r = __raw_readl(base + AU1200_USBCFG); if (enable) {
__raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG);
wmb();
udelay(2000);
} else {
__raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG);
wmb();
udelay(1000);
}
}
staticinlinevoid __au1200_ehci_control(void __iomem *base, int enable)
{ unsignedlong r = __raw_readl(base + AU1200_USBCFG); if (enable) {
__raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG);
wmb();
udelay(1000);
} else { if (!(r & USBCFG_UCE)) /* UDC also off? */
r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */
__raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG);
wmb();
udelay(1000);
}
}
staticinlinevoid __au1200_udc_control(void __iomem *base, int enable)
{ unsignedlong r = __raw_readl(base + AU1200_USBCFG); if (enable) {
__raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG);
wmb();
} else { if (!(r & USBCFG_ECE)) /* EHCI also off? */
r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */
__raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG);
wmb();
}
}
switch (block) { case ALCHEMY_USB_OHCI0:
__au1200_ohci_control(base, enable); break; case ALCHEMY_USB_UDC0:
__au1200_udc_control(base, enable); break; case ALCHEMY_USB_EHCI0:
__au1200_ehci_control(base, enable); break; default: return -ENODEV;
} return 0;
}
/* initialize USB block(s) to a known working state */ staticinlinevoid au1200_usb_init(void)
{ void __iomem *base =
(void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR);
__raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG);
wmb();
udelay(1000);
}
/* 48MHz check. Don't init if no one can provide it */
c = clk_get(NULL, "usbh_clk"); if (IS_ERR(c)) return -ENODEV; if (clk_round_rate(c, 48000000) != 48000000) {
clk_put(c); return -ENODEV;
} if (clk_set_rate(c, 48000000)) {
clk_put(c); return -ENODEV;
}
clk_put(c);
#ifdefined(__BIG_ENDIAN)
r |= USBHEN_BE; #endif
r |= USBHEN_C;
staticinlineint au1000_usb_control(int block, int enable, unsignedlong rb, int creg)
{ int ret = 0;
switch (block) { case ALCHEMY_USB_OHCI0:
__au1xx0_ohci_control(enable, rb, creg); break; default:
ret = -ENODEV;
} return ret;
}
/* * alchemy_usb_control - control Alchemy on-chip USB blocks * @block: USB block to target * @enable: set 1 to enable a block, 0 to disable
*/ int alchemy_usb_control(int block, int enable)
{ unsignedlong flags; int ret;
spin_lock_irqsave(&alchemy_usb_lock, flags); switch (alchemy_get_cputype()) { case ALCHEMY_CPU_AU1000: case ALCHEMY_CPU_AU1500: case ALCHEMY_CPU_AU1100:
ret = au1000_usb_control(block, enable,
AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); break; case ALCHEMY_CPU_AU1550:
ret = au1000_usb_control(block, enable,
AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); break; case ALCHEMY_CPU_AU1200:
ret = au1200_usb_control(block, enable); break; case ALCHEMY_CPU_AU1300:
ret = au1300_usb_control(block, enable); break; default:
ret = -ENODEV;
}
spin_unlock_irqrestore(&alchemy_usb_lock, flags); return ret;
}
EXPORT_SYMBOL_GPL(alchemy_usb_control);
staticunsignedlong alchemy_usb_pmdata[2];
staticvoid au1000_usb_pm(unsignedlong br, int creg, int susp)
{ void __iomem *base = (void __iomem *)KSEG1ADDR(br);
if (susp) {
alchemy_usb_pmdata[0] = __raw_readl(base + creg); /* There appears to be some undocumented reset register.... */
__raw_writel(0, base + 0x04);
wmb();
__raw_writel(0, base + creg);
wmb();
} else {
__raw_writel(alchemy_usb_pmdata[0], base + creg);
wmb();
}
}
staticvoid au1200_usb_pm(int susp)
{ void __iomem *base =
(void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR); if (susp) { /* save OTG_CAP/MUX registers which indicate port routing */ /* FIXME: write an OTG driver to do that */
alchemy_usb_pmdata[0] = __raw_readl(base + 0x00);
alchemy_usb_pmdata[1] = __raw_readl(base + 0x04);
} else { /* restore access to all MMIO areas */
au1200_usb_init();
/* restore OTG_CAP/MUX registers */
__raw_writel(alchemy_usb_pmdata[0], base + 0x00);
__raw_writel(alchemy_usb_pmdata[1], base + 0x04);
wmb();
}
}
staticint __init alchemy_usb_init(void)
{ int ret = 0;
switch (alchemy_get_cputype()) { case ALCHEMY_CPU_AU1000: case ALCHEMY_CPU_AU1500: case ALCHEMY_CPU_AU1100:
ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR,
AU1000_OHCICFG); break; case ALCHEMY_CPU_AU1550:
ret = au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR,
AU1550_OHCICFG); break; case ALCHEMY_CPU_AU1200:
au1200_usb_init(); break; case ALCHEMY_CPU_AU1300:
au1300_usb_init(); break;
}
if (!ret)
register_syscore_ops(&alchemy_usb_pm_ops);
return ret;
}
arch_initcall(alchemy_usb_init);
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.