245 lines
6.8 KiB
C
245 lines
6.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/pci.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <asm/sw64io.h>
|
|
|
|
static int handshake(void __iomem *ptr, u32 mask, u32 done,
|
|
int wait_usec, int delay_usec)
|
|
{
|
|
u32 result;
|
|
|
|
do {
|
|
result = readl(ptr);
|
|
result &= mask;
|
|
if (result == done)
|
|
return 0;
|
|
udelay(delay_usec);
|
|
wait_usec -= delay_usec;
|
|
} while (wait_usec > 0);
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
#define XHCI_HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff)
|
|
#define XHCI_EXT_CAPS_ID(p) (((p) >> 0) & 0xff)
|
|
#define XHCI_EXT_CAPS_NEXT(p) (((p) >> 8) & 0xff)
|
|
#define XHCI_HC_LENGTH(p) (((p) >> 0) & 0x00ff)
|
|
#define XHCI_CMD_OFFSET (0x00)
|
|
#define XHCI_STS_OFFSET (0x04)
|
|
#define XHCI_EXT_CAPS_LEGACY (1)
|
|
#define XHCI_HCC_PARAMS_OFFSET (0x10)
|
|
#define XHCI_LEGACY_CONTROL_OFFSET (0x04)
|
|
#define XHCI_LEGACY_DISABLE_SMI ((0x7 << 1) + (0xff << 5) + (0x7 << 17))
|
|
#define XHCI_LEGACY_SMI_EVENTS (0x7 << 29)
|
|
#define XHCI_HC_BIOS_OWNED (1 << 16)
|
|
#define XHCI_HC_OS_OWNED (1 << 24)
|
|
#define XHCI_CMD_RUN (1 << 0)
|
|
#define XHCI_STS_HALT (1 << 0)
|
|
#define XHCI_MAX_HALT_USEC (16 * 1000)
|
|
#define XHCI_CMD_EIE (1 << 2)
|
|
#define XHCI_CMD_HSEIE (1 << 3)
|
|
#define XHCI_CMD_EWE (1 << 10)
|
|
#define XHCI_IRQS (XHCI_CMD_EIE | XHCI_CMD_HSEIE | XHCI_CMD_EWE)
|
|
#define XHCI_STS_CNR (1 << 11)
|
|
#define STS_FATAL (1 << 2)
|
|
#define STS_EINT (1 << 3)
|
|
#define STS_PORT (1 << 4)
|
|
#define STS_SRE (1 << 10)
|
|
#define STS_RW1C_BITS (STS_FATAL | STS_EINT | STS_PORT | STS_SRE)
|
|
|
|
static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
|
|
{
|
|
u32 val;
|
|
u32 next;
|
|
u32 offset;
|
|
|
|
offset = start;
|
|
if (!start || start == XHCI_HCC_PARAMS_OFFSET) {
|
|
val = readl(base + XHCI_HCC_PARAMS_OFFSET);
|
|
if (val == ~0)
|
|
return 0;
|
|
offset = XHCI_HCC_EXT_CAPS(val) << 2;
|
|
if (!offset)
|
|
return 0;
|
|
}
|
|
do {
|
|
val = readl(base + offset);
|
|
if (val == ~0)
|
|
return 0;
|
|
if (offset != start && (id == 0 || XHCI_EXT_CAPS_ID(val) == id))
|
|
return offset;
|
|
|
|
next = XHCI_EXT_CAPS_NEXT(val);
|
|
offset += next << 2;
|
|
} while (next);
|
|
|
|
return 0;
|
|
}
|
|
|
|
extern void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
|
|
|
|
static void
|
|
fixup_usb_xhci_reset(struct pci_dev *dev)
|
|
{
|
|
void __iomem *op_reg_base;
|
|
int timeout;
|
|
u32 xhci_command;
|
|
u32 tmp, val;
|
|
void __iomem *base;
|
|
struct pci_controller *hose = dev->sysdata;
|
|
unsigned long offset;
|
|
int ext_cap_offset;
|
|
int retries = 3;
|
|
|
|
pci_read_config_dword(dev, PCI_COMMAND, &tmp);
|
|
tmp |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
|
pci_write_config_dword(dev, PCI_COMMAND, tmp);
|
|
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &tmp);
|
|
if (tmp & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
|
|
pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, &val);
|
|
offset = (unsigned long)(val) << 32 | (tmp & (~0xf));
|
|
} else
|
|
offset = (unsigned long)(tmp & (~0xf));
|
|
|
|
if (offset == 0)
|
|
return;
|
|
|
|
base = (void *)__va(SW64_PCI_IO_BASE(hose->node, hose->index) | offset);
|
|
|
|
ext_cap_offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_LEGACY);
|
|
if (!ext_cap_offset)
|
|
goto hc_init;
|
|
|
|
val = readl(base + ext_cap_offset);
|
|
|
|
if ((dev->vendor == PCI_VENDOR_ID_TI && dev->device == 0x8241) ||
|
|
(dev->vendor == PCI_VENDOR_ID_RENESAS
|
|
&& dev->device == 0x0014)) {
|
|
val = (val | XHCI_HC_OS_OWNED) & ~XHCI_HC_BIOS_OWNED;
|
|
writel(val, base + ext_cap_offset);
|
|
}
|
|
|
|
if (val & XHCI_HC_BIOS_OWNED) {
|
|
writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset);
|
|
|
|
timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
|
|
0, 1000000, 10);
|
|
if (timeout) {
|
|
pr_err("xHCI BIOS handoff failed (BIOS bug ?) %08x\n", val);
|
|
writel(val & ~XHCI_HC_BIOS_OWNED, base + ext_cap_offset);
|
|
}
|
|
}
|
|
|
|
val = readl(base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
|
|
val &= XHCI_LEGACY_DISABLE_SMI;
|
|
val |= XHCI_LEGACY_SMI_EVENTS;
|
|
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
|
|
|
|
hc_init:
|
|
if (dev->vendor == PCI_VENDOR_ID_INTEL)
|
|
usb_enable_intel_xhci_ports(dev);
|
|
|
|
op_reg_base = base + XHCI_HC_LENGTH(readl(base));
|
|
|
|
timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_CNR, 0,
|
|
5000000, 10);
|
|
if (timeout) {
|
|
val = readl(op_reg_base + XHCI_STS_OFFSET);
|
|
pr_err("xHCI HW not ready after 5 sec (HC bug?) status = 0x%x\n", val);
|
|
}
|
|
|
|
xhci_command = readl(op_reg_base + XHCI_CMD_OFFSET);
|
|
xhci_command |= 0x2;
|
|
writel(xhci_command, op_reg_base + XHCI_CMD_OFFSET);
|
|
|
|
timeout = handshake(op_reg_base + XHCI_CMD_OFFSET,
|
|
0x2, 0, 10 * 1000 * 1000, 125);
|
|
if (timeout)
|
|
pr_err("xHCI BIOS handoff time out\n");
|
|
|
|
retry:
|
|
val = readl(op_reg_base + XHCI_STS_OFFSET);
|
|
val |= STS_RW1C_BITS;
|
|
writel(val, op_reg_base + XHCI_STS_OFFSET);
|
|
val = readl(op_reg_base + XHCI_STS_OFFSET);
|
|
|
|
if ((val & STS_RW1C_BITS) && retries--) {
|
|
pr_err("clear USB Status Register (status = %#x) failed, retry\n", val);
|
|
goto retry;
|
|
}
|
|
|
|
val = readl(op_reg_base + XHCI_CMD_OFFSET);
|
|
val &= ~(XHCI_CMD_RUN | XHCI_IRQS);
|
|
writel(val, op_reg_base + XHCI_CMD_OFFSET);
|
|
timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_HALT, 1,
|
|
XHCI_MAX_HALT_USEC, 125);
|
|
if (timeout) {
|
|
val = readl(op_reg_base + XHCI_STS_OFFSET);
|
|
pr_err("xHCI HW did not halt within %d usec status = 0x%x\n",
|
|
XHCI_MAX_HALT_USEC, val);
|
|
}
|
|
|
|
xhci_command = readl(op_reg_base + XHCI_CMD_OFFSET);
|
|
xhci_command |= 0x2;
|
|
writel(xhci_command, op_reg_base + XHCI_CMD_OFFSET);
|
|
|
|
timeout = handshake(op_reg_base + XHCI_CMD_OFFSET,
|
|
0x2, 0, 10 * 1000 * 1000, 125);
|
|
if (timeout)
|
|
pr_err("xHCI BIOS handoff time out\n");
|
|
|
|
pci_read_config_dword(dev, PCI_COMMAND, &tmp);
|
|
tmp &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
|
pci_write_config_dword(dev, PCI_COMMAND, tmp);
|
|
}
|
|
DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
|
|
PCI_CLASS_SERIAL_USB_XHCI, 0, fixup_usb_xhci_reset);
|
|
|
|
#ifdef CONFIG_DCA
|
|
static void enable_sw_dca(struct pci_dev *dev)
|
|
{
|
|
struct pci_controller *hose = (struct pci_controller *)dev->sysdata;
|
|
unsigned long node, rc_index, dca_ctl, dca_conf;
|
|
int i;
|
|
|
|
if (dev->class >> 8 != PCI_CLASS_NETWORK_ETHERNET)
|
|
return;
|
|
node = hose->node;
|
|
rc_index = hose->index;
|
|
for (i = 0; i < 256; i++) {
|
|
dca_conf = read_piu_ior1(node, rc_index, DEVICEID0 + (i << 7));
|
|
if (dca_conf >> 63)
|
|
continue;
|
|
else {
|
|
dca_conf = (1UL << 63) | (dev->bus->number << 8) | dev->devfn;
|
|
printk("dca device index %d, dca_conf = %#lx\n", i, dca_conf);
|
|
write_piu_ior1(node, rc_index, DEVICEID0 + (i << 7), dca_conf);
|
|
break;
|
|
}
|
|
}
|
|
dca_ctl = read_piu_ior1(node, rc_index, DCACONTROL);
|
|
if (dca_ctl & 0x1) {
|
|
dca_ctl = 0x2;
|
|
write_piu_ior1(node, rc_index, DCACONTROL, dca_ctl);
|
|
printk("Node %ld RC %ld enable DCA 1.0\n", node, rc_index);
|
|
}
|
|
}
|
|
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, enable_sw_dca);
|
|
#endif
|
|
|
|
void __init reserve_mem_for_pci(void)
|
|
{
|
|
int ret;
|
|
unsigned long base = PCI_32BIT_MEMIO;
|
|
|
|
ret = add_memmap_region(base, PCI_32BIT_MEMIO_SIZE, memmap_pci);
|
|
if (ret) {
|
|
pr_err("reserved pages for pcie memory space failed\n");
|
|
return;
|
|
}
|
|
|
|
pr_info("reserved pages for pcie memory space %lx:%lx\n", base >> PAGE_SHIFT,
|
|
(base + PCI_32BIT_MEMIO_SIZE) >> PAGE_SHIFT);
|
|
}
|