1
0
Files
kernel-49/arch/mips/tc3262/pci_en75xx.c
Andrey Zolotarev 61a062159e tc3262: tune delays on init PCIe
Follow up Econet SDK.
2022-02-13 01:34:36 +07:00

644 lines
15 KiB
C

#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/pci.h>
#include <asm/io.h>
#include <asm/irq.h>
#ifdef CONFIG_PCI
#include <asm/tc3162/rt_mmap.h>
#include <asm/tc3162/surfboardint.h>
#include <asm/tc3162/tc3162.h>
#define ECONET_RSTCTRL_REG (RALINK_SYSCTL_BASE + 0x834)
#define ECONET_PCIEC_REG (RALINK_SYSCTL_BASE + 0x088)
#define ECONET_PCIE_ERR_IRQ_EN (RALINK_PCI_BASE + 0x0040)
#define ECONET_PCIE_LINKUP (RALINK_PCI_BASE + 0x0050)
#define ECONET_PCIE0_MAC_BASE (RALINK_PCI_BASE + 0x1000)
#define ECONET_PCIE1_MAC_BASE (RALINK_PCI_BASE + 0x3000)
#define ECONET_PCI_MM_MAP_BASE 0x20000000
#define ECONET_PCI_IO_MAP_BASE 0x1F600000
static int pcie_link_status;
static DEFINE_SPINLOCK(asic_pcr_lock);
static u32 en75xx_get_rc_port(u32 busn, u32 slot)
{
u32 rc = 2;
if (busn == 0 && slot < 2)
rc = slot;
else if (busn == 1 && slot == 0 && (pcie_link_status & 0x1))
rc = 0;
else if (busn == 2 && slot == 0 && (pcie_link_status & 0x2))
rc = 1;
return rc;
}
static int
en75xx_write_config_word(u32 busn, u32 slot, u32 func, u32 reg, u32 value)
{
u32 val, rc, mac_offset;
rc = en75xx_get_rc_port(busn, slot);
if (rc == 0)
mac_offset = ECONET_PCIE0_MAC_BASE;
else if (rc == 1)
mac_offset = ECONET_PCIE1_MAC_BASE;
else
return -1;
/* fmt=2 | type=4 | length=1 */
val = (2 << 29) | (4 << 24) | 1;
/* write TLP header offset 0..3 */
sysRegWrite(mac_offset + 0x0460, val);
/* write requester ID */
val = (rc << 19) | 0x070f;
/* write TLP header offset 4..7 */
sysRegWrite(mac_offset + 0x0464, val);
val = (busn << 24) | (slot << 19) | (func << 16) | (reg & 0xffc);
/* write TLP header offset 8..11 */
sysRegWrite(mac_offset + 0x0468, val);
/* write TLP data */
sysRegWrite(mac_offset + 0x0470, value);
/* start TLP request */
sysRegWrite(mac_offset + 0x0488, 0x1);
udelay(10);
/* polling TLP request status (max 10ms) */
val = 1000;
while ((val--) > 0) {
/* TLP request finished or timeout */
if ((sysRegRead(mac_offset + 0x0488) & 0x1) == 0)
break;
udelay(10);
}
if (val == 0) {
printk(KERN_ERR "PCR %s err: bus = %d, dev = %d, func = %d, reg = 0x%08X\n",
"write", busn, slot, func, reg);
return -1;
}
return 0;
}
static u32
en75xx_read_config_word(u32 busn, u32 slot, u32 func, u32 reg)
{
u32 val, rc, mac_offset;
rc = en75xx_get_rc_port(busn, slot);
if (rc == 0)
mac_offset = ECONET_PCIE0_MAC_BASE;
else if (rc == 1)
mac_offset = ECONET_PCIE1_MAC_BASE;
else
return 0xffffffff;
/* initialize the data reg */
sysRegWrite(mac_offset + 0x048c, 0xffffffff);
/* fmt=0 | type=4 | length=1 */
val = (4 << 24) | 1;
/* write TLP header offset 0..3 */
sysRegWrite(mac_offset + 0x0460, val);
/* write requester ID */
val = (rc << 19) | 0x070f;
/* write TLP header offset 4..7 */
sysRegWrite(mac_offset + 0x0464, val);
val = (busn << 24) | (slot << 19) | (func << 16) | (reg & 0xffc);
/* write TLP header offset 8..11 */
sysRegWrite(mac_offset + 0x0468, val);
/* start TLP requuest */
sysRegWrite(mac_offset + 0x0488, 0x1);
udelay(10);
/* polling TLP request status (max 10ms) */
val = 1000;
while ((val--) > 0) {
/* TLP request finished or timeout */
if ((sysRegRead(mac_offset + 0x0488) & 0x1) == 0)
break;
udelay(10);
}
if (val == 0) {
printk(KERN_ERR "PCR %s err: bus = %d, dev = %d, func = %d, reg = 0x%08X\n",
"read", busn, slot, func, reg);
return 0xffffffff;
}
/* return the data from data reg */
return sysRegRead(mac_offset + 0x048c);
}
static int
en75xx_pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val)
{
u32 busn = bus->number;
u32 slot = PCI_SLOT(devfn);
u32 func = PCI_FUNC(devfn);
u32 shift, reg, tmp;
unsigned long flags;
reg = (u32)where;
spin_lock_irqsave(&asic_pcr_lock, flags);
tmp = en75xx_read_config_word(busn, slot, func, reg);
spin_unlock_irqrestore(&asic_pcr_lock, flags);
switch (size) {
case 1:
shift = (reg & 0x3) << 3;
*val = (tmp >> shift) & 0xff;
break;
case 2:
shift = (reg & 0x2) << 3;
*val = (tmp >> shift) & 0xffff;
break;
default:
*val = tmp;
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int
en75xx_pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
{
u32 busn = bus->number;
u32 slot = PCI_SLOT(devfn);
u32 func = PCI_FUNC(devfn);
u32 shift, reg, tmp;
unsigned long flags;
reg = (u32)where;
spin_lock_irqsave(&asic_pcr_lock, flags);
switch (size) {
case 1:
tmp = en75xx_read_config_word(busn, slot, func, reg);
shift = (reg & 0x3) << 3;
tmp &= ~(0xff << shift);
tmp |= ((val & 0xff) << shift);
break;
case 2:
tmp = en75xx_read_config_word(busn, slot, func, reg);
shift = (reg & 0x2) << 3;
tmp &= ~(0xffff << shift);
tmp |= ((val & 0xffff) << shift);
break;
default:
tmp = val;
break;
}
en75xx_write_config_word(busn, slot, func, reg, tmp);
spin_unlock_irqrestore(&asic_pcr_lock, flags);
return PCIBIOS_SUCCESSFUL;
}
/*
* General-purpose PCI functions.
*/
struct pci_ops en75xx_pci_ops = {
.read = en75xx_pci_config_read,
.write = en75xx_pci_config_write,
};
static struct resource en75xx_res_pci_mem1 = {
.name = "PCI MEM1",
.start = ECONET_PCI_MM_MAP_BASE,
.end = ECONET_PCI_MM_MAP_BASE + 0x0fffffff,
.flags = IORESOURCE_MEM,
};
static struct resource en75xx_res_pci_io1 = {
.name = "PCI I/O1",
.start = ECONET_PCI_IO_MAP_BASE,
.end = ECONET_PCI_IO_MAP_BASE + 0xffff,
.flags = IORESOURCE_IO,
};
struct pci_controller en75xx_pci_controller = {
.pci_ops = &en75xx_pci_ops,
.mem_resource = &en75xx_res_pci_mem1,
.io_resource = &en75xx_res_pci_io1,
.mem_offset = 0x00000000UL,
.io_offset = 0x00000000UL,
};
#if defined(CONFIG_ECONET_EN7516) || \
defined(CONFIG_ECONET_EN7527) || \
defined(CONFIG_PCIE_PORT1)
static u32 en75xx_pcie_get_pos(u32 busn, u32 slot)
{
u32 val, pos;
val = en75xx_read_config_word(busn, slot, 0, 0x34);
pos = val & 0xff;
while (pos && pos != 0xff) {
val = en75xx_read_config_word(busn, slot, 0, pos);
if ((val & 0xff) == 0x10)
return pos;
pos = (val >> 0x08) & 0xff;
}
return 0;
}
#endif
static inline void en75xx_pcie_rc0_retrain(void)
{
#if defined(CONFIG_ECONET_EN7516) || \
defined(CONFIG_ECONET_EN7527)
u32 pos, ppos;
u32 linkcap, plinkcap, plinksta[2];
unsigned long flags;
spin_lock_irqsave(&asic_pcr_lock, flags);
ppos = en75xx_pcie_get_pos(0, 0);
pos = en75xx_pcie_get_pos(1, 0);
if (pos < 0x40 || ppos < 0x40)
goto exit_unlock;
plinkcap = en75xx_read_config_word(0, 0, 0, ppos + 0x0c);
linkcap = en75xx_read_config_word(1, 0, 0, pos + 0x0c);
if ((linkcap & 0x0f) == 1 || (plinkcap & 0x0f) == 1)
goto exit_unlock;
plinksta[0] = en75xx_read_config_word(0, 0, 0, ppos + 0x10);
if (((plinksta[0] >> 16) & 0x0f) == (plinkcap & 0x0f))
goto exit_unlock;
/* train link Gen1 -> Gen2 */
plinksta[0] = en75xx_read_config_word(0, 0, 0, ppos + 0x10);
plinksta[0] |= 0x20;
en75xx_write_config_word(0, 0, 0, ppos + 0x10, plinksta[0]);
spin_unlock_irqrestore(&asic_pcr_lock, flags);
msleep(250);
spin_lock_irqsave(&asic_pcr_lock, flags);
plinksta[1] = en75xx_read_config_word(0, 0, 0, ppos + 0x10);
printk(KERN_INFO "PCIe RC0 link trained: %x -> %x\n",
((plinksta[0] >> 16) & 0x0f), ((plinksta[1] >> 16) & 0x0f));
exit_unlock:
spin_unlock_irqrestore(&asic_pcr_lock, flags);
#endif
}
#ifdef CONFIG_PCIE_PORT1
static inline void en75xx_pcie_rc1_retrain(void)
{
u32 pos, ppos;
u32 linkcap, plinkcap, plinksta[2];
unsigned long flags;
spin_lock_irqsave(&asic_pcr_lock, flags);
ppos = en75xx_pcie_get_pos(0, 1);
pos = en75xx_pcie_get_pos(2, 0);
if (pos < 0x40 || ppos < 0x40)
goto exit_unlock;
plinkcap = en75xx_read_config_word(0, 1, 0, ppos + 0x0c);
linkcap = en75xx_read_config_word(2, 0, 0, pos + 0x0c);
if ((linkcap & 0x0f) == 1 || (plinkcap & 0x0f) == 1)
goto exit_unlock;
plinksta[0] = en75xx_read_config_word(0, 1, 0, ppos + 0x10);
if (((plinksta[0] >> 16) & 0x0f) == (plinkcap & 0x0f))
goto exit_unlock;
/* train link Gen1 -> Gen2 */
plinksta[0] = en75xx_read_config_word(0, 1, 0, ppos + 0x10);
plinksta[0] |= 0x20;
en75xx_write_config_word(0, 1, 0, ppos + 0x10, plinksta[0]);
spin_unlock_irqrestore(&asic_pcr_lock, flags);
msleep(250);
spin_lock_irqsave(&asic_pcr_lock, flags);
plinksta[1] = en75xx_read_config_word(0, 1, 0, ppos + 0x10);
printk(KERN_INFO "PCIe RC1 link trained: %x -> %x\n",
((plinksta[0] >> 16) & 0x0f), ((plinksta[1] >> 16) & 0x0f));
exit_unlock:
spin_unlock_irqrestore(&asic_pcr_lock, flags);
}
#endif
int pcibios_plat_dev_init(struct pci_dev *dev)
{
u32 busn = dev->bus->number;
u32 slot = PCI_SLOT(dev->devfn);
u32 i, val, tmp;
/* P2P bridge */
if (busn == 0) {
/* set CLS */
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
(L1_CACHE_BYTES >> 2));
}
if (busn == 0 && slot == 0 && (pcie_link_status & 0x1)) {
val = 0;
pci_read_config_dword(dev, 0x20, &val);
tmp = (val & 0xffff) << 16;
val = (val & 0xffff0000) + 0x100000;
val = val - tmp;
i = 0;
while (i < 32) {
if ((1u << i) >= val)
break;
i++;
}
/* config RC0 to EP addr window */
sysRegWrite(ECONET_PCIE0_MAC_BASE + 0x438, tmp | i);
mdelay(1);
/* enable EP to RC0 access */
sysRegWrite(ECONET_PCIE0_MAC_BASE + 0x448, 0x80);
en75xx_pcie_rc0_retrain();
}
#ifdef CONFIG_PCIE_PORT1
if (busn == 0 && slot == 1 && (pcie_link_status & 0x2)) {
val = 0;
pci_read_config_dword(dev, 0x20, &val);
tmp = (val & 0xffff) << 16;
val = (val & 0xffff0000) + 0x100000;
val = val - tmp;
i = 0;
while (i < 32) {
if ((1 << i) >= val)
break;
i++;
}
/* config RC1 to EP addr window */
sysRegWrite(ECONET_PCIE1_MAC_BASE + 0x438, tmp | i);
mdelay(1);
/* enable EP to RC1 access */
sysRegWrite(ECONET_PCIE1_MAC_BASE + 0x448, 0x80);
en75xx_pcie_rc1_retrain();
}
#endif
return 0;
}
int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int pci_irq = 0;
if (slot == 0x0)
pci_irq = SURFBOARDINT_PCIE0;
else if (slot == 0x1)
pci_irq = SURFBOARDINT_PCIE1;
return pci_irq;
}
static inline void en75xx_pcie_init_phy(void)
{
u32 __maybe_unused reg_val;
#if defined(CONFIG_ECONET_EN7512)
/* LCDDS_CLK_PH_INV */
reg_val = sysRegRead(RALINK_PCI_PHY0_BASE + 0x04a0);
reg_val |= (0x1 << 5);
sysRegWrite(RALINK_PCI_PHY0_BASE + 0x04a0, reg_val);
mdelay(1);
/* Patch TxDetRx Timing for 7512 E1, from DR 20160421, Biker_20160516 */
reg_val = sysRegRead(RALINK_PCI_PHY1_BASE + 0x0a28);
reg_val &= ~(0x1ff << 9);
reg_val |= (0x010 << 9);
sysRegWrite(RALINK_PCI_PHY1_BASE + 0x0a28, reg_val);
mdelay(1);
reg_val = sysRegRead(RALINK_PCI_PHY1_BASE + 0x0a2c);
reg_val &= ~0x1ff;
reg_val |= 0x010;
sysRegWrite(RALINK_PCI_PHY1_BASE + 0x0a2c, reg_val);
mdelay(1);
#ifdef CONFIG_PCIE_PORT1
if (isEN7512)
#endif
{
/* disable gen2 port PHY for COC test */
reg_val = sysRegRead(RALINK_PCI_PHY1_BASE + 0x030c);
reg_val |= (1u << 31); /* IP_SW_RESET */
sysRegWrite(RALINK_PCI_PHY1_BASE + 0x030c, reg_val);
mdelay(1);
}
#elif defined(CONFIG_ECONET_EN7528)
/* LCDDS_CLK_PH_INV */
reg_val = sysRegRead(RALINK_PCI_PHY0_BASE + 0x04a0);
reg_val |= (0x1 << 5);
sysRegWrite(RALINK_PCI_PHY0_BASE + 0x04a0, reg_val);
mdelay(1);
/* combo phy Rx R FT mean value too high, tune target R -5 Ohm */
reg_val = sysRegRead(RALINK_PCI_PHY1_BASE + 0x0b2c);
reg_val &= ~(0x3 << 12);
reg_val |= (0x1 << 12);
sysRegWrite(RALINK_PCI_PHY1_BASE + 0x0b2c, reg_val);
mdelay(1);
#endif
}
int __init init_en75xx_pci(void)
{
u32 i, reg_val;
/* reset PCIe devices by pulse low */
reg_val = sysRegRead(ECONET_PCIEC_REG);
reg_val &= ~((1 << 29) | (1 << 26));
sysRegWrite(ECONET_PCIEC_REG, reg_val);
mdelay(1);
en75xx_pcie_init_phy();
/* enabled PCIe RC1 clock */
reg_val = sysRegRead(ECONET_PCIEC_REG);
reg_val |= (1 << 22);
sysRegWrite(ECONET_PCIEC_REG, reg_val);
mdelay(1);
/* de-assert PCIe RC0/RC1/HB reset signal */
reg_val = sysRegRead(ECONET_RSTCTRL_REG);
reg_val &= ~(RALINK_PCIE0_RST | RALINK_PCIE1_RST | RALINK_PCIEHB_RST);
sysRegWrite(ECONET_RSTCTRL_REG, reg_val);
mdelay(1);
/* assert PCIe RC0/RC1/HB reset signal */
reg_val |= (RALINK_PCIE0_RST | RALINK_PCIE1_RST | RALINK_PCIEHB_RST);
sysRegWrite(ECONET_RSTCTRL_REG, reg_val);
mdelay(100);
/* de-assert PCIe RC0/RC1/HB reset signal */
reg_val &= ~(RALINK_PCIE0_RST | RALINK_PCIE1_RST | RALINK_PCIEHB_RST);
sysRegWrite(ECONET_RSTCTRL_REG, reg_val);
mdelay(10);
/* release PCIe devices reset */
reg_val = sysRegRead(ECONET_PCIEC_REG);
reg_val |= ((1 << 29) | (1 << 26));
sysRegWrite(ECONET_PCIEC_REG, reg_val);
/* wait before detect card in slots */
mdelay(250);
for (i = 0; i < 20; i++) {
pcie_link_status = (sysRegRead(ECONET_PCIE_LINKUP) >> 1) & 0x3;
if ((pcie_link_status & 0x1)
#ifdef CONFIG_PCIE_PORT1
&& (pcie_link_status & 0x2)
#endif
)
break;
msleep(20);
}
if ((pcie_link_status & 0x1) == 0) {
printk(KERN_WARNING "PCIe RC%d no card, disable PHY\n", 0);
/* disable gen1 port PHY */
reg_val = sysRegRead(RALINK_PCI_PHY0_BASE);
reg_val &= ~(1 << 5); /* rg_pe1_phy_en */
reg_val |= (1 << 4); /* rg_pe1_frc_phy_en */
sysRegWrite(RALINK_PCI_PHY0_BASE, reg_val);
mdelay(1);
}
#ifdef CONFIG_PCIE_PORT1
if ((pcie_link_status & 0x2) == 0) {
printk(KERN_WARNING "PCIe RC%d no card, disable PHY\n", 1);
/* disable gen2 port PHY */
reg_val = sysRegRead(RALINK_PCI_PHY1_BASE + 0x030c);
reg_val |= (1u << 31); /* IP_SW_RESET */
sysRegWrite(RALINK_PCI_PHY1_BASE + 0x030c, reg_val);
mdelay(1);
}
#endif
if (!pcie_link_status)
return 0;
/* change class to pci-pci class */
sysRegWrite(ECONET_PCIE0_MAC_BASE + 0x104, 0x06040001);
sysRegWrite(ECONET_PCIE1_MAC_BASE + 0x104, 0x06040001);
mdelay(1);
/* set host mode */
sysRegWrite(ECONET_PCIE0_MAC_BASE, 0x00804201);
sysRegWrite(ECONET_PCIE1_MAC_BASE, 0x00804201);
mdelay(1);
/* disable MSI interrupt */
reg_val = sysRegRead(ECONET_PCIE0_MAC_BASE + 0x11c);
reg_val &= ~(1 << 5);
sysRegWrite(ECONET_PCIE0_MAC_BASE + 0x11c, reg_val);
reg_val = sysRegRead(ECONET_PCIE1_MAC_BASE + 0x11c);
reg_val &= ~(1 << 5);
sysRegWrite(ECONET_PCIE1_MAC_BASE + 0x11c, reg_val);
/* setup PCIe port0 MAC */
if (pcie_link_status & 0x1) {
/* enable interrupt */
reg_val = sysRegRead(ECONET_PCIE0_MAC_BASE + 0x420);
reg_val &= ~(1 << 16);
sysRegWrite(ECONET_PCIE0_MAC_BASE + 0x420, reg_val);
/* enable error interrupt */
reg_val = sysRegRead(ECONET_PCIE_ERR_IRQ_EN);
reg_val |= 0x03;
sysRegWrite(ECONET_PCIE_ERR_IRQ_EN, reg_val);
}
#ifdef CONFIG_PCIE_PORT1
/* setup PCIe port1 MAC */
if (pcie_link_status & 0x2) {
/* enable interrupt */
reg_val = sysRegRead(ECONET_PCIE1_MAC_BASE + 0x420);
reg_val &= ~(1 << 16);
sysRegWrite(ECONET_PCIE1_MAC_BASE + 0x420, reg_val);
/* enable error interrupt */
reg_val = sysRegRead(ECONET_PCIE_ERR_IRQ_EN);
reg_val |= (0x03 << 2);
sysRegWrite(ECONET_PCIE_ERR_IRQ_EN, reg_val);
}
#endif
en75xx_pci_controller.io_map_base = mips_io_port_base;
register_pci_controller(&en75xx_pci_controller);
return 0;
}
arch_initcall(init_en75xx_pci);
#endif /* CONFIG_PCI */