diff options
Diffstat (limited to 'target/linux/lantiq/patches-5.15')
38 files changed, 12285 insertions, 0 deletions
diff --git a/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch b/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch new file mode 100644 index 0000000000..99bed24cfd --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch @@ -0,0 +1,5507 @@ +From 6f933347d0b4ed02d9534f5fa07f7b99f13eeaa1 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 7 Aug 2014 18:12:28 +0200 +Subject: [PATCH 01/36] MIPS: lantiq: add pcie driver + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/lantiq/Kconfig | 10 + + arch/mips/lantiq/xway/sysctrl.c | 2 + + arch/mips/pci/Makefile | 2 + + arch/mips/pci/fixup-lantiq-pcie.c | 82 +++ + arch/mips/pci/fixup-lantiq.c | 5 +- + arch/mips/pci/ifxmips_pci_common.h | 57 ++ + arch/mips/pci/ifxmips_pcie.c | 1099 ++++++++++++++++++++++++++++++ + arch/mips/pci/ifxmips_pcie.h | 135 ++++ + arch/mips/pci/ifxmips_pcie_ar10.h | 290 ++++++++ + arch/mips/pci/ifxmips_pcie_msi.c | 392 +++++++++++ + arch/mips/pci/ifxmips_pcie_phy.c | 478 +++++++++++++ + arch/mips/pci/ifxmips_pcie_pm.c | 176 +++++ + arch/mips/pci/ifxmips_pcie_pm.h | 36 + + arch/mips/pci/ifxmips_pcie_reg.h | 1001 +++++++++++++++++++++++++++ + arch/mips/pci/ifxmips_pcie_vr9.h | 271 ++++++++ + arch/mips/pci/pci.c | 25 + + arch/mips/pci/pcie-lantiq.h | 1305 ++++++++++++++++++++++++++++++++++++ + drivers/pci/pcie/aer/Kconfig | 2 +- + include/linux/pci.h | 2 + + include/linux/pci_ids.h | 6 + + 20 files changed, 5374 insertions(+), 2 deletions(-) + create mode 100644 arch/mips/pci/fixup-lantiq-pcie.c + create mode 100644 arch/mips/pci/ifxmips_pci_common.h + create mode 100644 arch/mips/pci/ifxmips_pcie.c + create mode 100644 arch/mips/pci/ifxmips_pcie.h + create mode 100644 arch/mips/pci/ifxmips_pcie_ar10.h + create mode 100644 arch/mips/pci/ifxmips_pcie_msi.c + create mode 100644 arch/mips/pci/ifxmips_pcie_phy.c + create mode 100644 arch/mips/pci/ifxmips_pcie_pm.c + create mode 100644 arch/mips/pci/ifxmips_pcie_pm.h + create mode 100644 arch/mips/pci/ifxmips_pcie_reg.h + create mode 100644 arch/mips/pci/ifxmips_pcie_vr9.h + create mode 100644 arch/mips/pci/pcie-lantiq.h + +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -20,6 +20,7 @@ config SOC_XWAY + bool "XWAY" + select SOC_TYPE_XWAY + select HAVE_PCI ++ select ARCH_SUPPORTS_MSI + select MFD_SYSCON + select MFD_CORE + +@@ -52,4 +53,13 @@ config PCI_LANTIQ + bool "PCI Support" + depends on SOC_XWAY && PCI + ++config PCIE_LANTIQ ++ bool "PCIE Support" ++ depends on SOC_XWAY && PCI ++ ++config PCIE_LANTIQ_MSI ++ bool ++ depends on PCIE_LANTIQ && PCI_MSI ++ default y ++ + endif +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -43,6 +43,8 @@ obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o + obj-$(CONFIG_SOC_MT7620) += pci-mt7620.o + obj-$(CONFIG_SOC_RT288X) += pci-rt2880.o + obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o ++obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie_phy.o ifxmips_pcie.o fixup-lantiq-pcie.o ++obj-$(CONFIG_PCIE_LANTIQ_MSI) += pcie-lantiq-msi.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +--- /dev/null ++++ b/arch/mips/pci/fixup-lantiq-pcie.c +@@ -0,0 +1,74 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_fixup_pcie.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \file ifxmips_fixup_pcie.c ++ \ingroup IFX_PCIE ++ \brief PCIe Fixup functions source file ++*/ ++#include <linux/pci.h> ++#include <linux/pci_regs.h> ++#include <linux/pci_ids.h> ++ ++#include <lantiq_soc.h> ++ ++#include "pcie-lantiq.h" ++ ++static void ++ifx_pcie_fixup_resource(struct pci_dev *dev) ++{ ++ u32 reg; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev)); ++ ++ printk("%s: fixup host controller %s (%04x:%04x)\n", ++ __func__, pci_name(dev), dev->vendor, dev->device); ++ ++ /* Setup COMMAND register */ ++ reg = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER /* | ++ PCI_COMMAND_INTX_DISABLE */| PCI_COMMAND_SERR; ++ pci_write_config_word(dev, PCI_COMMAND, reg); ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev)); ++} ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE, ifx_pcie_fixup_resource); ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_VENDOR_ID_LANTIQ, ifx_pcie_fixup_resource); ++ ++static void ++ifx_pcie_rc_class_early_fixup(struct pci_dev *dev) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev)); ++ ++ if (dev->devfn == PCI_DEVFN(0, 0) && ++ (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { ++ ++ dev->class = (PCI_CLASS_BRIDGE_PCI << 8) | (dev->class & 0xff); ++ ++ printk(KERN_INFO "%s: fixed pcie host bridge to pci-pci bridge\n", __func__); ++ } ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev)); ++ mdelay(10); ++} ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE, ++ ifx_pcie_rc_class_early_fixup); ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_DEVICE_ID_LANTIQ_PCIE, ++ ifx_pcie_rc_class_early_fixup); +--- a/arch/mips/pci/fixup-lantiq.c ++++ b/arch/mips/pci/fixup-lantiq.c +@@ -6,12 +6,19 @@ + + #include <linux/of_irq.h> + #include <linux/of_pci.h> ++#include <linux/pci.h> ++#include "ifxmips_pci_common.h" + + int (*ltq_pci_plat_arch_init)(struct pci_dev *dev) = NULL; + int (*ltq_pci_plat_dev_init)(struct pci_dev *dev) = NULL; + + int pcibios_plat_dev_init(struct pci_dev *dev) + { ++#ifdef CONFIG_PCIE_LANTIQ ++ if (pci_find_capability(dev, PCI_CAP_ID_EXP)) ++ ifx_pcie_bios_plat_dev_init(dev); ++#endif ++ + if (ltq_pci_plat_arch_init) + return ltq_pci_plat_arch_init(dev); + +@@ -23,5 +30,10 @@ int pcibios_plat_dev_init(struct pci_dev + + int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + { ++#ifdef CONFIG_PCIE_LANTIQ ++ if (pci_find_capability(dev, PCI_CAP_ID_EXP)) ++ return ifx_pcie_bios_map_irq(dev, slot, pin); ++#endif ++ + return of_irq_parse_and_map_pci(dev, slot, pin); + } +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pci_common.h +@@ -0,0 +1,53 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pci_common.h ++** PROJECT : IFX UEIP ++** MODULES : PCI subsystem ++** ++** DATE : 30 June 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 30 June,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++ ++#ifndef IFXMIPS_PCI_COMMON_H ++#define IFXMIPS_PCI_COMMON_H ++#include <linux/version.h> ++/*! ++ \defgroup IFX_PCI_COM IFX PCI/PCIe common parts for OS integration ++ \brief PCI/PCIe common parts ++*/ ++ ++/*! ++ \defgroup IFX_PCI_COM_OS OS APIs ++ \ingroup IFX_PCI_COM ++ \brief PCI/PCIe bus driver OS interface functions ++*/ ++/*! ++ \file ifxmips_pci_common.h ++ \ingroup IFX_PCI_COM ++ \brief PCI/PCIe bus driver common OS header file ++*/ ++#define IFX_PCI_CONST const ++#ifdef CONFIG_IFX_PCI ++extern int ifx_pci_bios_map_irq(IFX_PCI_CONST struct pci_dev *dev, u8 slot, u8 pin); ++extern int ifx_pci_bios_plat_dev_init(struct pci_dev *dev); ++#endif /* COFNIG_IFX_PCI */ ++ ++#ifdef CONFIG_PCIE_LANTIQ ++extern int ifx_pcie_bios_map_irq(IFX_PCI_CONST struct pci_dev *dev, u8 slot, u8 pin); ++extern int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev); ++#endif ++ ++#endif /* IFXMIPS_PCI_COMMON_H */ ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie.c +@@ -0,0 +1,1092 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2009 Lei Chuanhua <chuanhua.lei@infineon.com> ++ * Copyright (C) 2013 John Crispin <blogic@openwrt.org> ++ */ ++ ++#include <linux/types.h> ++#include <linux/pci.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/mm.h> ++#include <asm/paccess.h> ++#include <linux/pci.h> ++#include <linux/pci_regs.h> ++#include <linux/module.h> ++ ++#include "ifxmips_pcie.h" ++#include "ifxmips_pcie_reg.h" ++ ++/* Enable 32bit io due to its mem mapped io nature */ ++#define IFX_PCIE_ERROR_INT ++#define IFX_PCIE_IO_32BIT ++ ++#define IFX_PCIE_IR (INT_NUM_IM4_IRL0 + 25) ++#define IFX_PCIE_INTA (INT_NUM_IM4_IRL0 + 8) ++#define IFX_PCIE_INTB (INT_NUM_IM4_IRL0 + 9) ++#define IFX_PCIE_INTC (INT_NUM_IM4_IRL0 + 10) ++#define IFX_PCIE_INTD (INT_NUM_IM4_IRL0 + 11) ++#define MS(_v, _f) (((_v) & (_f)) >> _f##_S) ++#define SM(_v, _f) (((_v) << _f##_S) & (_f)) ++#define IFX_REG_SET_BIT(_f, _r) \ ++ IFX_REG_W32((IFX_REG_R32((_r)) &~ (_f)) | (_f), (_r)) ++ ++#define IFX_PCIE_LTSSM_ENABLE_TIMEOUT 10 ++ ++static DEFINE_SPINLOCK(ifx_pcie_lock); ++ ++u32 g_pcie_debug_flag = PCIE_MSG_ANY & (~PCIE_MSG_CFG); ++ ++static ifx_pcie_irq_t pcie_irqs[IFX_PCIE_CORE_NR] = { ++ { ++ .ir_irq = { ++ .irq = IFX_PCIE_IR, ++ .name = "ifx_pcie_rc0", ++ }, ++ ++ .legacy_irq = { ++ { ++ .irq_bit = PCIE_IRN_INTA, ++ .irq = IFX_PCIE_INTA, ++ }, ++ { ++ .irq_bit = PCIE_IRN_INTB, ++ .irq = IFX_PCIE_INTB, ++ }, ++ { ++ .irq_bit = PCIE_IRN_INTC, ++ .irq = IFX_PCIE_INTC, ++ }, ++ { ++ .irq_bit = PCIE_IRN_INTD, ++ .irq = IFX_PCIE_INTD, ++ }, ++ }, ++ }, ++ ++}; ++ ++void ifx_pcie_debug(const char *fmt, ...) ++{ ++ static char buf[256] = {0}; /* XXX */ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vsnprintf(buf, sizeof(buf), fmt, ap); ++ va_end(ap); ++ ++ printk("%s", buf); ++} ++ ++ ++static inline int pcie_ltssm_enable(int pcie_port) ++{ ++ int i; ++ ++ /* Enable LTSSM */ ++ IFX_REG_W32(PCIE_RC_CCR_LTSSM_ENABLE, PCIE_RC_CCR(pcie_port)); ++ ++ /* Wait for the link to come up */ ++ for (i = 0; i < IFX_PCIE_LTSSM_ENABLE_TIMEOUT; i++) { ++ if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_RETRAIN_PENDING)) ++ return 0; ++ udelay(10); ++ } ++ ++ printk("%s link timeout!!!!!\n", __func__); ++ return -1; ++} ++ ++static inline void pcie_status_register_clear(int pcie_port) ++{ ++ IFX_REG_W32(0, PCIE_RC_DR(pcie_port)); ++ IFX_REG_W32(0, PCIE_PCICMDSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_DCTLSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_LCTLSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_SLCTLSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_RSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_UES_R(pcie_port)); ++ IFX_REG_W32(0, PCIE_UEMR(pcie_port)); ++ IFX_REG_W32(0, PCIE_UESR(pcie_port)); ++ IFX_REG_W32(0, PCIE_CESR(pcie_port)); ++ IFX_REG_W32(0, PCIE_CEMR(pcie_port)); ++ IFX_REG_W32(0, PCIE_RESR(pcie_port)); ++ IFX_REG_W32(0, PCIE_PVCCRSR(pcie_port)); ++ IFX_REG_W32(0, PCIE_VC0_RSR0(pcie_port)); ++ IFX_REG_W32(0, PCIE_TPFCS(pcie_port)); ++ IFX_REG_W32(0, PCIE_TNPFCS(pcie_port)); ++ IFX_REG_W32(0, PCIE_TCFCS(pcie_port)); ++ IFX_REG_W32(0, PCIE_QSR(pcie_port)); ++ IFX_REG_W32(0, PCIE_IOBLSECS(pcie_port)); ++} ++ ++static inline int ifx_pcie_link_up(int pcie_port) ++{ ++ return (IFX_REG_R32(PCIE_PHY_SR(pcie_port)) & PCIE_PHY_SR_PHY_LINK_UP) ? 1 : 0; ++} ++ ++ ++static inline void pcie_mem_io_setup(int pcie_port) ++{ ++ u32 reg; ++ /* ++ * BAR[0:1] readonly register ++ * RC contains only minimal BARs for packets mapped to this device ++ * Mem/IO filters defines a range of memory occupied by memory mapped IO devices that ++ * reside on the downstream side fo the bridge. ++ */ ++ reg = SM((PCIE_MEM_PHY_PORT_TO_END(pcie_port) >> 20), PCIE_MBML_MEM_LIMIT_ADDR) ++ | SM((PCIE_MEM_PHY_PORT_TO_BASE(pcie_port) >> 20), PCIE_MBML_MEM_BASE_ADDR); ++ ++ IFX_REG_W32(reg, PCIE_MBML(pcie_port)); ++ ++ ++#ifdef IFX_PCIE_PREFETCH_MEM_64BIT ++ reg = SM((PCIE_MEM_PHY_PORT_TO_END(pcie_port) >> 20), PCIE_PMBL_END_ADDR) ++ | SM((PCIE_MEM_PHY_PORT_TO_BASE(pcie_port) >> 20), PCIE_PMBL_UPPER_12BIT) ++ | PCIE_PMBL_64BIT_ADDR; ++ IFX_REG_W32(reg, PCIE_PMBL(pcie_port)); ++ ++ /* Must configure upper 32bit */ ++ IFX_REG_W32(0, PCIE_PMBU32(pcie_port)); ++ IFX_REG_W32(0, PCIE_PMLU32(pcie_port)); ++#else ++ /* PCIe_PBML, same as MBML */ ++ IFX_REG_W32(IFX_REG_R32(PCIE_MBML(pcie_port)), PCIE_PMBL(pcie_port)); ++#endif ++ ++ /* IO Address Range */ ++ reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 12), PCIE_IOBLSECS_IO_LIMIT_ADDR) ++ | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 12), PCIE_IOBLSECS_IO_BASE_ADDR); ++#ifdef IFX_PCIE_IO_32BIT ++ reg |= PCIE_IOBLSECS_32BIT_IO_ADDR; ++#endif /* IFX_PCIE_IO_32BIT */ ++ IFX_REG_W32(reg, PCIE_IOBLSECS(pcie_port)); ++ ++#ifdef IFX_PCIE_IO_32BIT ++ reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 16), PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT) ++ | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 16), PCIE_IO_BANDL_UPPER_16BIT_IO_BASE); ++ IFX_REG_W32(reg, PCIE_IO_BANDL(pcie_port)); ++ ++#endif /* IFX_PCIE_IO_32BIT */ ++} ++ ++static inline void ++pcie_device_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Device capability register, set up Maximum payload size */ ++ reg = IFX_REG_R32(PCIE_DCAP(pcie_port)); ++ reg |= PCIE_DCAP_ROLE_BASE_ERR_REPORT; ++ reg |= SM(PCIE_MAX_PAYLOAD_128, PCIE_DCAP_MAX_PAYLOAD_SIZE); ++ ++ /* Only available for EP */ ++ reg &= ~(PCIE_DCAP_EP_L0S_LATENCY | PCIE_DCAP_EP_L1_LATENCY); ++ IFX_REG_W32(reg, PCIE_DCAP(pcie_port)); ++ ++ /* Device control and status register */ ++ /* Set Maximum Read Request size for the device as a Requestor */ ++ reg = IFX_REG_R32(PCIE_DCTLSTS(pcie_port)); ++ ++ /* ++ * Request size can be larger than the MPS used, but the completions returned ++ * for the read will be bounded by the MPS size. ++ * In our system, Max request size depends on AHB burst size. It is 64 bytes. ++ * but we set it as 128 as minimum one. ++ */ ++ reg |= SM(PCIE_MAX_PAYLOAD_128, PCIE_DCTLSTS_MAX_READ_SIZE) ++ | SM(PCIE_MAX_PAYLOAD_128, PCIE_DCTLSTS_MAX_PAYLOAD_SIZE); ++ ++ /* Enable relaxed ordering, no snoop, and all kinds of errors */ ++ reg |= PCIE_DCTLSTS_RELAXED_ORDERING_EN | PCIE_DCTLSTS_ERR_EN | PCIE_DCTLSTS_NO_SNOOP_EN; ++ ++ IFX_REG_W32(reg, PCIE_DCTLSTS(pcie_port)); ++} ++ ++static inline void ++pcie_link_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ /* ++ * XXX, Link capability register, bit 18 for EP CLKREQ# dynamic clock management for L1, L2/3 CPM ++ * L0s is reported during link training via TS1 order set by N_FTS ++ */ ++ reg = IFX_REG_R32(PCIE_LCAP(pcie_port)); ++ reg &= ~PCIE_LCAP_L0S_EIXT_LATENCY; ++ reg |= SM(3, PCIE_LCAP_L0S_EIXT_LATENCY); ++ IFX_REG_W32(reg, PCIE_LCAP(pcie_port)); ++ ++ /* Link control and status register */ ++ reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port)); ++ ++ /* Link Enable, ASPM enabled */ ++ reg &= ~PCIE_LCTLSTS_LINK_DISABLE; ++ ++#ifdef CONFIG_PCIEASPM ++ /* ++ * We use the same physical reference clock that the platform provides on the connector ++ * It paved the way for ASPM to calculate the new exit Latency ++ */ ++ reg |= PCIE_LCTLSTS_SLOT_CLK_CFG; ++ reg |= PCIE_LCTLSTS_COM_CLK_CFG; ++ /* ++ * We should disable ASPM by default except that we have dedicated power management support ++ * Enable ASPM will cause the system hangup/instability, performance degration ++ */ ++ reg |= PCIE_LCTLSTS_ASPM_ENABLE; ++#else ++ reg &= ~PCIE_LCTLSTS_ASPM_ENABLE; ++#endif /* CONFIG_PCIEASPM */ ++ ++ /* ++ * The maximum size of any completion with data packet is bounded by the MPS setting ++ * in device control register ++ */ ++ ++ /* RCB may cause multiple split transactions, two options available, we use 64 byte RCB */ ++ reg &= ~ PCIE_LCTLSTS_RCB128; ++ ++ IFX_REG_W32(reg, PCIE_LCTLSTS(pcie_port)); ++} ++ ++static inline void pcie_error_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ /* ++ * Forward ERR_COR, ERR_NONFATAL, ERR_FATAL to the backbone ++ * Poisoned write TLPs and completions indicating poisoned TLPs will set the PCIe_PCICMDSTS.MDPE ++ */ ++ reg = IFX_REG_R32(PCIE_INTRBCTRL(pcie_port)); ++ reg |= PCIE_INTRBCTRL_SERR_ENABLE | PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE; ++ ++ IFX_REG_W32(reg, PCIE_INTRBCTRL(pcie_port)); ++ ++ /* Uncorrectable Error Mask Register, Unmask <enable> all bits in PCIE_UESR */ ++ reg = IFX_REG_R32(PCIE_UEMR(pcie_port)); ++ reg &= ~PCIE_ALL_UNCORRECTABLE_ERR; ++ IFX_REG_W32(reg, PCIE_UEMR(pcie_port)); ++ ++ /* Uncorrectable Error Severity Register, ALL errors are FATAL */ ++ IFX_REG_W32(PCIE_ALL_UNCORRECTABLE_ERR, PCIE_UESR(pcie_port)); ++ ++ /* Correctable Error Mask Register, unmask <enable> all bits */ ++ reg = IFX_REG_R32(PCIE_CEMR(pcie_port)); ++ reg &= ~PCIE_CORRECTABLE_ERR; ++ IFX_REG_W32(reg, PCIE_CEMR(pcie_port)); ++ ++ /* Advanced Error Capabilities and Control Registr */ ++ reg = IFX_REG_R32(PCIE_AECCR(pcie_port)); ++ reg |= PCIE_AECCR_ECRC_CHECK_EN | PCIE_AECCR_ECRC_GEN_EN; ++ IFX_REG_W32(reg, PCIE_AECCR(pcie_port)); ++ ++ /* Root Error Command Register, Report all types of errors */ ++ reg = IFX_REG_R32(PCIE_RECR(pcie_port)); ++ reg |= PCIE_RECR_ERR_REPORT_EN; ++ IFX_REG_W32(reg, PCIE_RECR(pcie_port)); ++ ++ /* Clear the Root status register */ ++ reg = IFX_REG_R32(PCIE_RESR(pcie_port)); ++ IFX_REG_W32(reg, PCIE_RESR(pcie_port)); ++} ++ ++static inline void pcie_port_logic_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ /* FTS number, default 12, increase to 63, may increase time from/to L0s to L0 */ ++ reg = IFX_REG_R32(PCIE_AFR(pcie_port)); ++ reg &= ~(PCIE_AFR_FTS_NUM | PCIE_AFR_COM_FTS_NUM); ++ reg |= SM(PCIE_AFR_FTS_NUM_DEFAULT, PCIE_AFR_FTS_NUM) ++ | SM(PCIE_AFR_FTS_NUM_DEFAULT, PCIE_AFR_COM_FTS_NUM); ++ /* L0s and L1 entry latency */ ++ reg &= ~(PCIE_AFR_L0S_ENTRY_LATENCY | PCIE_AFR_L1_ENTRY_LATENCY); ++ reg |= SM(PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT, PCIE_AFR_L0S_ENTRY_LATENCY) ++ | SM(PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT, PCIE_AFR_L1_ENTRY_LATENCY); ++ IFX_REG_W32(reg, PCIE_AFR(pcie_port)); ++ ++ ++ /* Port Link Control Register */ ++ reg = IFX_REG_R32(PCIE_PLCR(pcie_port)); ++ reg |= PCIE_PLCR_DLL_LINK_EN; /* Enable the DLL link */ ++ IFX_REG_W32(reg, PCIE_PLCR(pcie_port)); ++ ++ /* Lane Skew Register */ ++ reg = IFX_REG_R32(PCIE_LSR(pcie_port)); ++ /* Enable ACK/NACK and FC */ ++ reg &= ~(PCIE_LSR_ACKNAK_DISABLE | PCIE_LSR_FC_DISABLE); ++ IFX_REG_W32(reg, PCIE_LSR(pcie_port)); ++ ++ /* Symbol Timer Register and Filter Mask Register 1 */ ++ reg = IFX_REG_R32(PCIE_STRFMR(pcie_port)); ++ ++ /* Default SKP interval is very accurate already, 5us */ ++ /* Enable IO/CFG transaction */ ++ reg |= PCIE_STRFMR_RX_CFG_TRANS_ENABLE | PCIE_STRFMR_RX_IO_TRANS_ENABLE; ++ /* Disable FC WDT */ ++ reg &= ~PCIE_STRFMR_FC_WDT_DISABLE; ++ IFX_REG_W32(reg, PCIE_STRFMR(pcie_port)); ++ ++ /* Filter Masker Register 2 */ ++ reg = IFX_REG_R32(PCIE_FMR2(pcie_port)); ++ reg |= PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 | PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1; ++ IFX_REG_W32(reg, PCIE_FMR2(pcie_port)); ++ ++ /* VC0 Completion Receive Queue Control Register */ ++ reg = IFX_REG_R32(PCIE_VC0_CRQCR(pcie_port)); ++ reg &= ~PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE; ++ reg |= SM(PCIE_VC0_TLP_QUEUE_MODE_BYPASS, PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE); ++ IFX_REG_W32(reg, PCIE_VC0_CRQCR(pcie_port)); ++} ++ ++static inline void pcie_rc_cfg_reg_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Disable LTSSM */ ++ IFX_REG_W32(0, PCIE_RC_CCR(pcie_port)); /* Disable LTSSM */ ++ ++ pcie_mem_io_setup(pcie_port); ++ ++ /* XXX, MSI stuff should only apply to EP */ ++ /* MSI Capability: Only enable 32-bit addresses */ ++ reg = IFX_REG_R32(PCIE_MCAPR(pcie_port)); ++ reg &= ~PCIE_MCAPR_ADDR64_CAP; ++ ++ reg |= PCIE_MCAPR_MSI_ENABLE; ++ ++ /* Disable multiple message */ ++ reg &= ~(PCIE_MCAPR_MULTI_MSG_CAP | PCIE_MCAPR_MULTI_MSG_ENABLE); ++ IFX_REG_W32(reg, PCIE_MCAPR(pcie_port)); ++ ++ ++ /* Enable PME, Soft reset enabled */ ++ reg = IFX_REG_R32(PCIE_PM_CSR(pcie_port)); ++ reg |= PCIE_PM_CSR_PME_ENABLE | PCIE_PM_CSR_SW_RST; ++ IFX_REG_W32(reg, PCIE_PM_CSR(pcie_port)); ++ ++ /* setup the bus */ ++ reg = SM(0, PCIE_BNR_PRIMARY_BUS_NUM) | SM(1, PCIE_PNR_SECONDARY_BUS_NUM) | SM(0xFF, PCIE_PNR_SUB_BUS_NUM); ++ IFX_REG_W32(reg, PCIE_BNR(pcie_port)); ++ ++ ++ pcie_device_setup(pcie_port); ++ pcie_link_setup(pcie_port); ++ pcie_error_setup(pcie_port); ++ ++ /* Root control and capabilities register */ ++ reg = IFX_REG_R32(PCIE_RCTLCAP(pcie_port)); ++ reg |= PCIE_RCTLCAP_SERR_ENABLE | PCIE_RCTLCAP_PME_INT_EN; ++ IFX_REG_W32(reg, PCIE_RCTLCAP(pcie_port)); ++ ++ /* Port VC Capability Register 2 */ ++ reg = IFX_REG_R32(PCIE_PVC2(pcie_port)); ++ reg &= ~PCIE_PVC2_VC_ARB_WRR; ++ reg |= PCIE_PVC2_VC_ARB_16P_FIXED_WRR; ++ IFX_REG_W32(reg, PCIE_PVC2(pcie_port)); ++ ++ /* VC0 Resource Capability Register */ ++ reg = IFX_REG_R32(PCIE_VC0_RC(pcie_port)); ++ reg &= ~PCIE_VC0_RC_REJECT_SNOOP; ++ IFX_REG_W32(reg, PCIE_VC0_RC(pcie_port)); ++ ++ pcie_port_logic_setup(pcie_port); ++} ++ ++static int ifx_pcie_wait_phy_link_up(int pcie_port) ++{ ++#define IFX_PCIE_PHY_LINK_UP_TIMEOUT 1000 /* XXX, tunable */ ++ int i; ++ ++ /* Wait for PHY link is up */ ++ for (i = 0; i < IFX_PCIE_PHY_LINK_UP_TIMEOUT; i++) { ++ if (ifx_pcie_link_up(pcie_port)) { ++ break; ++ } ++ udelay(100); ++ } ++ if (i >= IFX_PCIE_PHY_LINK_UP_TIMEOUT) { ++ printk(KERN_ERR "%s timeout\n", __func__); ++ return -1; ++ } ++ ++ /* Check data link up or not */ ++ if (!(IFX_REG_R32(PCIE_RC_DR(pcie_port)) & PCIE_RC_DR_DLL_UP)) { ++ printk(KERN_ERR "%s DLL link is still down\n", __func__); ++ return -1; ++ } ++ ++ /* Check Data link active or not */ ++ if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_DLL_ACTIVE)) { ++ printk(KERN_ERR "%s DLL is not active\n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static inline int pcie_app_loigc_setup(int pcie_port) ++{ ++ /* supress ahb bus errrors */ ++ IFX_REG_W32(PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS, PCIE_AHB_CTRL(pcie_port)); ++ ++ /* Pull PCIe EP out of reset */ ++ pcie_device_rst_deassert(pcie_port); ++ ++ /* Start LTSSM training between RC and EP */ ++ pcie_ltssm_enable(pcie_port); ++ ++ /* Check PHY status after enabling LTSSM */ ++ if (ifx_pcie_wait_phy_link_up(pcie_port) != 0) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * The numbers below are directly from the PCIe spec table 3-4/5. ++ */ ++static inline void pcie_replay_time_update(int pcie_port) ++{ ++ u32 reg; ++ int nlw; ++ int rtl; ++ ++ reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port)); ++ ++ nlw = MS(reg, PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH); ++ switch (nlw) { ++ case PCIE_MAX_LENGTH_WIDTH_X1: ++ rtl = 1677; ++ break; ++ case PCIE_MAX_LENGTH_WIDTH_X2: ++ rtl = 867; ++ break; ++ case PCIE_MAX_LENGTH_WIDTH_X4: ++ rtl = 462; ++ break; ++ case PCIE_MAX_LENGTH_WIDTH_X8: ++ rtl = 258; ++ break; ++ default: ++ rtl = 1677; ++ break; ++ } ++ reg = IFX_REG_R32(PCIE_ALTRT(pcie_port)); ++ reg &= ~PCIE_ALTRT_REPLAY_TIME_LIMIT; ++ reg |= SM(rtl, PCIE_ALTRT_REPLAY_TIME_LIMIT); ++ IFX_REG_W32(reg, PCIE_ALTRT(pcie_port)); ++} ++ ++/* ++ * Table 359 Enhanced Configuration Address Mapping1) ++ * 1) This table is defined in Table 7-1, page 341, PCI Express Base Specification v1.1 ++ * Memory Address PCI Express Configuration Space ++ * A[(20+n-1):20] Bus Number 1 < n < 8 ++ * A[19:15] Device Number ++ * A[14:12] Function Number ++ * A[11:8] Extended Register Number ++ * A[7:2] Register Number ++ * A[1:0] Along with size of the access, used to generate Byte Enables ++ * For VR9, only the address bits [22:0] are mapped to the configuration space: ++ * . Address bits [22:20] select the target bus (1-of-8)1) ++ * . Address bits [19:15] select the target device (1-of-32) on the bus ++ * . Address bits [14:12] select the target function (1-of-8) within the device. ++ * . Address bits [11:2] selects the target dword (1-of-1024) within the selected function.s configuration space ++ * . Address bits [1:0] define the start byte location within the selected dword. ++ */ ++static inline u32 pcie_bus_addr(u8 bus_num, u16 devfn, int where) ++{ ++ u32 addr; ++ u8 bus; ++ ++ if (!bus_num) { ++ /* type 0 */ ++ addr = ((PCI_SLOT(devfn) & 0x1F) << 15) | ((PCI_FUNC(devfn) & 0x7) << 12) | ((where & 0xFFF)& ~3); ++ } else { ++ bus = bus_num; ++ /* type 1, only support 8 buses */ ++ addr = ((bus & 0x7) << 20) | ((PCI_SLOT(devfn) & 0x1F) << 15) | ++ ((PCI_FUNC(devfn) & 0x7) << 12) | ((where & 0xFFF) & ~3); ++ } ++ return addr; ++} ++ ++static int pcie_valid_config(int pcie_port, int bus, int dev) ++{ ++ /* RC itself */ ++ if ((bus == 0) && (dev == 0)) { ++ return 1; ++ } ++ ++ /* No physical link */ ++ if (!ifx_pcie_link_up(pcie_port)) { ++ return 0; ++ } ++ ++ /* Bus zero only has RC itself ++ * XXX, check if EP will be integrated ++ */ ++ if ((bus == 0) && (dev != 0)) { ++ return 0; ++ } ++ ++ /* Maximum 8 buses supported for VRX */ ++ if (bus > 9) { ++ return 0; ++ } ++ ++ /* ++ * PCIe is PtP link, one bus only supports only one device ++ * except bus zero and PCIe switch which is virtual bus device ++ * The following two conditions really depends on the system design ++ * and attached the device. ++ * XXX, how about more new switch ++ */ ++ if ((bus == 1) && (dev != 0)) { ++ return 0; ++ } ++ ++ if ((bus >= 3) && (dev != 0)) { ++ return 0; ++ } ++ return 1; ++} ++ ++static inline u32 ifx_pcie_cfg_rd(int pcie_port, u32 reg) ++{ ++ return IFX_REG_R32((volatile u32 *)(PCIE_CFG_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++static inline void ifx_pcie_cfg_wr(int pcie_port, unsigned int reg, u32 val) ++{ ++ IFX_REG_W32( val, (volatile u32 *)(PCIE_CFG_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++static inline u32 ifx_pcie_rc_cfg_rd(int pcie_port, u32 reg) ++{ ++ return IFX_REG_R32((volatile u32 *)(PCIE_RC_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++static inline void ifx_pcie_rc_cfg_wr(int pcie_port, unsigned int reg, u32 val) ++{ ++ IFX_REG_W32(val, (volatile u32 *)(PCIE_RC_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++u32 ifx_pcie_bus_enum_read_hack(int where, u32 value) ++{ ++ u32 tvalue = value; ++ ++ if (where == PCI_PRIMARY_BUS) { ++ u8 primary, secondary, subordinate; ++ ++ primary = tvalue & 0xFF; ++ secondary = (tvalue >> 8) & 0xFF; ++ subordinate = (tvalue >> 16) & 0xFF; ++ primary += pcibios_1st_host_bus_nr(); ++ secondary += pcibios_1st_host_bus_nr(); ++ subordinate += pcibios_1st_host_bus_nr(); ++ tvalue = (tvalue & 0xFF000000) | (u32)primary | (u32)(secondary << 8) | (u32)(subordinate << 16); ++ } ++ return tvalue; ++} ++ ++u32 ifx_pcie_bus_enum_write_hack(int where, u32 value) ++{ ++ u32 tvalue = value; ++ ++ if (where == PCI_PRIMARY_BUS) { ++ u8 primary, secondary, subordinate; ++ ++ primary = tvalue & 0xFF; ++ secondary = (tvalue >> 8) & 0xFF; ++ subordinate = (tvalue >> 16) & 0xFF; ++ if (primary > 0 && primary != 0xFF) { ++ primary -= pcibios_1st_host_bus_nr(); ++ } ++ ++ if (secondary > 0 && secondary != 0xFF) { ++ secondary -= pcibios_1st_host_bus_nr(); ++ } ++ if (subordinate > 0 && subordinate != 0xFF) { ++ subordinate -= pcibios_1st_host_bus_nr(); ++ } ++ tvalue = (tvalue & 0xFF000000) | (u32)primary | (u32)(secondary << 8) | (u32)(subordinate << 16); ++ } ++ else if (where == PCI_SUBORDINATE_BUS) { ++ u8 subordinate = tvalue & 0xFF; ++ ++ subordinate = subordinate > 0 ? subordinate - pcibios_1st_host_bus_nr() : 0; ++ tvalue = subordinate; ++ } ++ return tvalue; ++} ++ ++static int ifx_pcie_read_config(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 *value) ++{ ++ u32 data = 0; ++ int bus_number = bus->number; ++ static const u32 mask[8] = {0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0}; ++ int ret = PCIBIOS_SUCCESSFUL; ++ struct ifx_pci_controller *ctrl = bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ if (unlikely(size != 1 && size != 2 && size != 4)){ ++ ret = PCIBIOS_BAD_REGISTER_NUMBER; ++ goto out; ++ } ++ ++ /* Make sure the address is aligned to natural boundary */ ++ if (unlikely(((size - 1) & where))) { ++ ret = PCIBIOS_BAD_REGISTER_NUMBER; ++ goto out; ++ } ++ ++ /* ++ * If we are second controller, we have to cheat OS so that it assume ++ * its bus number starts from 0 in host controller ++ */ ++ bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port); ++ ++ /* ++ * We need to force the bus number to be zero on the root ++ * bus. Linux numbers the 2nd root bus to start after all ++ * busses on root 0. ++ */ ++ if (bus->parent == NULL) { ++ bus_number = 0; ++ } ++ ++ /* ++ * PCIe only has a single device connected to it. It is ++ * always device ID 0. Don't bother doing reads for other ++ * device IDs on the first segment. ++ */ ++ if ((bus_number == 0) && (PCI_SLOT(devfn) != 0)) { ++ ret = PCIBIOS_FUNC_NOT_SUPPORTED; ++ goto out; ++ } ++ ++ if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) { ++ *value = 0xffffffff; ++ ret = PCIBIOS_DEVICE_NOT_FOUND; ++ goto out; ++ } ++ ++ PCIE_IRQ_LOCK(ifx_pcie_lock); ++ if (bus_number == 0) { /* RC itself */ ++ u32 t; ++ ++ t = (where & ~3); ++ data = ifx_pcie_rc_cfg_rd(pcie_port, t); ++ } else { ++ u32 addr = pcie_bus_addr(bus_number, devfn, where); ++ ++ data = ifx_pcie_cfg_rd(pcie_port, addr); ++ #ifdef CONFIG_IFX_PCIE_HW_SWAP ++ data = le32_to_cpu(data); ++ #endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } ++ /* To get a correct PCI topology, we have to restore the bus number to OS */ ++ data = ifx_pcie_bus_enum_hack(bus, devfn, where, data, pcie_port, 1); ++ ++ PCIE_IRQ_UNLOCK(ifx_pcie_lock); ++ ++ *value = (data >> (8 * (where & 3))) & mask[size & 7]; ++out: ++ return ret; ++} ++ ++static u32 ifx_pcie_size_to_value(int where, int size, u32 data, u32 value) ++{ ++ u32 shift; ++ u32 tdata = data; ++ ++ switch (size) { ++ case 1: ++ shift = (where & 0x3) << 3; ++ tdata &= ~(0xffU << shift); ++ tdata |= ((value & 0xffU) << shift); ++ break; ++ case 2: ++ shift = (where & 3) << 3; ++ tdata &= ~(0xffffU << shift); ++ tdata |= ((value & 0xffffU) << shift); ++ break; ++ case 4: ++ tdata = value; ++ break; ++ } ++ return tdata; ++} ++ ++static int ifx_pcie_write_config(struct pci_bus *bus, u32 devfn, ++ int where, int size, u32 value) ++{ ++ int bus_number = bus->number; ++ int ret = PCIBIOS_SUCCESSFUL; ++ struct ifx_pci_controller *ctrl = bus->sysdata; ++ int pcie_port = ctrl->port; ++ u32 tvalue = value; ++ u32 data; ++ ++ /* Make sure the address is aligned to natural boundary */ ++ if (unlikely(((size - 1) & where))) { ++ ret = PCIBIOS_BAD_REGISTER_NUMBER; ++ goto out; ++ } ++ /* ++ * If we are second controller, we have to cheat OS so that it assume ++ * its bus number starts from 0 in host controller ++ */ ++ bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port); ++ ++ /* ++ * We need to force the bus number to be zero on the root ++ * bus. Linux numbers the 2nd root bus to start after all ++ * busses on root 0. ++ */ ++ if (bus->parent == NULL) { ++ bus_number = 0; ++ } ++ ++ if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) { ++ ret = PCIBIOS_DEVICE_NOT_FOUND; ++ goto out; ++ } ++ ++ /* XXX, some PCIe device may need some delay */ ++ PCIE_IRQ_LOCK(ifx_pcie_lock); ++ ++ /* ++ * To configure the correct bus topology using native way, we have to cheat Os so that ++ * it can configure the PCIe hardware correctly. ++ */ ++ tvalue = ifx_pcie_bus_enum_hack(bus, devfn, where, value, pcie_port, 0); ++ ++ if (bus_number == 0) { /* RC itself */ ++ u32 t; ++ ++ t = (where & ~3); ++ data = ifx_pcie_rc_cfg_rd(pcie_port, t); ++ ++ data = ifx_pcie_size_to_value(where, size, data, tvalue); ++ ++ ifx_pcie_rc_cfg_wr(pcie_port, t, data); ++ } else { ++ u32 addr = pcie_bus_addr(bus_number, devfn, where); ++ ++ data = ifx_pcie_cfg_rd(pcie_port, addr); ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ data = le32_to_cpu(data); ++#endif ++ ++ data = ifx_pcie_size_to_value(where, size, data, tvalue); ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ data = cpu_to_le32(data); ++#endif ++ ifx_pcie_cfg_wr(pcie_port, addr, data); ++ } ++ PCIE_IRQ_UNLOCK(ifx_pcie_lock); ++out: ++ return ret; ++} ++ ++static struct resource ifx_pcie_io_resource = { ++ .name = "PCIe0 I/O space", ++ .start = PCIE_IO_PHY_BASE, ++ .end = PCIE_IO_PHY_END, ++ .flags = IORESOURCE_IO, ++}; ++ ++static struct resource ifx_pcie_mem_resource = { ++ .name = "PCIe0 Memory space", ++ .start = PCIE_MEM_PHY_BASE, ++ .end = PCIE_MEM_PHY_END, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct pci_ops ifx_pcie_ops = { ++ .read = ifx_pcie_read_config, ++ .write = ifx_pcie_write_config, ++}; ++ ++static struct ifx_pci_controller ifx_pcie_controller[IFX_PCIE_CORE_NR] = { ++ { ++ .pcic = { ++ .pci_ops = &ifx_pcie_ops, ++ .mem_resource = &ifx_pcie_mem_resource, ++ .io_resource = &ifx_pcie_io_resource, ++ }, ++ .port = IFX_PCIE_PORT0, ++ }, ++}; ++ ++#ifdef IFX_PCIE_ERROR_INT ++ ++static irqreturn_t pcie_rc_core_isr(int irq, void *dev_id) ++{ ++ struct ifx_pci_controller *ctrl = (struct ifx_pci_controller *)dev_id; ++ int pcie_port = ctrl->port; ++ u32 reg; ++ ++ pr_debug("PCIe RC error intr %d\n", irq); ++ reg = IFX_REG_R32(PCIE_IRNCR(pcie_port)); ++ reg &= PCIE_RC_CORE_COMBINED_INT; ++ IFX_REG_W32(reg, PCIE_IRNCR(pcie_port)); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ++pcie_rc_core_int_init(int pcie_port) ++{ ++ int ret; ++ ++ /* Enable core interrupt */ ++ IFX_REG_SET_BIT(PCIE_RC_CORE_COMBINED_INT, PCIE_IRNEN(pcie_port)); ++ ++ /* Clear it first */ ++ IFX_REG_SET_BIT(PCIE_RC_CORE_COMBINED_INT, PCIE_IRNCR(pcie_port)); ++ ret = request_irq(pcie_irqs[pcie_port].ir_irq.irq, pcie_rc_core_isr, 0, ++ pcie_irqs[pcie_port].ir_irq.name, &ifx_pcie_controller[pcie_port]); ++ if (ret) ++ printk(KERN_ERR "%s request irq %d failed\n", __func__, IFX_PCIE_IR); ++ ++ return ret; ++} ++#endif ++ ++int ifx_pcie_bios_map_irq(IFX_PCI_CONST struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ u32 irq_bit = 0; ++ int irq = 0; ++ struct ifx_pci_controller *ctrl = dev->bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ printk("%s port %d dev %s slot %d pin %d \n", __func__, pcie_port, pci_name(dev), slot, pin); ++ ++ if ((pin == PCIE_LEGACY_DISABLE) || (pin > PCIE_LEGACY_INT_MAX)) { ++ printk(KERN_WARNING "WARNING: dev %s: invalid interrupt pin %d\n", pci_name(dev), pin); ++ return -1; ++ } ++ ++ /* Pin index so minus one */ ++ irq_bit = pcie_irqs[pcie_port].legacy_irq[pin - 1].irq_bit; ++ irq = pcie_irqs[pcie_port].legacy_irq[pin - 1].irq; ++ IFX_REG_SET_BIT(irq_bit, PCIE_IRNEN(pcie_port)); ++ IFX_REG_SET_BIT(irq_bit, PCIE_IRNCR(pcie_port)); ++ printk("%s dev %s irq %d assigned\n", __func__, pci_name(dev), irq); ++ return irq; ++} ++ ++int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) ++{ ++ u16 config; ++#ifdef IFX_PCIE_ERROR_INT ++ u32 dconfig; ++ int pos; ++#endif ++ ++ /* Enable reporting System errors and parity errors on all devices */ ++ /* Enable parity checking and error reporting */ ++ pci_read_config_word(dev, PCI_COMMAND, &config); ++ config |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR /*| PCI_COMMAND_INVALIDATE | ++ PCI_COMMAND_FAST_BACK*/; ++ pci_write_config_word(dev, PCI_COMMAND, config); ++ ++ if (dev->subordinate) { ++ /* Set latency timers on sub bridges */ ++ pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); /* XXX, */ ++ /* More bridge error detection */ ++ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &config); ++ config |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR; ++ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, config); ++ } ++#ifdef IFX_PCIE_ERROR_INT ++ /* Enable the PCIe normal error reporting */ ++ pos = pci_find_capability(dev, PCI_CAP_ID_EXP); ++ if (pos) { ++ ++ /* Disable system error generation in response to error messages */ ++ pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &config); ++ config &= ~(PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | PCI_EXP_RTCTL_SEFEE); ++ pci_write_config_word(dev, pos + PCI_EXP_RTCTL, config); ++ ++ /* Clear PCIE Capability's Device Status */ ++ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &config); ++ pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, config); ++ ++ /* Update Device Control */ ++ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &config); ++ /* Correctable Error Reporting */ ++ config |= PCI_EXP_DEVCTL_CERE; ++ /* Non-Fatal Error Reporting */ ++ config |= PCI_EXP_DEVCTL_NFERE; ++ /* Fatal Error Reporting */ ++ config |= PCI_EXP_DEVCTL_FERE; ++ /* Unsupported Request */ ++ config |= PCI_EXP_DEVCTL_URRE; ++ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, config); ++ } ++ ++ /* Find the Advanced Error Reporting capability */ ++ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); ++ if (pos) { ++ /* Clear Uncorrectable Error Status */ ++ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &dconfig); ++ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, dconfig); ++ /* Enable reporting of all uncorrectable errors */ ++ /* Uncorrectable Error Mask - turned on bits disable errors */ ++ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, 0); ++ /* ++ * Leave severity at HW default. This only controls if ++ * errors are reported as uncorrectable or ++ * correctable, not if the error is reported. ++ */ ++ /* PCI_ERR_UNCOR_SEVER - Uncorrectable Error Severity */ ++ /* Clear Correctable Error Status */ ++ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &dconfig); ++ pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, dconfig); ++ /* Enable reporting of all correctable errors */ ++ /* Correctable Error Mask - turned on bits disable errors */ ++ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, 0); ++ /* Advanced Error Capabilities */ ++ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &dconfig); ++ /* ECRC Generation Enable */ ++ if (dconfig & PCI_ERR_CAP_ECRC_GENC) { ++ dconfig |= PCI_ERR_CAP_ECRC_GENE; ++ } ++ /* ECRC Check Enable */ ++ if (dconfig & PCI_ERR_CAP_ECRC_CHKC) { ++ dconfig |= PCI_ERR_CAP_ECRC_CHKE; ++ } ++ pci_write_config_dword(dev, pos + PCI_ERR_CAP, dconfig); ++ ++ /* PCI_ERR_HEADER_LOG - Header Log Register (16 bytes) */ ++ /* Enable Root Port's interrupt in response to error messages */ ++ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ++ PCI_ERR_ROOT_CMD_COR_EN | ++ PCI_ERR_ROOT_CMD_NONFATAL_EN | ++ PCI_ERR_ROOT_CMD_FATAL_EN); ++ /* Clear the Root status register */ ++ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &dconfig); ++ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, dconfig); ++ } ++#endif /* IFX_PCIE_ERROR_INT */ ++ /* WAR, only 128 MRRS is supported, force all EPs to support this value */ ++ pcie_set_readrq(dev, 128); ++ return 0; ++} ++ ++static int ++pcie_rc_initialize(int pcie_port) ++{ ++ int i; ++#define IFX_PCIE_PHY_LOOP_CNT 5 ++ ++ pcie_rcu_endian_setup(pcie_port); ++ ++ pcie_ep_gpio_rst_init(pcie_port); ++ ++ /* ++ * XXX, PCIe elastic buffer bug will cause not to be detected. One more ++ * reset PCIe PHY will solve this issue ++ */ ++ for (i = 0; i < IFX_PCIE_PHY_LOOP_CNT; i++) { ++ /* Disable PCIe PHY Analog part for sanity check */ ++ pcie_phy_pmu_disable(pcie_port); ++ ++ pcie_phy_rst_assert(pcie_port); ++ pcie_phy_rst_deassert(pcie_port); ++ ++ /* Make sure PHY PLL is stable */ ++ udelay(20); ++ ++ /* PCIe Core reset enabled, low active, sw programmed */ ++ pcie_core_rst_assert(pcie_port); ++ ++ /* Put PCIe EP in reset status */ ++ pcie_device_rst_assert(pcie_port); ++ ++ /* PCI PHY & Core reset disabled, high active, sw programmed */ ++ pcie_core_rst_deassert(pcie_port); ++ ++ /* Already in a quiet state, program PLL, enable PHY, check ready bit */ ++ pcie_phy_clock_mode_setup(pcie_port); ++ ++ /* Enable PCIe PHY and Clock */ ++ pcie_core_pmu_setup(pcie_port); ++ ++ /* Clear status registers */ ++ pcie_status_register_clear(pcie_port); ++ ++#ifdef CONFIG_PCI_MSI ++ pcie_msi_init(pcie_port); ++#endif /* CONFIG_PCI_MSI */ ++ pcie_rc_cfg_reg_setup(pcie_port); ++ ++ /* Once link is up, break out */ ++ if (pcie_app_loigc_setup(pcie_port) == 0) ++ break; ++ } ++ if (i >= IFX_PCIE_PHY_LOOP_CNT) { ++ printk(KERN_ERR "%s link up failed!!!!!\n", __func__); ++ return -EIO; ++ } ++ /* NB, don't increase ACK/NACK timer timeout value, which will cause a lot of COR errors */ ++ pcie_replay_time_update(pcie_port); ++ return 0; ++} ++ ++static int __init ifx_pcie_bios_init(void) ++{ ++ void __iomem *io_map_base; ++ int pcie_port; ++ int startup_port; ++ ++ /* Enable AHB Master/ Slave */ ++ pcie_ahb_pmu_setup(); ++ ++ startup_port = IFX_PCIE_PORT0; ++ ++ for (pcie_port = startup_port; pcie_port < IFX_PCIE_CORE_NR; pcie_port++){ ++ if (pcie_rc_initialize(pcie_port) == 0) { ++ IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: ifx_pcie_cfg_base 0x%p\n", ++ __func__, PCIE_CFG_PORT_TO_BASE(pcie_port)); ++ /* Otherwise, warning will pop up */ ++ io_map_base = ioremap(PCIE_IO_PHY_PORT_TO_BASE(pcie_port), PCIE_IO_SIZE); ++ if (io_map_base == NULL) { ++ IFX_PCIE_PRINT(PCIE_MSG_ERR, "%s io space ioremap failed\n", __func__); ++ return -ENOMEM; ++ } ++ ifx_pcie_controller[pcie_port].pcic.io_map_base = (unsigned long)io_map_base; ++ ++ register_pci_controller(&ifx_pcie_controller[pcie_port].pcic); ++ /* XXX, clear error status */ ++ ++ IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: mem_resource 0x%p, io_resource 0x%p\n", ++ __func__, &ifx_pcie_controller[pcie_port].pcic.mem_resource, ++ &ifx_pcie_controller[pcie_port].pcic.io_resource); ++ ++ #ifdef IFX_PCIE_ERROR_INT ++ pcie_rc_core_int_init(pcie_port); ++ #endif /* IFX_PCIE_ERROR_INT */ ++ } ++ } ++ ++ return 0; ++} ++arch_initcall(ifx_pcie_bios_init); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Chuanhua.Lei@infineon.com"); ++MODULE_SUPPORTED_DEVICE("Infineon builtin PCIe RC module"); ++MODULE_DESCRIPTION("Infineon builtin PCIe RC driver"); ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie.h +@@ -0,0 +1,131 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie.h ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++#ifndef IFXMIPS_PCIE_H ++#define IFXMIPS_PCIE_H ++#include <linux/version.h> ++#include <linux/types.h> ++#include <linux/pci.h> ++#include <linux/interrupt.h> ++#include "ifxmips_pci_common.h" ++#include "ifxmips_pcie_reg.h" ++ ++/*! ++ \defgroup IFX_PCIE PCI Express bus driver module ++ \brief PCI Express IP module support VRX200 ++*/ ++ ++/*! ++ \defgroup IFX_PCIE_OS OS APIs ++ \ingroup IFX_PCIE ++ \brief PCIe bus driver OS interface functions ++*/ ++ ++/*! ++ \file ifxmips_pcie.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe module common header file ++*/ ++#define PCIE_IRQ_LOCK(lock) do { \ ++ unsigned long flags; \ ++ spin_lock_irqsave(&(lock), flags); ++#define PCIE_IRQ_UNLOCK(lock) \ ++ spin_unlock_irqrestore(&(lock), flags); \ ++} while (0) ++ ++#define PCIE_MSG_MSI 0x00000001 ++#define PCIE_MSG_ISR 0x00000002 ++#define PCIE_MSG_FIXUP 0x00000004 ++#define PCIE_MSG_READ_CFG 0x00000008 ++#define PCIE_MSG_WRITE_CFG 0x00000010 ++#define PCIE_MSG_CFG (PCIE_MSG_READ_CFG | PCIE_MSG_WRITE_CFG) ++#define PCIE_MSG_REG 0x00000020 ++#define PCIE_MSG_INIT 0x00000040 ++#define PCIE_MSG_ERR 0x00000080 ++#define PCIE_MSG_PHY 0x00000100 ++#define PCIE_MSG_ANY 0x000001ff ++ ++#define IFX_PCIE_PORT0 0 ++#define IFX_PCIE_PORT1 1 ++ ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++#define IFX_PCIE_CORE_NR 2 ++#else ++#define IFX_PCIE_CORE_NR 1 ++#endif ++ ++#define IFX_PCIE_ERROR_INT ++ ++//#define IFX_PCIE_DBG ++ ++#if defined(IFX_PCIE_DBG) ++#define IFX_PCIE_PRINT(_m, _fmt, args...) do { \ ++ ifx_pcie_debug((_fmt), ##args); \ ++} while (0) ++ ++#define INLINE ++#else ++#define IFX_PCIE_PRINT(_m, _fmt, args...) \ ++ do {} while(0) ++#define INLINE inline ++#endif ++ ++struct ifx_pci_controller { ++ struct pci_controller pcic; ++ ++ /* RC specific, per host bus information */ ++ u32 port; /* Port index, 0 -- 1st core, 1 -- 2nd core */ ++}; ++ ++typedef struct ifx_pcie_ir_irq { ++ const unsigned int irq; ++ const char name[16]; ++}ifx_pcie_ir_irq_t; ++ ++typedef struct ifx_pcie_legacy_irq{ ++ const u32 irq_bit; ++ const int irq; ++}ifx_pcie_legacy_irq_t; ++ ++typedef struct ifx_pcie_irq { ++ ifx_pcie_ir_irq_t ir_irq; ++ ifx_pcie_legacy_irq_t legacy_irq[PCIE_LEGACY_INT_MAX]; ++}ifx_pcie_irq_t; ++ ++extern u32 g_pcie_debug_flag; ++extern void ifx_pcie_debug(const char *fmt, ...); ++extern void pcie_phy_clock_mode_setup(int pcie_port); ++extern void pcie_msi_pic_init(int pcie_port); ++extern u32 ifx_pcie_bus_enum_read_hack(int where, u32 value); ++extern u32 ifx_pcie_bus_enum_write_hack(int where, u32 value); ++ ++#define CONFIG_VR9 ++ ++#ifdef CONFIG_VR9 ++#include "ifxmips_pcie_vr9.h" ++#elif defined (CONFIG_AR10) ++#include "ifxmips_pcie_ar10.h" ++#else ++#error "PCIE: platform not defined" ++#endif /* CONFIG_VR9 */ ++ ++#endif /* IFXMIPS_PCIE_H */ ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_ar10.h +@@ -0,0 +1,290 @@ ++/**************************************************************************** ++ Copyright (c) 2010 ++ Lantiq Deutschland GmbH ++ Am Campeon 3; 85579 Neubiberg, Germany ++ ++ For licensing information, see the file 'LICENSE' in the root folder of ++ this software module. ++ ++ *****************************************************************************/ ++/*! ++ \file ifxmips_pcie_ar10.h ++ \ingroup IFX_PCIE ++ \brief PCIe RC driver ar10 specific file ++*/ ++ ++#ifndef IFXMIPS_PCIE_AR10_H ++#define IFXMIPS_PCIE_AR10_H ++#ifndef AUTOCONF_INCLUDED ++#include <linux/config.h> ++#endif /* AUTOCONF_INCLUDED */ ++#include <linux/types.h> ++#include <linux/delay.h> ++ ++/* Project header file */ ++#include <asm/ifx/ifx_types.h> ++#include <asm/ifx/ifx_pmu.h> ++#include <asm/ifx/ifx_gpio.h> ++#include <asm/ifx/ifx_ebu_led.h> ++ ++static inline void pcie_ep_gpio_rst_init(int pcie_port) ++{ ++ ifx_ebu_led_enable(); ++ if (pcie_port == 0) { ++ ifx_ebu_led_set_data(11, 1); ++ } ++ else { ++ ifx_ebu_led_set_data(12, 1); ++ } ++} ++ ++static inline void pcie_ahb_pmu_setup(void) ++{ ++ /* XXX, moved to CGU to control AHBM */ ++} ++ ++static inline void pcie_rcu_endian_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++ /* Inbound, big endian */ ++ reg |= IFX_RCU_BE_AHB4S; ++ if (pcie_port == 0) { ++ reg |= IFX_RCU_BE_PCIE0M; ++ ++ #ifdef CONFIG_IFX_PCIE_HW_SWAP ++ /* Outbound, software swap needed */ ++ reg |= IFX_RCU_BE_AHB3M; ++ reg &= ~IFX_RCU_BE_PCIE0S; ++ #else ++ /* Outbound little endian */ ++ reg &= ~IFX_RCU_BE_AHB3M; ++ reg &= ~IFX_RCU_BE_PCIE0S; ++ #endif ++ } ++ else { ++ reg |= IFX_RCU_BE_PCIE1M; ++ #ifdef CONFIG_IFX_PCIE1_HW_SWAP ++ /* Outbound, software swap needed */ ++ reg |= IFX_RCU_BE_AHB3M; ++ reg &= ~IFX_RCU_BE_PCIE1S; ++ #else ++ /* Outbound little endian */ ++ reg &= ~IFX_RCU_BE_AHB3M; ++ reg &= ~IFX_RCU_BE_PCIE1S; ++ #endif ++ } ++ ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++ IFX_PCIE_PRINT(PCIE_MSG_REG, "%s IFX_RCU_AHB_ENDIAN: 0x%08x\n", __func__, IFX_REG_R32(IFX_RCU_AHB_ENDIAN)); ++} ++ ++static inline void pcie_phy_pmu_enable(int pcie_port) ++{ ++ if (pcie_port == 0) { /* XXX, should use macro*/ ++ PCIE0_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ PCIE1_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline void pcie_phy_pmu_disable(int pcie_port) ++{ ++ if (pcie_port == 0) { /* XXX, should use macro*/ ++ PCIE0_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++ } ++ else { ++ PCIE1_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++ } ++} ++ ++static inline void pcie_pdi_big_endian(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++ if (pcie_port == 0) { ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_BE_PCIE0_PDI; ++ } ++ else { ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_BE_PCIE1_PDI; ++ } ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++} ++ ++static inline void pcie_pdi_pmu_enable(int pcie_port) ++{ ++ if (pcie_port == 0) { ++ /* Enable PDI to access PCIe PHY register */ ++ PDI0_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ PDI1_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline void pcie_core_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ ++ /* Reset Core, bit 22 */ ++ if (pcie_port == 0) { ++ reg |= 0x00400000; ++ } ++ else { ++ reg |= 0x08000000; /* Bit 27 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_core_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ if (pcie_port == 0) { ++ reg &= ~0x00400000; /* bit 22 */ ++ } ++ else { ++ reg &= ~0x08000000; /* Bit 27 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ if (pcie_port == 0) { ++ reg |= 0x00001000; /* Bit 12 */ ++ } ++ else { ++ reg |= 0x00002000; /* Bit 13 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ if (pcie_port == 0) { ++ reg &= ~0x00001000; /* Bit 12 */ ++ } ++ else { ++ reg &= ~0x00002000; /* Bit 13 */ ++ } ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_device_rst_assert(int pcie_port) ++{ ++ if (pcie_port == 0) { ++ ifx_ebu_led_set_data(11, 0); ++ } ++ else { ++ ifx_ebu_led_set_data(12, 0); ++ } ++} ++ ++static inline void pcie_device_rst_deassert(int pcie_port) ++{ ++ mdelay(100); ++ if (pcie_port == 0) { ++ ifx_ebu_led_set_data(11, 1); ++ } ++ else { ++ ifx_ebu_led_set_data(12, 1); ++ } ++ ifx_ebu_led_disable(); ++} ++ ++static inline void pcie_core_pmu_setup(int pcie_port) ++{ ++ if (pcie_port == 0) { ++ PCIE0_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ PCIE1_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline void pcie_msi_init(int pcie_port) ++{ ++ pcie_msi_pic_init(pcie_port); ++ if (pcie_port == 0) { ++ MSI0_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++ else { ++ MSI1_PMU_SETUP(IFX_PMU_ENABLE); ++ } ++} ++ ++static inline u32 ++ifx_pcie_bus_nr_deduct(u32 bus_number, int pcie_port) ++{ ++ u32 tbus_number = bus_number; ++ ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ ++ if (pcibios_host_nr() > 1) { ++ tbus_number -= pcibios_1st_host_bus_nr(); ++ } ++ } ++#endif /* CONFIG_IFX_PCI */ ++ return tbus_number; ++} ++ ++static inline u32 ++ifx_pcie_bus_enum_hack(struct pci_bus *bus, u32 devfn, int where, u32 value, int pcie_port, int read) ++{ ++ struct pci_dev *pdev; ++ u32 tvalue = value; ++ ++ /* Sanity check */ ++ pdev = pci_get_slot(bus, devfn); ++ if (pdev == NULL) { ++ return tvalue; ++ } ++ ++ /* Only care about PCI bridge */ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { ++ return tvalue; ++ } ++ ++ if (read) { /* Read hack */ ++ #ifdef CONFIG_IFX_PCIE_2ND_CORE ++ if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_read_hack(where, tvalue); ++ } ++ } ++ #endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++ } ++ else { /* Write hack */ ++ #ifdef CONFIG_IFX_PCIE_2ND_CORE ++ if (pcie_port == IFX_PCIE_PORT1) { /* Port 1 must check if there are two cores enabled */ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_write_hack(where, tvalue); ++ } ++ } ++ #endif ++ } ++ return tvalue; ++} ++ ++#endif /* IFXMIPS_PCIE_AR10_H */ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_msi.c +@@ -0,0 +1,392 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_msi.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCI MSI sub module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe MSI Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Date $Author $Comment ++** 02 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \defgroup IFX_PCIE_MSI MSI OS APIs ++ \ingroup IFX_PCIE ++ \brief PCIe bus driver OS interface functions ++*/ ++ ++/*! ++ \file ifxmips_pcie_msi.c ++ \ingroup IFX_PCIE ++ \brief PCIe MSI OS interface file ++*/ ++ ++#ifndef AUTOCONF_INCLUDED ++#include <linux/config.h> ++#endif /* AUTOCONF_INCLUDED */ ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/kernel_stat.h> ++#include <linux/pci.h> ++#include <linux/msi.h> ++#include <linux/module.h> ++#include <asm/bootinfo.h> ++#include <asm/irq.h> ++#include <asm/traps.h> ++ ++#include <asm/ifx/ifx_types.h> ++#include <asm/ifx/ifx_regs.h> ++#include <asm/ifx/common_routines.h> ++#include <asm/ifx/irq.h> ++ ++#include "ifxmips_pcie_reg.h" ++#include "ifxmips_pcie.h" ++ ++#define IFX_MSI_IRQ_NUM 16 ++ ++enum { ++ IFX_PCIE_MSI_IDX0 = 0, ++ IFX_PCIE_MSI_IDX1, ++ IFX_PCIE_MSI_IDX2, ++ IFX_PCIE_MSI_IDX3, ++}; ++ ++typedef struct ifx_msi_irq_idx { ++ const int irq; ++ const int idx; ++}ifx_msi_irq_idx_t; ++ ++struct ifx_msi_pic { ++ volatile u32 pic_table[IFX_MSI_IRQ_NUM]; ++ volatile u32 pic_endian; /* 0x40 */ ++}; ++typedef struct ifx_msi_pic *ifx_msi_pic_t; ++ ++typedef struct ifx_msi_irq { ++ const volatile ifx_msi_pic_t msi_pic_p; ++ const u32 msi_phy_base; ++ const ifx_msi_irq_idx_t msi_irq_idx[IFX_MSI_IRQ_NUM]; ++ /* ++ * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is ++ * in use. ++ */ ++ u16 msi_free_irq_bitmask; ++ ++ /* ++ * Each bit in msi_multiple_irq_bitmask tells that the device using ++ * this bit in msi_free_irq_bitmask is also using the next bit. This ++ * is used so we can disable all of the MSI interrupts when a device ++ * uses multiple. ++ */ ++ u16 msi_multiple_irq_bitmask; ++}ifx_msi_irq_t; ++ ++static ifx_msi_irq_t msi_irqs[IFX_PCIE_CORE_NR] = { ++ { ++ .msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI_PIC_REG_BASE, ++ .msi_phy_base = PCIE_MSI_PHY_BASE, ++ .msi_irq_idx = { ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ }, ++ .msi_free_irq_bitmask = 0, ++ .msi_multiple_irq_bitmask= 0, ++ }, ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ { ++ .msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI1_PIC_REG_BASE, ++ .msi_phy_base = PCIE1_MSI_PHY_BASE, ++ .msi_irq_idx = { ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ }, ++ .msi_free_irq_bitmask = 0, ++ .msi_multiple_irq_bitmask= 0, ++ ++ }, ++#endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++}; ++ ++/* ++ * This lock controls updates to msi_free_irq_bitmask, ++ * msi_multiple_irq_bitmask and pic register settting ++ */ ++static DEFINE_SPINLOCK(ifx_pcie_msi_lock); ++ ++void pcie_msi_pic_init(int pcie_port) ++{ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_pic_p->pic_endian = IFX_MSI_PIC_BIG_ENDIAN; ++ spin_unlock(&ifx_pcie_msi_lock); ++} ++ ++/** ++ * \fn int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) ++ * \brief Called when a driver request MSI interrupts instead of the ++ * legacy INT A-D. This routine will allocate multiple interrupts ++ * for MSI devices that support them. A device can override this by ++ * programming the MSI control bits [6:4] before calling ++ * pci_enable_msi(). ++ * ++ * \param[in] pdev Device requesting MSI interrupts ++ * \param[in] desc MSI descriptor ++ * ++ * \return -EINVAL Invalid pcie root port or invalid msi bit ++ * \return 0 OK ++ * \ingroup IFX_PCIE_MSI ++ */ ++int ++arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) ++{ ++ int irq, pos; ++ u16 control; ++ int irq_idx; ++ int irq_step; ++ int configured_private_bits; ++ int request_private_bits; ++ struct msi_msg msg; ++ u16 search_mask; ++ struct ifx_pci_controller *ctrl = pdev->bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s %s enter\n", __func__, pci_name(pdev)); ++ ++ /* XXX, skip RC MSI itself */ ++ if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) { ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s RC itself doesn't use MSI interrupt\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Read the MSI config to figure out how many IRQs this device ++ * wants. Most devices only want 1, which will give ++ * configured_private_bits and request_private_bits equal 0. ++ */ ++ pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &control); ++ ++ /* ++ * If the number of private bits has been configured then use ++ * that value instead of the requested number. This gives the ++ * driver the chance to override the number of interrupts ++ * before calling pci_enable_msi(). ++ */ ++ configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4; ++ if (configured_private_bits == 0) { ++ /* Nothing is configured, so use the hardware requested size */ ++ request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1; ++ } ++ else { ++ /* ++ * Use the number of configured bits, assuming the ++ * driver wanted to override the hardware request ++ * value. ++ */ ++ request_private_bits = configured_private_bits; ++ } ++ ++ /* ++ * The PCI 2.3 spec mandates that there are at most 32 ++ * interrupts. If this device asks for more, only give it one. ++ */ ++ if (request_private_bits > 5) { ++ request_private_bits = 0; ++ } ++again: ++ /* ++ * The IRQs have to be aligned on a power of two based on the ++ * number being requested. ++ */ ++ irq_step = (1 << request_private_bits); ++ ++ /* Mask with one bit for each IRQ */ ++ search_mask = (1 << irq_step) - 1; ++ ++ /* ++ * We're going to search msi_free_irq_bitmask_lock for zero ++ * bits. This represents an MSI interrupt number that isn't in ++ * use. ++ */ ++ spin_lock(&ifx_pcie_msi_lock); ++ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos += irq_step) { ++ if ((msi_irqs[pcie_port].msi_free_irq_bitmask & (search_mask << pos)) == 0) { ++ msi_irqs[pcie_port].msi_free_irq_bitmask |= search_mask << pos; ++ msi_irqs[pcie_port].msi_multiple_irq_bitmask |= (search_mask >> 1) << pos; ++ break; ++ } ++ } ++ spin_unlock(&ifx_pcie_msi_lock); ++ ++ /* Make sure the search for available interrupts didn't fail */ ++ if (pos >= IFX_MSI_IRQ_NUM) { ++ if (request_private_bits) { ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s: Unable to find %d free " ++ "interrupts, trying just one", __func__, 1 << request_private_bits); ++ request_private_bits = 0; ++ goto again; ++ } ++ else { ++ printk(KERN_ERR "%s: Unable to find a free MSI interrupt\n", __func__); ++ return -EINVAL; ++ } ++ } ++ irq = msi_irqs[pcie_port].msi_irq_idx[pos].irq; ++ irq_idx = msi_irqs[pcie_port].msi_irq_idx[pos].idx; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "pos %d, irq %d irq_idx %d\n", pos, irq, irq_idx); ++ ++ /* ++ * Initialize MSI. This has to match the memory-write endianess from the device ++ * Address bits [23:12] ++ */ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] = SM(irq_idx, IFX_MSI_PIC_INT_LINE) | ++ SM((msi_irqs[pcie_port].msi_phy_base >> 12), IFX_MSI_PIC_MSG_ADDR) | ++ SM((1 << pos), IFX_MSI_PIC_MSG_DATA); ++ ++ /* Enable this entry */ ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~IFX_MSI_PCI_INT_DISABLE; ++ spin_unlock(&ifx_pcie_msi_lock); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "pic_table[%d]: 0x%08x\n", ++ pos, msi_irqs[pcie_port].msi_pic_p->pic_table[pos]); ++ ++ /* Update the number of IRQs the device has available to it */ ++ control &= ~PCI_MSI_FLAGS_QSIZE; ++ control |= (request_private_bits << 4); ++ pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, control); ++ ++ set_irq_msi(irq, desc); ++ msg.address_hi = 0x0; ++ msg.address_lo = msi_irqs[pcie_port].msi_phy_base; ++ msg.data = SM((1 << pos), IFX_MSI_PIC_MSG_DATA); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "msi_data: pos %d 0x%08x\n", pos, msg.data); ++ ++ write_msi_msg(irq, &msg); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__); ++ return 0; ++} ++ ++static int ++pcie_msi_irq_to_port(unsigned int irq, int *port) ++{ ++ int ret = 0; ++ ++ if (irq == IFX_PCIE_MSI_IR0 || irq == IFX_PCIE_MSI_IR1 || ++ irq == IFX_PCIE_MSI_IR2 || irq == IFX_PCIE_MSI_IR3) { ++ *port = IFX_PCIE_PORT0; ++ } ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ else if (irq == IFX_PCIE1_MSI_IR0 || irq == IFX_PCIE1_MSI_IR1 || ++ irq == IFX_PCIE1_MSI_IR2 || irq == IFX_PCIE1_MSI_IR3) { ++ *port = IFX_PCIE_PORT1; ++ } ++#endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++ else { ++ printk(KERN_ERR "%s: Attempted to teardown illegal " ++ "MSI interrupt (%d)\n", __func__, irq); ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++/** ++ * \fn void arch_teardown_msi_irq(unsigned int irq) ++ * \brief Called when a device no longer needs its MSI interrupts. All ++ * MSI interrupts for the device are freed. ++ * ++ * \param irq The devices first irq number. There may be multple in sequence. ++ * \return none ++ * \ingroup IFX_PCIE_MSI ++ */ ++void ++arch_teardown_msi_irq(unsigned int irq) ++{ ++ int pos; ++ int number_irqs; ++ u16 bitmask; ++ int pcie_port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s enter\n", __func__); ++ ++ BUG_ON(irq > INT_NUM_IM4_IRL31); ++ ++ if (pcie_msi_irq_to_port(irq, &pcie_port) != 0) { ++ return; ++ } ++ ++ /* Shift the mask to the correct bit location, not always correct ++ * Probally, the first match will be chosen. ++ */ ++ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos++) { ++ if ((msi_irqs[pcie_port].msi_irq_idx[pos].irq == irq) ++ && (msi_irqs[pcie_port].msi_free_irq_bitmask & ( 1 << pos))) { ++ break; ++ } ++ } ++ if (pos >= IFX_MSI_IRQ_NUM) { ++ printk(KERN_ERR "%s: Unable to find a matched MSI interrupt\n", __func__); ++ return; ++ } ++ spin_lock(&ifx_pcie_msi_lock); ++ /* Disable this entry */ ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] |= IFX_MSI_PCI_INT_DISABLE; ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~(IFX_MSI_PIC_INT_LINE | IFX_MSI_PIC_MSG_ADDR | IFX_MSI_PIC_MSG_DATA); ++ spin_unlock(&ifx_pcie_msi_lock); ++ /* ++ * Count the number of IRQs we need to free by looking at the ++ * msi_multiple_irq_bitmask. Each bit set means that the next ++ * IRQ is also owned by this device. ++ */ ++ number_irqs = 0; ++ while (((pos + number_irqs) < IFX_MSI_IRQ_NUM) && ++ (msi_irqs[pcie_port].msi_multiple_irq_bitmask & (1 << (pos + number_irqs)))) { ++ number_irqs++; ++ } ++ number_irqs++; ++ ++ /* Mask with one bit for each IRQ */ ++ bitmask = (1 << number_irqs) - 1; ++ ++ bitmask <<= pos; ++ if ((msi_irqs[pcie_port].msi_free_irq_bitmask & bitmask) != bitmask) { ++ printk(KERN_ERR "%s: Attempted to teardown MSI " ++ "interrupt (%d) not in use\n", __func__, irq); ++ return; ++ } ++ /* Checks are done, update the in use bitmask */ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_free_irq_bitmask &= ~bitmask; ++ msi_irqs[pcie_port].msi_multiple_irq_bitmask &= ~(bitmask >> 1); ++ spin_unlock(&ifx_pcie_msi_lock); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Chuanhua.Lei@infineon.com"); ++MODULE_SUPPORTED_DEVICE("Infineon PCIe IP builtin MSI PIC module"); ++MODULE_DESCRIPTION("Infineon PCIe IP builtin MSI PIC driver"); ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_phy.c +@@ -0,0 +1,478 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_phy.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe PHY sub module ++** ++** DATE : 14 May 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 14 May,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \file ifxmips_pcie_phy.c ++ \ingroup IFX_PCIE ++ \brief PCIe PHY PLL register programming source file ++*/ ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <asm/paccess.h> ++#include <linux/delay.h> ++ ++#include "ifxmips_pcie_reg.h" ++#include "ifxmips_pcie.h" ++ ++/* PCIe PDI only supports 16 bit operation */ ++ ++#define IFX_PCIE_PHY_REG_WRITE16(__addr, __data) \ ++ ((*(volatile u16 *) (__addr)) = (__data)) ++ ++#define IFX_PCIE_PHY_REG_READ16(__addr) \ ++ (*(volatile u16 *) (__addr)) ++ ++#define IFX_PCIE_PHY_REG16(__addr) \ ++ (*(volatile u16 *) (__addr)) ++ ++#define IFX_PCIE_PHY_REG(__reg, __value, __mask) do { \ ++ u16 read_data; \ ++ u16 write_data; \ ++ read_data = IFX_PCIE_PHY_REG_READ16((__reg)); \ ++ write_data = (read_data & ((u16)~(__mask))) | (((u16)(__value)) & ((u16)(__mask)));\ ++ IFX_PCIE_PHY_REG_WRITE16((__reg), write_data); \ ++} while (0) ++ ++#define IFX_PCIE_PLL_TIMEOUT 1000 /* Tunnable */ ++ ++//#define IFX_PCI_PHY_REG_DUMP ++ ++#ifdef IFX_PCI_PHY_REG_DUMP ++static void ++pcie_phy_reg_dump(int pcie_port) ++{ ++ printk("PLL REGFILE\n"); ++ printk("PCIE_PHY_PLL_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL1(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL2(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL3(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL4 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL4(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL5 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL5(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL6 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL6(pcie_port))); ++ printk("PCIE_PHY_PLL_CTRL7 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_CTRL7(pcie_port))); ++ printk("PCIE_PHY_PLL_A_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_A_CTRL1(pcie_port))); ++ printk("PCIE_PHY_PLL_A_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_A_CTRL2(pcie_port))); ++ printk("PCIE_PHY_PLL_A_CTRL3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_A_CTRL3(pcie_port))); ++ printk("PCIE_PHY_PLL_STATUS 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_STATUS(pcie_port))); ++ ++ printk("TX1 REGFILE\n"); ++ printk("PCIE_PHY_TX1_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX1_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX1_CTRL3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_CTRL3(pcie_port))); ++ printk("PCIE_PHY_TX1_A_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_A_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX1_A_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_A_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX1_MOD1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_MOD1(pcie_port))); ++ printk("PCIE_PHY_TX1_MOD2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_MOD2(pcie_port))); ++ printk("PCIE_PHY_TX1_MOD3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX1_MOD3(pcie_port))); ++ ++ printk("TX2 REGFILE\n"); ++ printk("PCIE_PHY_TX2_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX2_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX2_A_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_A_CTRL1(pcie_port))); ++ printk("PCIE_PHY_TX2_A_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_A_CTRL2(pcie_port))); ++ printk("PCIE_PHY_TX2_MOD1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_MOD1(pcie_port))); ++ printk("PCIE_PHY_TX2_MOD2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_MOD2(pcie_port))); ++ printk("PCIE_PHY_TX2_MOD3 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_TX2_MOD3(pcie_port))); ++ ++ printk("RX1 REGFILE\n"); ++ printk("PCIE_PHY_RX1_CTRL1 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_CTRL1(pcie_port))); ++ printk("PCIE_PHY_RX1_CTRL2 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_CTRL2(pcie_port))); ++ printk("PCIE_PHY_RX1_CDR 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_CDR(pcie_port))); ++ printk("PCIE_PHY_RX1_EI 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_EI(pcie_port))); ++ printk("PCIE_PHY_RX1_A_CTRL 0x%04x\n", IFX_PCIE_PHY_REG16(PCIE_PHY_RX1_A_CTRL(pcie_port))); ++} ++#endif /* IFX_PCI_PHY_REG_DUMP */ ++ ++static void ++pcie_phy_comm_setup(int pcie_port) ++{ ++ /* PLL Setting */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF); ++ ++ /* increase the bias reference voltage */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF); ++ ++ /* Endcnt */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF); ++ ++ /* force */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF); ++ ++ /* ctrl_lim */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF); ++ ++ /* ctrl */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00); ++ ++ /* RTERM*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF); ++ ++ /* Improved 100MHz clock output */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF); ++ ++ /* Reduced CDR BW to avoid glitches */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); ++} ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++static void ++pcie_phy_36mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE ++static void ++pcie_phy_36mhz_ssc_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ ++ /* PLL Setting */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF); ++ ++ /* Increase the bias reference voltage */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF); ++ ++ /* Endcnt */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF); ++ ++ /* Force */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008); ++ ++ /* Predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF); ++ ++ /* ctrl_lim */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF); ++ ++ /* ctrl */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00); ++ ++ /* RTERM*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF); ++ ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0400, 0x0400); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0100); ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1c72, 0xFFFF); ++ ++ /* improved 100MHz clock output */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF); ++ ++ /* reduced CDR BW to avoid glitches */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_25MHZ_MODE ++static void ++pcie_phy_25mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0200); ++ ++ /* en_ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0002, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0040, 0x0070); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x6000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x4000, 0x4000); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_25MHZ_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_100MHZ_MODE ++static void ++pcie_phy_100mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Initial PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_100MHZ_MODE */ ++ ++static int ++pcie_phy_wait_startup_ready(int pcie_port) ++{ ++ int i; ++ ++ for (i = 0; i < IFX_PCIE_PLL_TIMEOUT; i++) { ++ if ((IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_STATUS(pcie_port)) & 0x0040) != 0) { ++ break; ++ } ++ udelay(10); ++ } ++ if (i >= IFX_PCIE_PLL_TIMEOUT) { ++ printk(KERN_ERR "%s PLL Link timeout\n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static void ++pcie_phy_load_enable(int pcie_port, int slice) ++{ ++ /* Set the load_en of tx/rx slice to '1' */ ++ switch (slice) { ++ case 1: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0010, 0x0010); ++ break; ++ case 2: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0010, 0x0010); ++ break; ++ case 3: ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0002, 0x0002); ++ break; ++ } ++} ++ ++static void ++pcie_phy_load_disable(int pcie_port, int slice) ++{ ++ /* set the load_en of tx/rx slice to '0' */ ++ switch (slice) { ++ case 1: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0000, 0x0010); ++ break; ++ case 2: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0000, 0x0010); ++ break; ++ case 3: ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0000, 0x0002); ++ break; ++ } ++} ++ ++static void ++pcie_phy_load_war(int pcie_port) ++{ ++ int slice; ++ ++ for (slice = 1; slice < 4; slice++) { ++ pcie_phy_load_enable(pcie_port, slice); ++ udelay(1); ++ pcie_phy_load_disable(pcie_port, slice); ++ } ++} ++ ++static void ++pcie_phy_tx2_modulation(int pcie_port) ++{ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD1(pcie_port), 0x1FFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD2(pcie_port), 0xFFFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0601, 0xFFFF); ++ mdelay(1); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0001, 0xFFFF); ++} ++ ++static void ++pcie_phy_tx1_modulation(int pcie_port) ++{ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD1(pcie_port), 0x1FFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD2(pcie_port), 0xFFFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0601, 0xFFFF); ++ mdelay(1); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0001, 0xFFFF); ++} ++ ++static void ++pcie_phy_tx_modulation_war(int pcie_port) ++{ ++ int i; ++ ++#define PCIE_PHY_MODULATION_NUM 5 ++ for (i = 0; i < PCIE_PHY_MODULATION_NUM; i++) { ++ pcie_phy_tx2_modulation(pcie_port); ++ pcie_phy_tx1_modulation(pcie_port); ++ } ++#undef PCIE_PHY_MODULATION_NUM ++} ++ ++void ++pcie_phy_clock_mode_setup(int pcie_port) ++{ ++ pcie_pdi_big_endian(pcie_port); ++ ++ /* Enable PDI to access PCIe PHY register */ ++ pcie_pdi_pmu_enable(pcie_port); ++ ++ /* Configure PLL and PHY clock */ ++ pcie_phy_comm_setup(pcie_port); ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ pcie_phy_36mhz_mode_setup(pcie_port); ++#elif defined(CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE) ++ pcie_phy_36mhz_ssc_mode_setup(pcie_port); ++#elif defined(CONFIG_IFX_PCIE_PHY_25MHZ_MODE) ++ pcie_phy_25mhz_mode_setup(pcie_port); ++#elif defined (CONFIG_IFX_PCIE_PHY_100MHZ_MODE) ++ pcie_phy_100mhz_mode_setup(pcie_port); ++#else ++ #error "PCIE PHY Clock Mode must be chosen first!!!!" ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ ++ ++ /* Enable PCIe PHY and make PLL setting take effect */ ++ pcie_phy_pmu_enable(pcie_port); ++ ++ /* Check if we are in startup_ready status */ ++ pcie_phy_wait_startup_ready(pcie_port); ++ ++ pcie_phy_load_war(pcie_port); ++ ++ /* Apply TX modulation workarounds */ ++ pcie_phy_tx_modulation_war(pcie_port); ++ ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Modified PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++} ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_pm.c +@@ -0,0 +1,176 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_pm.c ++** PROJECT : IFX UEIP ++** MODULES : PCIE Root Complex Driver ++** ++** DATE : 21 Dec 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIE Root Complex Driver Power Managment ++** COPYRIGHT : Copyright (c) 2009 ++** Lantiq Deutschland GmbH ++** Am Campeon 3, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** ++** HISTORY ++** $Date $Author $Comment ++** 21 Dec,2009 Lei Chuanhua First UEIP release ++*******************************************************************************/ ++/*! ++ \defgroup IFX_PCIE_PM Power Management functions ++ \ingroup IFX_PCIE ++ \brief IFX PCIE Root Complex Driver power management functions ++*/ ++ ++/*! ++ \file ifxmips_pcie_pm.c ++ \ingroup IFX_PCIE ++ \brief source file for PCIE Root Complex Driver Power Management ++*/ ++ ++#ifndef EXPORT_SYMTAB ++#define EXPORT_SYMTAB ++#endif ++#ifndef AUTOCONF_INCLUDED ++#include <linux/config.h> ++#endif /* AUTOCONF_INCLUDED */ ++#include <linux/version.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <asm/system.h> ++ ++/* Project header */ ++#include <asm/ifx/ifx_types.h> ++#include <asm/ifx/ifx_regs.h> ++#include <asm/ifx/common_routines.h> ++#include <asm/ifx/ifx_pmcu.h> ++#include "ifxmips_pcie_pm.h" ++ ++/** ++ * \fn static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) ++ * \brief the callback function to request pmcu state in the power management hardware-dependent module ++ * ++ * \param pmcuState This parameter is a PMCU state. ++ * ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \return IFX_PMCU_RETURN_DENIED Not allowed to operate power state ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_state_change(IFX_PMCU_STATE_t pmcuState) ++{ ++ switch(pmcuState) ++ { ++ case IFX_PMCU_STATE_D0: ++ return IFX_PMCU_RETURN_SUCCESS; ++ case IFX_PMCU_STATE_D1: // Not Applicable ++ return IFX_PMCU_RETURN_DENIED; ++ case IFX_PMCU_STATE_D2: // Not Applicable ++ return IFX_PMCU_RETURN_DENIED; ++ case IFX_PMCU_STATE_D3: // Module clock gating and Power gating ++ return IFX_PMCU_RETURN_SUCCESS; ++ default: ++ return IFX_PMCU_RETURN_DENIED; ++ } ++} ++ ++/** ++ * \fn static IFX_PMCU_RETURN_t ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) ++ * \brief the callback function to get pmcu state in the power management hardware-dependent module ++ ++ * \param pmcuState Pointer to return power state. ++ * ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \return IFX_PMCU_RETURN_DENIED Not allowed to operate power state ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_state_get(IFX_PMCU_STATE_t *pmcuState) ++{ ++ return IFX_PMCU_RETURN_SUCCESS; ++} ++ ++/** ++ * \fn IFX_PMCU_RETURN_t ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++ * \brief Apply all callbacks registered to be executed before a state change for pmcuModule ++ * ++ * \param pmcuModule Module ++ * \param newState New state ++ * \param oldState Old state ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_prechange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++{ ++ return IFX_PMCU_RETURN_SUCCESS; ++} ++ ++/** ++ * \fn IFX_PMCU_RETURN_t ifx_pcie_pmcu_postchange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++ * \brief Apply all callbacks registered to be executed before a state change for pmcuModule ++ * ++ * \param pmcuModule Module ++ * \param newState New state ++ * \param oldState Old state ++ * \return IFX_PMCU_RETURN_SUCCESS Set Power State successfully ++ * \return IFX_PMCU_RETURN_ERROR Failed to set power state. ++ * \ingroup IFX_PCIE_PM ++ */ ++static IFX_PMCU_RETURN_t ++ifx_pcie_pmcu_postchange(IFX_PMCU_MODULE_t pmcuModule, IFX_PMCU_STATE_t newState, IFX_PMCU_STATE_t oldState) ++{ ++ return IFX_PMCU_RETURN_SUCCESS; ++} ++ ++/** ++ * \fn static void ifx_pcie_pmcu_init(void) ++ * \brief Register with central PMCU module ++ * \return none ++ * \ingroup IFX_PCIE_PM ++ */ ++void ++ifx_pcie_pmcu_init(void) ++{ ++ IFX_PMCU_REGISTER_t pmcuRegister; ++ ++ /* XXX, hook driver context */ ++ ++ /* State function register */ ++ memset(&pmcuRegister, 0, sizeof(IFX_PMCU_REGISTER_t)); ++ pmcuRegister.pmcuModule = IFX_PMCU_MODULE_PCIE; ++ pmcuRegister.pmcuModuleNr = 0; ++ pmcuRegister.ifx_pmcu_state_change = ifx_pcie_pmcu_state_change; ++ pmcuRegister.ifx_pmcu_state_get = ifx_pcie_pmcu_state_get; ++ pmcuRegister.pre = ifx_pcie_pmcu_prechange; ++ pmcuRegister.post= ifx_pcie_pmcu_postchange; ++ ifx_pmcu_register(&pmcuRegister); ++} ++ ++/** ++ * \fn static void ifx_pcie_pmcu_exit(void) ++ * \brief Unregister with central PMCU module ++ * ++ * \return none ++ * \ingroup IFX_PCIE_PM ++ */ ++void ++ifx_pcie_pmcu_exit(void) ++{ ++ IFX_PMCU_REGISTER_t pmcuUnRegister; ++ ++ /* XXX, hook driver context */ ++ ++ pmcuUnRegister.pmcuModule = IFX_PMCU_MODULE_PCIE; ++ pmcuUnRegister.pmcuModuleNr = 0; ++ ifx_pmcu_unregister(&pmcuUnRegister); ++} ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_pm.h +@@ -0,0 +1,36 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_pm.h ++** PROJECT : IFX UEIP ++** MODULES : PCIe Root Complex Driver ++** ++** DATE : 21 Dec 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver Power Managment ++** COPYRIGHT : Copyright (c) 2009 ++** Lantiq Deutschland GmbH ++** Am Campeon 3, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** ++** HISTORY ++** $Date $Author $Comment ++** 21 Dec,2009 Lei Chuanhua First UEIP release ++*******************************************************************************/ ++/*! ++ \file ifxmips_pcie_pm.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe Root Complex Driver Power Management ++*/ ++ ++#ifndef IFXMIPS_PCIE_PM_H ++#define IFXMIPS_PCIE_PM_H ++ ++void ifx_pcie_pmcu_init(void); ++void ifx_pcie_pmcu_exit(void); ++ ++#endif /* IFXMIPS_PCIE_PM_H */ ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_reg.h +@@ -0,0 +1,1001 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_reg.h ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++#ifndef IFXMIPS_PCIE_REG_H ++#define IFXMIPS_PCIE_REG_H ++/*! ++ \file ifxmips_pcie_reg.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe module register definition ++*/ ++/* PCIe Address Mapping Base */ ++#define PCIE_CFG_PHY_BASE 0x1D000000UL ++#define PCIE_CFG_BASE (KSEG1 + PCIE_CFG_PHY_BASE) ++#define PCIE_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE_MEM_PHY_BASE 0x1C000000UL ++#define PCIE_MEM_BASE (KSEG1 + PCIE_MEM_PHY_BASE) ++#define PCIE_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE_MEM_PHY_END (PCIE_MEM_PHY_BASE + PCIE_MEM_SIZE - 1) ++ ++#define PCIE_IO_PHY_BASE 0x1D800000UL ++#define PCIE_IO_BASE (KSEG1 + PCIE_IO_PHY_BASE) ++#define PCIE_IO_SIZE (1 * 1024 * 1024) ++#define PCIE_IO_PHY_END (PCIE_IO_PHY_BASE + PCIE_IO_SIZE - 1) ++ ++#define PCIE_RC_CFG_BASE (KSEG1 + 0x1D900000) ++#define PCIE_APP_LOGIC_REG (KSEG1 + 0x1E100900) ++#define PCIE_MSI_PHY_BASE 0x1F600000UL ++ ++#define PCIE_PDI_PHY_BASE 0x1F106800UL ++#define PCIE_PDI_BASE (KSEG1 + PCIE_PDI_PHY_BASE) ++#define PCIE_PDI_SIZE 0x400 ++ ++#define PCIE1_CFG_PHY_BASE 0x19000000UL ++#define PCIE1_CFG_BASE (KSEG1 + PCIE1_CFG_PHY_BASE) ++#define PCIE1_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE1_MEM_PHY_BASE 0x18000000UL ++#define PCIE1_MEM_BASE (KSEG1 + PCIE1_MEM_PHY_BASE) ++#define PCIE1_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE1_MEM_PHY_END (PCIE1_MEM_PHY_BASE + PCIE1_MEM_SIZE - 1) ++ ++#define PCIE1_IO_PHY_BASE 0x19800000UL ++#define PCIE1_IO_BASE (KSEG1 + PCIE1_IO_PHY_BASE) ++#define PCIE1_IO_SIZE (1 * 1024 * 1024) ++#define PCIE1_IO_PHY_END (PCIE1_IO_PHY_BASE + PCIE1_IO_SIZE - 1) ++ ++#define PCIE1_RC_CFG_BASE (KSEG1 + 0x19900000) ++#define PCIE1_APP_LOGIC_REG (KSEG1 + 0x1E100700) ++#define PCIE1_MSI_PHY_BASE 0x1F400000UL ++ ++#define PCIE1_PDI_PHY_BASE 0x1F700400UL ++#define PCIE1_PDI_BASE (KSEG1 + PCIE1_PDI_PHY_BASE) ++#define PCIE1_PDI_SIZE 0x400 ++ ++#define PCIE_CFG_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_CFG_BASE) : (PCIE_CFG_BASE)) ++#define PCIE_MEM_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_BASE) : (PCIE_MEM_BASE)) ++#define PCIE_IO_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_BASE) : (PCIE_IO_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_PHY_BASE) : (PCIE_MEM_PHY_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_MEM_PHY_END) : (PCIE_MEM_PHY_END)) ++#define PCIE_IO_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_PHY_BASE) : (PCIE_IO_PHY_BASE)) ++#define PCIE_IO_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_IO_PHY_END) : (PCIE_IO_PHY_END)) ++#define PCIE_APP_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_APP_LOGIC_REG) : (PCIE_APP_LOGIC_REG)) ++#define PCIE_RC_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_RC_CFG_BASE) : (PCIE_RC_CFG_BASE)) ++#define PCIE_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_PDI_BASE) : (PCIE_PDI_BASE)) ++ ++/* PCIe Application Logic Register */ ++/* RC Core Control Register */ ++#define PCIE_RC_CCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x10) ++/* This should be enabled after initializing configuratin registers ++ * Also should check link status retraining bit ++ */ ++#define PCIE_RC_CCR_LTSSM_ENABLE 0x00000001 /* Enable LTSSM to continue link establishment */ ++ ++/* RC Core Debug Register */ ++#define PCIE_RC_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x14) ++#define PCIE_RC_DR_DLL_UP 0x00000001 /* Data Link Layer Up */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE 0x0000000E /* Current Power State */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE_S 1 ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE 0x000001F0 /* Current LTSSM State */ ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE_S 4 ++ ++#define PCIE_RC_DR_PM_DEV_STATE 0x00000E00 /* Power Management D-State */ ++#define PCIE_RC_DR_PM_DEV_STATE_S 9 ++ ++#define PCIE_RC_DR_PM_ENABLED 0x00001000 /* Power Management State from PMU */ ++#define PCIE_RC_DR_PME_EVENT_ENABLED 0x00002000 /* Power Management Event Enable State */ ++#define PCIE_RC_DR_AUX_POWER_ENABLED 0x00004000 /* Auxiliary Power Enable */ ++ ++/* Current Power State Definition */ ++enum { ++ PCIE_RC_DR_D0 = 0, ++ PCIE_RC_DR_D1, /* Not supported */ ++ PCIE_RC_DR_D2, /* Not supported */ ++ PCIE_RC_DR_D3, ++ PCIE_RC_DR_UN, ++}; ++ ++/* PHY Link Status Register */ ++#define PCIE_PHY_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x18) ++#define PCIE_PHY_SR_PHY_LINK_UP 0x00000001 /* PHY Link Up/Down Indicator */ ++ ++/* Electromechanical Control Register */ ++#define PCIE_EM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x1C) ++#define PCIE_EM_CR_CARD_IS_PRESENT 0x00000001 /* Card Presence Detect State */ ++#define PCIE_EM_CR_MRL_OPEN 0x00000002 /* MRL Sensor State */ ++#define PCIE_EM_CR_POWER_FAULT_SET 0x00000004 /* Power Fault Detected */ ++#define PCIE_EM_CR_MRL_SENSOR_SET 0x00000008 /* MRL Sensor Changed */ ++#define PCIE_EM_CR_PRESENT_DETECT_SET 0x00000010 /* Card Presense Detect Changed */ ++#define PCIE_EM_CR_CMD_CPL_INT_SET 0x00000020 /* Command Complete Interrupt */ ++#define PCIE_EM_CR_SYS_INTERLOCK_SET 0x00000040 /* System Electromechanical IterLock Engaged */ ++#define PCIE_EM_CR_ATTENTION_BUTTON_SET 0x00000080 /* Attention Button Pressed */ ++ ++/* Interrupt Status Register */ ++#define PCIE_IR_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x20) ++#define PCIE_IR_SR_PME_CAUSE_MSI 0x00000002 /* MSI caused by PME */ ++#define PCIE_IR_SR_HP_PME_WAKE_GEN 0x00000004 /* Hotplug PME Wake Generation */ ++#define PCIE_IR_SR_HP_MSI 0x00000008 /* Hotplug MSI */ ++#define PCIE_IR_SR_AHB_LU_ERR 0x00000030 /* AHB Bridge Lookup Error Signals */ ++#define PCIE_IR_SR_AHB_LU_ERR_S 4 ++#define PCIE_IR_SR_INT_MSG_NUM 0x00003E00 /* Interrupt Message Number */ ++#define PCIE_IR_SR_INT_MSG_NUM_S 9 ++#define PCIE_IR_SR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_IR_SR_AER_INT_MSG_NUM_S 27 ++ ++/* Message Control Register */ ++#define PCIE_MSG_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x30) ++#define PCIE_MSG_CR_GEN_PME_TURN_OFF_MSG 0x00000001 /* Generate PME Turn Off Message */ ++#define PCIE_MSG_CR_GEN_UNLOCK_MSG 0x00000002 /* Generate Unlock Message */ ++ ++#define PCIE_VDM_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x34) ++ ++/* Vendor-Defined Message Requester ID Register */ ++#define PCIE_VDM_RID(X) (PCIE_APP_PORT_TO_BASE (X) + 0x38) ++#define PCIE_VDM_RID_VENROR_MSG_REQ_ID 0x0000FFFF ++#define PCIE_VDM_RID_VDMRID_S 0 ++ ++/* ASPM Control Register */ ++#define PCIE_ASPM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x40) ++#define PCIE_ASPM_CR_HOT_RST 0x00000001 /* Hot Reset Request to the downstream device */ ++#define PCIE_ASPM_CR_REQ_EXIT_L1 0x00000002 /* Request to Exit L1 */ ++#define PCIE_ASPM_CR_REQ_ENTER_L1 0x00000004 /* Request to Enter L1 */ ++ ++/* Vendor Message DW0 Register */ ++#define PCIE_VM_MSG_DW0(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x50) ++#define PCIE_VM_MSG_DW0_TYPE 0x0000001F /* Message type */ ++#define PCIE_VM_MSG_DW0_TYPE_S 0 ++#define PCIE_VM_MSG_DW0_FORMAT 0x00000060 /* Format */ ++#define PCIE_VM_MSG_DW0_FORMAT_S 5 ++#define PCIE_VM_MSG_DW0_TC 0x00007000 /* Traffic Class */ ++#define PCIE_VM_MSG_DW0_TC_S 12 ++#define PCIE_VM_MSG_DW0_ATTR 0x000C0000 /* Atrributes */ ++#define PCIE_VM_MSG_DW0_ATTR_S 18 ++#define PCIE_VM_MSG_DW0_EP_TLP 0x00100000 /* Poisoned TLP */ ++#define PCIE_VM_MSG_DW0_TD 0x00200000 /* TLP Digest */ ++#define PCIE_VM_MSG_DW0_LEN 0xFFC00000 /* Length */ ++#define PCIE_VM_MSG_DW0_LEN_S 22 ++ ++/* Format Definition */ ++enum { ++ PCIE_VM_MSG_FORMAT_00 = 0, /* 3DW Hdr, no data*/ ++ PCIE_VM_MSG_FORMAT_01, /* 4DW Hdr, no data */ ++ PCIE_VM_MSG_FORMAT_10, /* 3DW Hdr, with data */ ++ PCIE_VM_MSG_FORMAT_11, /* 4DW Hdr, with data */ ++}; ++ ++/* Traffic Class Definition */ ++enum { ++ PCIE_VM_MSG_TC0 = 0, ++ PCIE_VM_MSG_TC1, ++ PCIE_VM_MSG_TC2, ++ PCIE_VM_MSG_TC3, ++ PCIE_VM_MSG_TC4, ++ PCIE_VM_MSG_TC5, ++ PCIE_VM_MSG_TC6, ++ PCIE_VM_MSG_TC7, ++}; ++ ++/* Attributes Definition */ ++enum { ++ PCIE_VM_MSG_ATTR_00 = 0, /* RO and No Snoop cleared */ ++ PCIE_VM_MSG_ATTR_01, /* RO cleared , No Snoop set */ ++ PCIE_VM_MSG_ATTR_10, /* RO set, No Snoop cleared*/ ++ PCIE_VM_MSG_ATTR_11, /* RO and No Snoop set */ ++}; ++ ++/* Payload Size Definition */ ++#define PCIE_VM_MSG_LEN_MIN 0 ++#define PCIE_VM_MSG_LEN_MAX 1024 ++ ++/* Vendor Message DW1 Register */ ++#define PCIE_VM_MSG_DW1(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x54) ++#define PCIE_VM_MSG_DW1_FUNC_NUM 0x00000070 /* Function Number */ ++#define PCIE_VM_MSG_DW1_FUNC_NUM_S 8 ++#define PCIE_VM_MSG_DW1_CODE 0x00FF0000 /* Message Code */ ++#define PCIE_VM_MSG_DW1_CODE_S 16 ++#define PCIE_VM_MSG_DW1_TAG 0xFF000000 /* Tag */ ++#define PCIE_VM_MSG_DW1_TAG_S 24 ++ ++#define PCIE_VM_MSG_DW2(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x58) ++#define PCIE_VM_MSG_DW3(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x5C) ++ ++/* Vendor Message Request Register */ ++#define PCIE_VM_MSG_REQR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x60) ++#define PCIE_VM_MSG_REQR_REQ 0x00000001 /* Vendor Message Request */ ++ ++ ++/* AHB Slave Side Band Control Register */ ++#define PCIE_AHB_SSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x70) ++#define PCIE_AHB_SSB_REQ_BCM 0x00000001 /* Slave Reques BCM filed */ ++#define PCIE_AHB_SSB_REQ_EP 0x00000002 /* Slave Reques EP filed */ ++#define PCIE_AHB_SSB_REQ_TD 0x00000004 /* Slave Reques TD filed */ ++#define PCIE_AHB_SSB_REQ_ATTR 0x00000018 /* Slave Reques Attribute number */ ++#define PCIE_AHB_SSB_REQ_ATTR_S 3 ++#define PCIE_AHB_SSB_REQ_TC 0x000000E0 /* Slave Request TC Field */ ++#define PCIE_AHB_SSB_REQ_TC_S 5 ++ ++/* AHB Master SideBand Ctrl Register */ ++#define PCIE_AHB_MSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x74) ++#define PCIE_AHB_MSB_RESP_ATTR 0x00000003 /* Master Response Attribute number */ ++#define PCIE_AHB_MSB_RESP_ATTR_S 0 ++#define PCIE_AHB_MSB_RESP_BAD_EOT 0x00000004 /* Master Response Badeot filed */ ++#define PCIE_AHB_MSB_RESP_BCM 0x00000008 /* Master Response BCM filed */ ++#define PCIE_AHB_MSB_RESP_EP 0x00000010 /* Master Response EP filed */ ++#define PCIE_AHB_MSB_RESP_TD 0x00000020 /* Master Response TD filed */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM 0x000003C0 /* Master Response Function number */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM_S 6 ++ ++/* AHB Control Register, fixed bus enumeration exception */ ++#define PCIE_AHB_CTRL(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x78) ++#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 ++ ++/* Interrupt Enalbe Register */ ++#define PCIE_IRNEN(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF4) ++#define PCIE_IRNCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF8) ++#define PCIE_IRNICR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xFC) ++ ++/* PCIe interrupt enable/control/capture register definition */ ++#define PCIE_IRN_AER_REPORT 0x00000001 /* AER Interrupt */ ++#define PCIE_IRN_AER_MSIX 0x00000002 /* Advanced Error MSI-X Interrupt */ ++#define PCIE_IRN_PME 0x00000004 /* PME Interrupt */ ++#define PCIE_IRN_HOTPLUG 0x00000008 /* Hotplug Interrupt */ ++#define PCIE_IRN_RX_VDM_MSG 0x00000010 /* Vendor-Defined Message Interrupt */ ++#define PCIE_IRN_RX_CORRECTABLE_ERR_MSG 0x00000020 /* Correctable Error Message Interrupt */ ++#define PCIE_IRN_RX_NON_FATAL_ERR_MSG 0x00000040 /* Non-fatal Error Message */ ++#define PCIE_IRN_RX_FATAL_ERR_MSG 0x00000080 /* Fatal Error Message */ ++#define PCIE_IRN_RX_PME_MSG 0x00000100 /* PME Message Interrupt */ ++#define PCIE_IRN_RX_PME_TURNOFF_ACK 0x00000200 /* PME Turnoff Ack Message Interrupt */ ++#define PCIE_IRN_AHB_BR_FATAL_ERR 0x00000400 /* AHB Fatal Error Interrupt */ ++#define PCIE_IRN_LINK_AUTO_BW_STATUS 0x00000800 /* Link Auto Bandwidth Status Interrupt */ ++#define PCIE_IRN_BW_MGT 0x00001000 /* Bandwidth Managment Interrupt */ ++#define PCIE_IRN_INTA 0x00002000 /* INTA */ ++#define PCIE_IRN_INTB 0x00004000 /* INTB */ ++#define PCIE_IRN_INTC 0x00008000 /* INTC */ ++#define PCIE_IRN_INTD 0x00010000 /* INTD */ ++#define PCIE_IRN_WAKEUP 0x00020000 /* Wake up Interrupt */ ++ ++#define PCIE_RC_CORE_COMBINED_INT (PCIE_IRN_AER_REPORT | PCIE_IRN_AER_MSIX | PCIE_IRN_PME | \ ++ PCIE_IRN_HOTPLUG | PCIE_IRN_RX_VDM_MSG | PCIE_IRN_RX_CORRECTABLE_ERR_MSG |\ ++ PCIE_IRN_RX_NON_FATAL_ERR_MSG | PCIE_IRN_RX_FATAL_ERR_MSG | \ ++ PCIE_IRN_RX_PME_MSG | PCIE_IRN_RX_PME_TURNOFF_ACK | PCIE_IRN_AHB_BR_FATAL_ERR | \ ++ PCIE_IRN_LINK_AUTO_BW_STATUS | PCIE_IRN_BW_MGT) ++/* PCIe RC Configuration Register */ ++#define PCIE_VDID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x00) ++ ++/* Bit definition from pci_reg.h */ ++#define PCIE_PCICMDSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x04) ++#define PCIE_CCRID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x08) ++#define PCIE_CLSLTHTBR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x0C) /* EP only */ ++/* BAR0, BAR1,Only necessary if the bridges implements a device-specific register set or memory buffer */ ++#define PCIE_BAR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10) /* Not used*/ ++#define PCIE_BAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14) /* Not used */ ++ ++#define PCIE_BNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x18) /* Mandatory */ ++/* Bus Number Register bits */ ++#define PCIE_BNR_PRIMARY_BUS_NUM 0x000000FF ++#define PCIE_BNR_PRIMARY_BUS_NUM_S 0 ++#define PCIE_PNR_SECONDARY_BUS_NUM 0x0000FF00 ++#define PCIE_PNR_SECONDARY_BUS_NUM_S 8 ++#define PCIE_PNR_SUB_BUS_NUM 0x00FF0000 ++#define PCIE_PNR_SUB_BUS_NUM_S 16 ++ ++/* IO Base/Limit Register bits */ ++#define PCIE_IOBLSECS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x1C) /* RC only */ ++#define PCIE_IOBLSECS_32BIT_IO_ADDR 0x00000001 ++#define PCIE_IOBLSECS_IO_BASE_ADDR 0x000000F0 ++#define PCIE_IOBLSECS_IO_BASE_ADDR_S 4 ++#define PCIE_IOBLSECS_32BIT_IOLIMT 0x00000100 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR 0x0000F000 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR_S 12 ++ ++/* Non-prefetchable Memory Base/Limit Register bit */ ++#define PCIE_MBML(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x20) /* RC only */ ++#define PCIE_MBML_MEM_BASE_ADDR 0x0000FFF0 ++#define PCIE_MBML_MEM_BASE_ADDR_S 4 ++#define PCIE_MBML_MEM_LIMIT_ADDR 0xFFF00000 ++#define PCIE_MBML_MEM_LIMIT_ADDR_S 20 ++ ++/* Prefetchable Memory Base/Limit Register bit */ ++#define PCIE_PMBL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x24) /* RC only */ ++#define PCIE_PMBL_64BIT_ADDR 0x00000001 ++#define PCIE_PMBL_UPPER_12BIT 0x0000FFF0 ++#define PCIE_PMBL_UPPER_12BIT_S 4 ++#define PCIE_PMBL_E64MA 0x00010000 ++#define PCIE_PMBL_END_ADDR 0xFFF00000 ++#define PCIE_PMBL_END_ADDR_S 20 ++#define PCIE_PMBU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x28) /* RC only */ ++#define PCIE_PMLU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x2C) /* RC only */ ++ ++/* I/O Base/Limit Upper 16 bits register */ ++#define PCIE_IO_BANDL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x30) /* RC only */ ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE 0x0000FFFF ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE_S 0 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT 0xFFFF0000 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT_S 16 ++ ++#define PCIE_CPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x34) ++#define PCIE_EBBAR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x38) ++ ++/* Interrupt and Secondary Bridge Control Register */ ++#define PCIE_INTRBCTRL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x3C) ++ ++#define PCIE_INTRBCTRL_INT_LINE 0x000000FF ++#define PCIE_INTRBCTRL_INT_LINE_S 0 ++#define PCIE_INTRBCTRL_INT_PIN 0x0000FF00 ++#define PCIE_INTRBCTRL_INT_PIN_S 8 ++#define PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE 0x00010000 /* #PERR */ ++#define PCIE_INTRBCTRL_SERR_ENABLE 0x00020000 /* #SERR */ ++#define PCIE_INTRBCTRL_ISA_ENABLE 0x00040000 /* ISA enable, IO 64KB only */ ++#define PCIE_INTRBCTRL_VGA_ENABLE 0x00080000 /* VGA enable */ ++#define PCIE_INTRBCTRL_VGA_16BIT_DECODE 0x00100000 /* VGA 16bit decode */ ++#define PCIE_INTRBCTRL_RST_SECONDARY_BUS 0x00400000 /* Secondary bus rest, hot rest, 1ms */ ++/* Others are read only */ ++enum { ++ PCIE_INTRBCTRL_INT_NON = 0, ++ PCIE_INTRBCTRL_INTA, ++ PCIE_INTRBCTRL_INTB, ++ PCIE_INTRBCTRL_INTC, ++ PCIE_INTRBCTRL_INTD, ++}; ++ ++#define PCIE_PM_CAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x40) ++ ++/* Power Management Control and Status Register */ ++#define PCIE_PM_CSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x44) ++ ++#define PCIE_PM_CSR_POWER_STATE 0x00000003 /* Power State */ ++#define PCIE_PM_CSR_POWER_STATE_S 0 ++#define PCIE_PM_CSR_SW_RST 0x00000008 /* Soft Reset Enabled */ ++#define PCIE_PM_CSR_PME_ENABLE 0x00000100 /* PME Enable */ ++#define PCIE_PM_CSR_PME_STATUS 0x00008000 /* PME status */ ++ ++/* MSI Capability Register for EP */ ++#define PCIE_MCAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x50) ++ ++#define PCIE_MCAPR_MSI_CAP_ID 0x000000FF /* MSI Capability ID */ ++#define PCIE_MCAPR_MSI_CAP_ID_S 0 ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR_S 8 ++#define PCIE_MCAPR_MSI_ENABLE 0x00010000 /* MSI Enable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP 0x000E0000 /* Multiple Message Capable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP_S 17 ++#define PCIE_MCAPR_MULTI_MSG_ENABLE 0x00700000 /* Multiple Message Enable */ ++#define PCIE_MCAPR_MULTI_MSG_ENABLE_S 20 ++#define PCIE_MCAPR_ADDR64_CAP 0X00800000 /* 64-bit Address Capable */ ++ ++/* MSI Message Address Register */ ++#define PCIE_MA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x54) ++ ++#define PCIE_MA_ADDR_MASK 0xFFFFFFFC /* Message Address */ ++ ++/* MSI Message Upper Address Register */ ++#define PCIE_MUA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x58) ++ ++/* MSI Message Data Register */ ++#define PCIE_MD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x5C) ++ ++#define PCIE_MD_DATA 0x0000FFFF /* Message Data */ ++#define PCIE_MD_DATA_S 0 ++ ++/* PCI Express Capability Register */ ++#define PCIE_XCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70) ++ ++#define PCIE_XCAP_ID 0x000000FF /* PCI Express Capability ID */ ++#define PCIE_XCAP_ID_S 0 ++#define PCIE_XCAP_NEXT_CAP 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_XCAP_NEXT_CAP_S 8 ++#define PCIE_XCAP_VER 0x000F0000 /* PCI Express Capability Version */ ++#define PCIE_XCAP_VER_S 16 ++#define PCIE_XCAP_DEV_PORT_TYPE 0x00F00000 /* Device Port Type */ ++#define PCIE_XCAP_DEV_PORT_TYPE_S 20 ++#define PCIE_XCAP_SLOT_IMPLEMENTED 0x01000000 /* Slot Implemented */ ++#define PCIE_XCAP_MSG_INT_NUM 0x3E000000 /* Interrupt Message Number */ ++#define PCIE_XCAP_MSG_INT_NUM_S 25 ++ ++/* Device Capability Register */ ++#define PCIE_DCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74) ++ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE 0x00000007 /* Max Payload size */ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE_S 0 ++#define PCIE_DCAP_PHANTOM_FUNC 0x00000018 /* Phanton Function, not supported */ ++#define PCIE_DCAP_PHANTOM_FUNC_S 3 ++#define PCIE_DCAP_EXT_TAG 0x00000020 /* Extended Tag Field */ ++#define PCIE_DCAP_EP_L0S_LATENCY 0x000001C0 /* EP L0s latency only */ ++#define PCIE_DCAP_EP_L0S_LATENCY_S 6 ++#define PCIE_DCAP_EP_L1_LATENCY 0x00000E00 /* EP L1 latency only */ ++#define PCIE_DCAP_EP_L1_LATENCY_S 9 ++#define PCIE_DCAP_ROLE_BASE_ERR_REPORT 0x00008000 /* Role Based ERR */ ++ ++/* Maximum payload size supported */ ++enum { ++ PCIE_MAX_PAYLOAD_128 = 0, ++ PCIE_MAX_PAYLOAD_256, ++ PCIE_MAX_PAYLOAD_512, ++ PCIE_MAX_PAYLOAD_1024, ++ PCIE_MAX_PAYLOAD_2048, ++ PCIE_MAX_PAYLOAD_4096, ++}; ++ ++/* Device Control and Status Register */ ++#define PCIE_DCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x78) ++ ++#define PCIE_DCTLSTS_CORRECTABLE_ERR_EN 0x00000001 /* COR-ERR */ ++#define PCIE_DCTLSTS_NONFATAL_ERR_EN 0x00000002 /* Non-fatal ERR */ ++#define PCIE_DCTLSTS_FATAL_ERR_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_DCTLSYS_UR_REQ_EN 0x00000008 /* UR ERR */ ++#define PCIE_DCTLSTS_RELAXED_ORDERING_EN 0x00000010 /* Enable relaxing ordering */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE 0x000000E0 /* Max payload mask */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE_S 5 ++#define PCIE_DCTLSTS_EXT_TAG_EN 0x00000100 /* Extended tag field */ ++#define PCIE_DCTLSTS_PHANTOM_FUNC_EN 0x00000200 /* Phantom Function Enable */ ++#define PCIE_DCTLSTS_AUX_PM_EN 0x00000400 /* AUX Power PM Enable */ ++#define PCIE_DCTLSTS_NO_SNOOP_EN 0x00000800 /* Enable no snoop, except root port*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE 0x00007000 /* Max Read Request size*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE_S 12 ++#define PCIE_DCTLSTS_CORRECTABLE_ERR 0x00010000 /* COR-ERR Detected */ ++#define PCIE_DCTLSTS_NONFATAL_ERR 0x00020000 /* Non-Fatal ERR Detected */ ++#define PCIE_DCTLSTS_FATAL_ER 0x00040000 /* Fatal ERR Detected */ ++#define PCIE_DCTLSTS_UNSUPPORTED_REQ 0x00080000 /* UR Detected */ ++#define PCIE_DCTLSTS_AUX_POWER 0x00100000 /* Aux Power Detected */ ++#define PCIE_DCTLSTS_TRANSACT_PENDING 0x00200000 /* Transaction pending */ ++ ++#define PCIE_DCTLSTS_ERR_EN (PCIE_DCTLSTS_CORRECTABLE_ERR_EN | \ ++ PCIE_DCTLSTS_NONFATAL_ERR_EN | PCIE_DCTLSTS_FATAL_ERR_EN | \ ++ PCIE_DCTLSYS_UR_REQ_EN) ++ ++/* Link Capability Register */ ++#define PCIE_LCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7C) ++#define PCIE_LCAP_MAX_LINK_SPEED 0x0000000F /* Max link speed, 0x1 by default */ ++#define PCIE_LCAP_MAX_LINK_SPEED_S 0 ++#define PCIE_LCAP_MAX_LENGTH_WIDTH 0x000003F0 /* Maxium Length Width */ ++#define PCIE_LCAP_MAX_LENGTH_WIDTH_S 4 ++#define PCIE_LCAP_ASPM_LEVEL 0x00000C00 /* Active State Link PM Support */ ++#define PCIE_LCAP_ASPM_LEVEL_S 10 ++#define PCIE_LCAP_L0S_EIXT_LATENCY 0x00007000 /* L0s Exit Latency */ ++#define PCIE_LCAP_L0S_EIXT_LATENCY_S 12 ++#define PCIE_LCAP_L1_EXIT_LATENCY 0x00038000 /* L1 Exit Latency */ ++#define PCIE_LCAP_L1_EXIT_LATENCY_S 15 ++#define PCIE_LCAP_CLK_PM 0x00040000 /* Clock Power Management */ ++#define PCIE_LCAP_SDER 0x00080000 /* Surprise Down Error Reporting */ ++#define PCIE_LCAP_DLL_ACTIVE_REPROT 0x00100000 /* Data Link Layer Active Reporting Capable */ ++#define PCIE_LCAP_PORT_NUM 0xFF0000000 /* Port number */ ++#define PCIE_LCAP_PORT_NUM_S 24 ++ ++/* Maximum Length width definition */ ++#define PCIE_MAX_LENGTH_WIDTH_RES 0x00 ++#define PCIE_MAX_LENGTH_WIDTH_X1 0x01 /* Default */ ++#define PCIE_MAX_LENGTH_WIDTH_X2 0x02 ++#define PCIE_MAX_LENGTH_WIDTH_X4 0x04 ++#define PCIE_MAX_LENGTH_WIDTH_X8 0x08 ++#define PCIE_MAX_LENGTH_WIDTH_X12 0x0C ++#define PCIE_MAX_LENGTH_WIDTH_X16 0x10 ++#define PCIE_MAX_LENGTH_WIDTH_X32 0x20 ++ ++/* Active State Link PM definition */ ++enum { ++ PCIE_ASPM_RES0 = 0, ++ PCIE_ASPM_L0S_ENTRY_SUPPORT, /* L0s */ ++ PCIE_ASPM_RES1, ++ PCIE_ASPM_L0S_L1_ENTRY_SUPPORT, /* L0s and L1, default */ ++}; ++ ++/* L0s Exit Latency definition */ ++enum { ++ PCIE_L0S_EIXT_LATENCY_L64NS = 0, /* < 64 ns */ ++ PCIE_L0S_EIXT_LATENCY_B64A128, /* > 64 ns < 128 ns */ ++ PCIE_L0S_EIXT_LATENCY_B128A256, /* > 128 ns < 256 ns */ ++ PCIE_L0S_EIXT_LATENCY_B256A512, /* > 256 ns < 512 ns */ ++ PCIE_L0S_EIXT_LATENCY_B512TO1U, /* > 512 ns < 1 us */ ++ PCIE_L0S_EIXT_LATENCY_B1A2U, /* > 1 us < 2 us */ ++ PCIE_L0S_EIXT_LATENCY_B2A4U, /* > 2 us < 4 us */ ++ PCIE_L0S_EIXT_LATENCY_M4US, /* > 4 us */ ++}; ++ ++/* L1 Exit Latency definition */ ++enum { ++ PCIE_L1_EXIT_LATENCY_L1US = 0, /* < 1 us */ ++ PCIE_L1_EXIT_LATENCY_B1A2, /* > 1 us < 2 us */ ++ PCIE_L1_EXIT_LATENCY_B2A4, /* > 2 us < 4 us */ ++ PCIE_L1_EXIT_LATENCY_B4A8, /* > 4 us < 8 us */ ++ PCIE_L1_EXIT_LATENCY_B8A16, /* > 8 us < 16 us */ ++ PCIE_L1_EXIT_LATENCY_B16A32, /* > 16 us < 32 us */ ++ PCIE_L1_EXIT_LATENCY_B32A64, /* > 32 us < 64 us */ ++ PCIE_L1_EXIT_LATENCY_M64US, /* > 64 us */ ++}; ++ ++/* Link Control and Status Register */ ++#define PCIE_LCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x80) ++#define PCIE_LCTLSTS_ASPM_ENABLE 0x00000003 /* Active State Link PM Control */ ++#define PCIE_LCTLSTS_ASPM_ENABLE_S 0 ++#define PCIE_LCTLSTS_RCB128 0x00000008 /* Read Completion Boundary 128*/ ++#define PCIE_LCTLSTS_LINK_DISABLE 0x00000010 /* Link Disable */ ++#define PCIE_LCTLSTS_RETRIAN_LINK 0x00000020 /* Retrain Link */ ++#define PCIE_LCTLSTS_COM_CLK_CFG 0x00000040 /* Common Clock Configuration */ ++#define PCIE_LCTLSTS_EXT_SYNC 0x00000080 /* Extended Synch */ ++#define PCIE_LCTLSTS_CLK_PM_EN 0x00000100 /* Enable Clock Powerm Management */ ++#define PCIE_LCTLSTS_LINK_SPEED 0x000F0000 /* Link Speed */ ++#define PCIE_LCTLSTS_LINK_SPEED_S 16 ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH 0x03F00000 /* Negotiated Link Width */ ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH_S 20 ++#define PCIE_LCTLSTS_RETRAIN_PENDING 0x08000000 /* Link training is ongoing */ ++#define PCIE_LCTLSTS_SLOT_CLK_CFG 0x10000000 /* Slot Clock Configuration */ ++#define PCIE_LCTLSTS_DLL_ACTIVE 0x20000000 /* Data Link Layer Active */ ++ ++/* Slot Capabilities Register */ ++#define PCIE_SLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x84) ++ ++/* Slot Capabilities */ ++#define PCIE_SLCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x88) ++ ++/* Root Control and Capability Register */ ++#define PCIE_RCTLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x8C) ++#define PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR 0x00000001 /* #SERR on COR-ERR */ ++#define PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR 0x00000002 /* #SERR on Non-Fatal ERR */ ++#define PCIE_RCTLCAP_SERR_ON_FATAL_ERR 0x00000004 /* #SERR on Fatal ERR */ ++#define PCIE_RCTLCAP_PME_INT_EN 0x00000008 /* PME Interrupt Enable */ ++#define PCIE_RCTLCAP_SERR_ENABLE (PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR | \ ++ PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR | PCIE_RCTLCAP_SERR_ON_FATAL_ERR) ++/* Root Status Register */ ++#define PCIE_RSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x90) ++#define PCIE_RSTS_PME_REQ_ID 0x0000FFFF /* PME Request ID */ ++#define PCIE_RSTS_PME_REQ_ID_S 0 ++#define PCIE_RSTS_PME_STATUS 0x00010000 /* PME Status */ ++#define PCIE_RSTS_PME_PENDING 0x00020000 /* PME Pending */ ++ ++/* PCI Express Enhanced Capability Header */ ++#define PCIE_ENHANCED_CAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x100) ++#define PCIE_ENHANCED_CAP_ID 0x0000FFFF /* PCI Express Extended Capability ID */ ++#define PCIE_ENHANCED_CAP_ID_S 0 ++#define PCIE_ENHANCED_CAP_VER 0x000F0000 /* Capability Version */ ++#define PCIE_ENHANCED_CAP_VER_S 16 ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET 0xFFF00000 /* Next Capability Offset */ ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET_S 20 ++ ++/* Uncorrectable Error Status Register */ ++#define PCIE_UES_R(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x104) ++#define PCIE_DATA_LINK_PROTOCOL_ERR 0x00000010 /* Data Link Protocol Error Status */ ++#define PCIE_SURPRISE_DOWN_ERROR 0x00000020 /* Surprise Down Error Status */ ++#define PCIE_POISONED_TLP 0x00001000 /* Poisoned TLP Status */ ++#define PCIE_FC_PROTOCOL_ERR 0x00002000 /* Flow Control Protocol Error Status */ ++#define PCIE_COMPLETION_TIMEOUT 0x00004000 /* Completion Timeout Status */ ++#define PCIE_COMPLETOR_ABORT 0x00008000 /* Completer Abort Error */ ++#define PCIE_UNEXPECTED_COMPLETION 0x00010000 /* Unexpected Completion Status */ ++#define PCIE_RECEIVER_OVERFLOW 0x00020000 /* Receive Overflow Status */ ++#define PCIE_MALFORNED_TLP 0x00040000 /* Malformed TLP Stauts */ ++#define PCIE_ECRC_ERR 0x00080000 /* ECRC Error Stauts */ ++#define PCIE_UR_REQ 0x00100000 /* Unsupported Request Error Status */ ++#define PCIE_ALL_UNCORRECTABLE_ERR (PCIE_DATA_LINK_PROTOCOL_ERR | PCIE_SURPRISE_DOWN_ERROR | \ ++ PCIE_POISONED_TLP | PCIE_FC_PROTOCOL_ERR | PCIE_COMPLETION_TIMEOUT | \ ++ PCIE_COMPLETOR_ABORT | PCIE_UNEXPECTED_COMPLETION | PCIE_RECEIVER_OVERFLOW |\ ++ PCIE_MALFORNED_TLP | PCIE_ECRC_ERR | PCIE_UR_REQ) ++ ++/* Uncorrectable Error Mask Register, Mask means no report */ ++#define PCIE_UEMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x108) ++ ++/* Uncorrectable Error Severity Register */ ++#define PCIE_UESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10C) ++ ++/* Correctable Error Status Register */ ++#define PCIE_CESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x110) ++#define PCIE_RX_ERR 0x00000001 /* Receive Error Status */ ++#define PCIE_BAD_TLP 0x00000040 /* Bad TLP Status */ ++#define PCIE_BAD_DLLP 0x00000080 /* Bad DLLP Status */ ++#define PCIE_REPLAY_NUM_ROLLOVER 0x00000100 /* Replay Number Rollover Status */ ++#define PCIE_REPLAY_TIMER_TIMEOUT_ERR 0x00001000 /* Reply Timer Timeout Status */ ++#define PCIE_ADVISORY_NONFTAL_ERR 0x00002000 /* Advisory Non-Fatal Error Status */ ++#define PCIE_CORRECTABLE_ERR (PCIE_RX_ERR | PCIE_BAD_TLP | PCIE_BAD_DLLP | PCIE_REPLAY_NUM_ROLLOVER |\ ++ PCIE_REPLAY_TIMER_TIMEOUT_ERR | PCIE_ADVISORY_NONFTAL_ERR) ++ ++/* Correctable Error Mask Register */ ++#define PCIE_CEMR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x114) ++ ++/* Advanced Error Capabilities and Control Register */ ++#define PCIE_AECCR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x118) ++#define PCIE_AECCR_FIRST_ERR_PTR 0x0000001F /* First Error Pointer */ ++#define PCIE_AECCR_FIRST_ERR_PTR_S 0 ++#define PCIE_AECCR_ECRC_GEN_CAP 0x00000020 /* ECRC Generation Capable */ ++#define PCIE_AECCR_ECRC_GEN_EN 0x00000040 /* ECRC Generation Enable */ ++#define PCIE_AECCR_ECRC_CHECK_CAP 0x00000080 /* ECRC Check Capable */ ++#define PCIE_AECCR_ECRC_CHECK_EN 0x00000100 /* ECRC Check Enable */ ++ ++/* Header Log Register 1 */ ++#define PCIE_HLR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x11C) ++ ++/* Header Log Register 2 */ ++#define PCIE_HLR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x120) ++ ++/* Header Log Register 3 */ ++#define PCIE_HLR3(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x124) ++ ++/* Header Log Register 4 */ ++#define PCIE_HLR4(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x128) ++ ++/* Root Error Command Register */ ++#define PCIE_RECR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x12C) ++#define PCIE_RECR_CORRECTABLE_ERR_REPORT_EN 0x00000001 /* COR-ERR */ ++#define PCIE_RECR_NONFATAL_ERR_REPORT_EN 0x00000002 /* Non-Fatal ERR */ ++#define PCIE_RECR_FATAL_ERR_REPORT_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_RECR_ERR_REPORT_EN (PCIE_RECR_CORRECTABLE_ERR_REPORT_EN | \ ++ PCIE_RECR_NONFATAL_ERR_REPORT_EN | PCIE_RECR_FATAL_ERR_REPORT_EN) ++ ++/* Root Error Status Register */ ++#define PCIE_RESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x130) ++#define PCIE_RESR_CORRECTABLE_ERR 0x00000001 /* COR-ERR Receveid */ ++#define PCIE_RESR_MULTI_CORRECTABLE_ERR 0x00000002 /* Multiple COR-ERR Received */ ++#define PCIE_RESR_FATAL_NOFATAL_ERR 0x00000004 /* ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_MULTI_FATAL_NOFATAL_ERR 0x00000008 /* Multiple ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_FIRST_UNCORRECTABLE_FATAL_ERR 0x00000010 /* First UN-COR Fatal */ ++#define PCIR_RESR_NON_FATAL_ERR 0x00000020 /* Non-Fatal Error Message Received */ ++#define PCIE_RESR_FATAL_ERR 0x00000040 /* Fatal Message Received */ ++#define PCIE_RESR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_RESR_AER_INT_MSG_NUM_S 27 ++ ++/* Error Source Indentification Register */ ++#define PCIE_ESIR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x134) ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID 0x0000FFFF ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID_S 0 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID 0xFFFF0000 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID_S 16 ++ ++/* VC Enhanced Capability Header */ ++#define PCIE_VC_ECH(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x140) ++ ++/* Port VC Capability Register */ ++#define PCIE_PVC1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x144) ++#define PCIE_PVC1_EXT_VC_CNT 0x00000007 /* Extended VC Count */ ++#define PCIE_PVC1_EXT_VC_CNT_S 0 ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT 0x00000070 /* Low Priority Extended VC Count */ ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT_S 4 ++#define PCIE_PVC1_REF_CLK 0x00000300 /* Reference Clock */ ++#define PCIE_PVC1_REF_CLK_S 8 ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE 0x00000C00 /* Port Arbitration Table Entry Size */ ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE_S 10 ++ ++/* Extended Virtual Channel Count Defintion */ ++#define PCIE_EXT_VC_CNT_MIN 0 ++#define PCIE_EXT_VC_CNT_MAX 7 ++ ++/* Port Arbitration Table Entry Size Definition */ ++enum { ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S1BIT = 0, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S2BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S4BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S8BIT, ++}; ++ ++/* Port VC Capability Register 2 */ ++#define PCIE_PVC2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x148) ++#define PCIE_PVC2_VC_ARB_16P_FIXED_WRR 0x00000001 /* HW Fixed arbitration, 16 phase WRR */ ++#define PCIE_PVC2_VC_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_PVC2_VC_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_PVC2_VC_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_PVC2_VC_ARB_WRR 0x0000000F ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET 0xFF000000 /* VC arbitration table offset, not support */ ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET_S 24 ++ ++/* Port VC Control and Status Register */ ++#define PCIE_PVCCRSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14C) ++#define PCIE_PVCCRSR_LOAD_VC_ARB_TAB 0x00000001 /* Load VC Arbitration Table */ ++#define PCIE_PVCCRSR_VC_ARB_SEL 0x0000000E /* VC Arbitration Select */ ++#define PCIE_PVCCRSR_VC_ARB_SEL_S 1 ++#define PCIE_PVCCRSR_VC_ARB_TAB_STATUS 0x00010000 /* Arbitration Status */ ++ ++/* VC0 Resource Capability Register */ ++#define PCIE_VC0_RC(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x150) ++#define PCIE_VC0_RC_PORT_ARB_HW_FIXED 0x00000001 /* HW Fixed arbitration */ ++#define PCIE_VC0_RC_PORT_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_128P_WRR 0x00000010 /* Time-based 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_256P_WRR 0x00000020 /* Time-based 256 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB (PCIE_VC0_RC_PORT_ARB_HW_FIXED | PCIE_VC0_RC_PORT_ARB_32P_WRR |\ ++ PCIE_VC0_RC_PORT_ARB_64P_WRR | PCIE_VC0_RC_PORT_ARB_128P_WRR | \ ++ PCIE_VC0_RC_PORT_ARB_TM_128P_WRR | PCIE_VC0_RC_PORT_ARB_TM_256P_WRR) ++ ++#define PCIE_VC0_RC_REJECT_SNOOP 0x00008000 /* Reject Snoop Transactioin */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS 0x007F0000 /* Maximum time Slots */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS_S 16 ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET 0xFF000000 /* Port Arbitration Table Offset */ ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET_S 24 ++ ++/* VC0 Resource Control Register */ ++#define PCIE_VC0_RC0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x154) ++#define PCIE_VC0_RC0_TVM0 0x00000001 /* TC0 and VC0 */ ++#define PCIE_VC0_RC0_TVM1 0x00000002 /* TC1 and VC1 */ ++#define PCIE_VC0_RC0_TVM2 0x00000004 /* TC2 and VC2 */ ++#define PCIE_VC0_RC0_TVM3 0x00000008 /* TC3 and VC3 */ ++#define PCIE_VC0_RC0_TVM4 0x00000010 /* TC4 and VC4 */ ++#define PCIE_VC0_RC0_TVM5 0x00000020 /* TC5 and VC5 */ ++#define PCIE_VC0_RC0_TVM6 0x00000040 /* TC6 and VC6 */ ++#define PCIE_VC0_RC0_TVM7 0x00000080 /* TC7 and VC7 */ ++#define PCIE_VC0_RC0_TC_VC 0x000000FF /* TC/VC mask */ ++ ++#define PCIE_VC0_RC0_LOAD_PORT_ARB_TAB 0x00010000 /* Load Port Arbitration Table */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL 0x000E0000 /* Port Arbitration Select */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL_S 17 ++#define PCIE_VC0_RC0_VC_ID 0x07000000 /* VC ID */ ++#define PCIE_VC0_RC0_VC_ID_S 24 ++#define PCIE_VC0_RC0_VC_EN 0x80000000 /* VC Enable */ ++ ++/* VC0 Resource Status Register */ ++#define PCIE_VC0_RSR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x158) ++#define PCIE_VC0_RSR0_PORT_ARB_TAB_STATUS 0x00010000 /* Port Arbitration Table Status,not used */ ++#define PCIE_VC0_RSR0_VC_NEG_PENDING 0x00020000 /* VC Negotiation Pending */ ++ ++/* Ack Latency Timer and Replay Timer Register */ ++#define PCIE_ALTRT(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x700) ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT 0x0000FFFF /* Round Trip Latency Time Limit */ ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT_S 0 ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT 0xFFFF0000 /* Replay Time Limit */ ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT_S 16 ++ ++/* Other Message Register */ ++#define PCIE_OMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x704) ++ ++/* Port Force Link Register */ ++#define PCIE_PFLR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x708) ++#define PCIE_PFLR_LINK_NUM 0x000000FF /* Link Number */ ++#define PCIE_PFLR_LINK_NUM_S 0 ++#define PCIE_PFLR_FORCE_LINK 0x00008000 /* Force link */ ++#define PCIE_PFLR_LINK_STATE 0x003F0000 /* Link State */ ++#define PCIE_PFLR_LINK_STATE_S 16 ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT 0xFF000000 /* Low Power Entrance Count, only for EP */ ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT_S 24 ++ ++/* Ack Frequency Register */ ++#define PCIE_AFR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70C) ++#define PCIE_AFR_AF 0x000000FF /* Ack Frequency */ ++#define PCIE_AFR_AF_S 0 ++#define PCIE_AFR_FTS_NUM 0x0000FF00 /* The number of Fast Training Sequence from L0S to L0 */ ++#define PCIE_AFR_FTS_NUM_S 8 ++#define PCIE_AFR_COM_FTS_NUM 0x00FF0000 /* N_FTS; when common clock is used*/ ++#define PCIE_AFR_COM_FTS_NUM_S 16 ++#define PCIE_AFR_L0S_ENTRY_LATENCY 0x07000000 /* L0s Entrance Latency */ ++#define PCIE_AFR_L0S_ENTRY_LATENCY_S 24 ++#define PCIE_AFR_L1_ENTRY_LATENCY 0x38000000 /* L1 Entrance Latency */ ++#define PCIE_AFR_L1_ENTRY_LATENCY_S 27 ++#define PCIE_AFR_FTS_NUM_DEFAULT 32 ++#define PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT 7 ++#define PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT 5 ++ ++/* Port Link Control Register */ ++#define PCIE_PLCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x710) ++#define PCIE_PLCR_OTHER_MSG_REQ 0x00000001 /* Other Message Request */ ++#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ ++#define PCIE_PLCR_LOOPBACK_EN 0x00000004 /* Loopback Enable */ ++#define PCIE_PLCR_LTSSM_HOT_RST 0x00000008 /* Force LTSSM to the hot reset */ ++#define PCIE_PLCR_DLL_LINK_EN 0x00000020 /* Enable Link initialization */ ++#define PCIE_PLCR_FAST_LINK_SIM_EN 0x00000080 /* Sets all internal timers to fast mode for simulation purposes */ ++#define PCIE_PLCR_LINK_MODE 0x003F0000 /* Link Mode Enable Mask */ ++#define PCIE_PLCR_LINK_MODE_S 16 ++#define PCIE_PLCR_CORRUPTED_CRC_EN 0x02000000 /* Enabled Corrupt CRC */ ++ ++/* Lane Skew Register */ ++#define PCIE_LSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x714) ++#define PCIE_LSR_LANE_SKEW_NUM 0x00FFFFFF /* Insert Lane Skew for Transmit, not applicable */ ++#define PCIE_LSR_LANE_SKEW_NUM_S 0 ++#define PCIE_LSR_FC_DISABLE 0x01000000 /* Disable of Flow Control */ ++#define PCIE_LSR_ACKNAK_DISABLE 0x02000000 /* Disable of Ack/Nak */ ++#define PCIE_LSR_LANE_DESKEW_DISABLE 0x80000000 /* Disable of Lane-to-Lane Skew */ ++ ++/* Symbol Number Register */ ++#define PCIE_SNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x718) ++#define PCIE_SNR_TS 0x0000000F /* Number of TS Symbol */ ++#define PCIE_SNR_TS_S 0 ++#define PCIE_SNR_SKP 0x00000700 /* Number of SKP Symbol */ ++#define PCIE_SNR_SKP_S 8 ++#define PCIE_SNR_REPLAY_TIMER 0x0007C000 /* Timer Modifier for Replay Timer */ ++#define PCIE_SNR_REPLAY_TIMER_S 14 ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER 0x00F80000 /* Timer Modifier for Ack/Nak Latency Timer */ ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER_S 19 ++#define PCIE_SNR_FC_TIMER 0x1F000000 /* Timer Modifier for Flow Control Watchdog Timer */ ++#define PCIE_SNR_FC_TIMER_S 28 ++ ++/* Symbol Timer Register and Filter Mask Register 1 */ ++#define PCIE_STRFMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x71C) ++#define PCIE_STRFMR_SKP_INTERVAL 0x000007FF /* SKP lnterval Value */ ++#define PCIE_STRFMR_SKP_INTERVAL_S 0 ++#define PCIE_STRFMR_FC_WDT_DISABLE 0x00008000 /* Disable of FC Watchdog Timer */ ++#define PCIE_STRFMR_TLP_FUNC_MISMATCH_OK 0x00010000 /* Mask Function Mismatch Filtering for Incoming Requests */ ++#define PCIE_STRFMR_POISONED_TLP_OK 0x00020000 /* Mask Poisoned TLP Filtering */ ++#define PCIE_STRFMR_BAR_MATCH_OK 0x00040000 /* Mask BAR Match Filtering */ ++#define PCIE_STRFMR_TYPE1_CFG_REQ_OK 0x00080000 /* Mask Type 1 Configuration Request Filtering */ ++#define PCIE_STRFMR_LOCKED_REQ_OK 0x00100000 /* Mask Locked Request Filtering */ ++#define PCIE_STRFMR_CPL_TAG_ERR_RULES_OK 0x00200000 /* Mask Tag Error Rules for Received Completions */ ++#define PCIE_STRFMR_CPL_REQUESTOR_ID_MISMATCH_OK 0x00400000 /* Mask Requester ID Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_FUNC_MISMATCH_OK 0x00800000 /* Mask Function Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_TC_MISMATCH_OK 0x01000000 /* Mask Traffic Class Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_ATTR_MISMATCH_OK 0x02000000 /* Mask Attribute Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_LENGTH_MISMATCH_OK 0x04000000 /* Mask Length Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_TLP_ECRC_ERR_OK 0x08000000 /* Mask ECRC Error Filtering */ ++#define PCIE_STRFMR_CPL_TLP_ECRC_OK 0x10000000 /* Mask ECRC Error Filtering for Completions */ ++#define PCIE_STRFMR_RX_TLP_MSG_NO_DROP 0x20000000 /* Send Message TLPs */ ++#define PCIE_STRFMR_RX_IO_TRANS_ENABLE 0x40000000 /* Mask Filtering of received I/O Requests */ ++#define PCIE_STRFMR_RX_CFG_TRANS_ENABLE 0x80000000 /* Mask Filtering of Received Configuration Requests */ ++ ++#define PCIE_DEF_SKP_INTERVAL 700 /* 1180 ~1538 , 125MHz * 2, 250MHz * 1 */ ++ ++/* Filter Masker Register 2 */ ++#define PCIE_FMR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x720) ++#define PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1 0x00000001 /* Mask RADM Filtering and Error Handling Rules */ ++#define PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 0x00000002 /* Mask RADM Filtering and Error Handling Rules */ ++ ++/* Debug Register 0 */ ++#define PCIE_DBR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x728) ++ ++/* Debug Register 1 */ ++#define PCIE_DBR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x72C) ++ ++/* Transmit Posted FC Credit Status Register */ ++#define PCIE_TPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x730) ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS 0x00000FFF /* Transmit Posted Data FC Credits */ ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS_S 0 ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS 0x000FF000 /* Transmit Posted Header FC Credits */ ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Non-Posted FC Credit Status */ ++#define PCIE_TNPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x734) ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS 0x00000FFF /* Transmit Non-Posted Data FC Credits */ ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS_S 0 ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS 0x000FF000 /* Transmit Non-Posted Header FC Credits */ ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Complete FC Credit Status Register */ ++#define PCIE_TCFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x738) ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS 0x00000FFF /* Transmit Completion Data FC Credits */ ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS_S 0 ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS 0x000FF000 /* Transmit Completion Header FC Credits */ ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS_S 12 ++ ++/* Queue Status Register */ ++#define PCIE_QSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x73C) ++#define PCIE_QSR_WAIT_UPDATE_FC_DLL 0x00000001 /* Received TLP FC Credits Not Returned */ ++#define PCIE_QSR_TX_RETRY_BUF_NOT_EMPTY 0x00000002 /* Transmit Retry Buffer Not Empty */ ++#define PCIE_QSR_RX_QUEUE_NOT_EMPTY 0x00000004 /* Received Queue Not Empty */ ++ ++/* VC Transmit Arbitration Register 1 */ ++#define PCIE_VCTAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x740) ++#define PCIE_VCTAR1_WRR_WEIGHT_VC0 0x000000FF /* WRR Weight for VC0 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC1 0x0000FF00 /* WRR Weight for VC1 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC2 0x00FF0000 /* WRR Weight for VC2 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC3 0xFF000000 /* WRR Weight for VC3 */ ++ ++/* VC Transmit Arbitration Register 2 */ ++#define PCIE_VCTAR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x744) ++#define PCIE_VCTAR2_WRR_WEIGHT_VC4 0x000000FF /* WRR Weight for VC4 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC5 0x0000FF00 /* WRR Weight for VC5 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC6 0x00FF0000 /* WRR Weight for VC6 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC7 0xFF000000 /* WRR Weight for VC7 */ ++ ++/* VC0 Posted Receive Queue Control Register */ ++#define PCIE_VC0_PRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x748) ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS 0x00000FFF /* VC0 Posted Data Credits */ ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS_S 0 ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS 0x000FF000 /* VC0 Posted Header Credits */ ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS_S 12 ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE 0x00E00000 /* VC0 Posted TLP Queue Mode */ ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE_S 20 ++#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ ++#define PCIE_VC0_PRQCR_VC_STRICT_ORDER 0x80000000 /* VC0 Ordering for Receive Queues */ ++ ++/* VC0 Non-Posted Receive Queue Control */ ++#define PCIE_VC0_NPRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74C) ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS 0x00000FFF /* VC0 Non-Posted Data Credits */ ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS_S 0 ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS 0x000FF000 /* VC0 Non-Posted Header Credits */ ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS_S 12 ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE 0x00E00000 /* VC0 Non-Posted TLP Queue Mode */ ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE_S 20 ++ ++/* VC0 Completion Receive Queue Control */ ++#define PCIE_VC0_CRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x750) ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS 0x00000FFF /* VC0 Completion TLP Queue Mode */ ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS_S 0 ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS 0x000FF000 /* VC0 Completion Header Credits */ ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS_S 12 ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE 0x00E00000 /* VC0 Completion Data Credits */ ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE_S 21 ++ ++/* Applicable to the above three registers */ ++enum { ++ PCIE_VC0_TLP_QUEUE_MODE_STORE_FORWARD = 1, ++ PCIE_VC0_TLP_QUEUE_MODE_CUT_THROUGH = 2, ++ PCIE_VC0_TLP_QUEUE_MODE_BYPASS = 4, ++}; ++ ++/* VC0 Posted Buffer Depth Register */ ++#define PCIE_VC0_PBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7A8) ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Posted Data Queue Depth */ ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Posted Header Queue Depth */ ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Non-Posted Buffer Depth Register */ ++#define PCIE_VC0_NPBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7AC) ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Non-Posted Data Queue Depth */ ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Non-Posted Header Queue Depth */ ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Completion Buffer Depth Register */ ++#define PCIE_VC0_CBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7B0) ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES 0x00003FFF /* C0 Completion Data Queue Depth */ ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Completion Header Queue Depth */ ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES_S 16 ++ ++/* PHY Status Register, all zeros in VR9 */ ++#define PCIE_PHYSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x810) ++ ++/* PHY Control Register, all zeros in VR9 */ ++#define PCIE_PHYCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x814) ++ ++/* ++ * PCIe PDI PHY register definition, suppose all the following ++ * stuff is confidential. ++ * XXX, detailed bit definition ++ */ ++#define PCIE_PHY_PLL_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x22 << 1)) ++#define PCIE_PHY_PLL_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x23 << 1)) ++#define PCIE_PHY_PLL_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x24 << 1)) ++#define PCIE_PHY_PLL_CTRL4(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x25 << 1)) ++#define PCIE_PHY_PLL_CTRL5(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x26 << 1)) ++#define PCIE_PHY_PLL_CTRL6(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x27 << 1)) ++#define PCIE_PHY_PLL_CTRL7(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x28 << 1)) ++#define PCIE_PHY_PLL_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x29 << 1)) ++#define PCIE_PHY_PLL_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2A << 1)) ++#define PCIE_PHY_PLL_A_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2B << 1)) ++#define PCIE_PHY_PLL_STATUS(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2C << 1)) ++ ++#define PCIE_PHY_TX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x30 << 1)) ++#define PCIE_PHY_TX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x31 << 1)) ++#define PCIE_PHY_TX1_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x32 << 1)) ++#define PCIE_PHY_TX1_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x33 << 1)) ++#define PCIE_PHY_TX1_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x34 << 1)) ++#define PCIE_PHY_TX1_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x35 << 1)) ++#define PCIE_PHY_TX1_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x36 << 1)) ++#define PCIE_PHY_TX1_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x37 << 1)) ++ ++#define PCIE_PHY_TX2_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x38 << 1)) ++#define PCIE_PHY_TX2_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x39 << 1)) ++#define PCIE_PHY_TX2_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3B << 1)) ++#define PCIE_PHY_TX2_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3C << 1)) ++#define PCIE_PHY_TX2_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3D << 1)) ++#define PCIE_PHY_TX2_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3E << 1)) ++#define PCIE_PHY_TX2_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3F << 1)) ++ ++#define PCIE_PHY_RX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x50 << 1)) ++#define PCIE_PHY_RX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x51 << 1)) ++#define PCIE_PHY_RX1_CDR(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x52 << 1)) ++#define PCIE_PHY_RX1_EI(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x53 << 1)) ++#define PCIE_PHY_RX1_A_CTRL(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x55 << 1)) ++ ++/* Interrupt related stuff */ ++#define PCIE_LEGACY_DISABLE 0 ++#define PCIE_LEGACY_INTA 1 ++#define PCIE_LEGACY_INTB 2 ++#define PCIE_LEGACY_INTC 3 ++#define PCIE_LEGACY_INTD 4 ++#define PCIE_LEGACY_INT_MAX PCIE_LEGACY_INTD ++ ++#endif /* IFXMIPS_PCIE_REG_H */ ++ +--- /dev/null ++++ b/arch/mips/pci/ifxmips_pcie_vr9.h +@@ -0,0 +1,269 @@ ++/**************************************************************************** ++ Copyright (c) 2010 ++ Lantiq Deutschland GmbH ++ Am Campeon 3; 85579 Neubiberg, Germany ++ ++ For licensing information, see the file 'LICENSE' in the root folder of ++ this software module. ++ ++ *****************************************************************************/ ++/*! ++ \file ifxmips_pcie_vr9.h ++ \ingroup IFX_PCIE ++ \brief PCIe RC driver vr9 specific file ++*/ ++ ++#ifndef IFXMIPS_PCIE_VR9_H ++#define IFXMIPS_PCIE_VR9_H ++ ++#include <linux/types.h> ++#include <linux/delay.h> ++ ++#include <linux/gpio.h> ++#include <lantiq_soc.h> ++ ++#define IFX_PCIE_GPIO_RESET 494 ++ ++#define IFX_REG_R32 ltq_r32 ++#define IFX_REG_W32 ltq_w32 ++#define CONFIG_IFX_PCIE_HW_SWAP ++#define IFX_RCU_AHB_ENDIAN ((volatile u32*)(IFX_RCU + 0x004C)) ++#define IFX_RCU_RST_REQ ((volatile u32*)(IFX_RCU + 0x0010)) ++#define IFX_RCU_AHB_BE_PCIE_PDI 0x00000080 /* Configure PCIE PDI module in big endian*/ ++ ++#define IFX_RCU (KSEG1 | 0x1F203000) ++#define IFX_RCU_AHB_BE_PCIE_M 0x00000001 /* Configure AHB master port that connects to PCIe RC in big endian */ ++#define IFX_RCU_AHB_BE_PCIE_S 0x00000010 /* Configure AHB slave port that connects to PCIe RC in little endian */ ++#define IFX_RCU_AHB_BE_XBAR_M 0x00000002 /* Configure AHB master port that connects to XBAR in big endian */ ++#define CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ ++#define IFX_PMU1_MODULE_PCIE_PHY (0) ++#define IFX_PMU1_MODULE_PCIE_CTRL (1) ++#define IFX_PMU1_MODULE_PDI (4) ++#define IFX_PMU1_MODULE_MSI (5) ++ ++#define IFX_PMU_MODULE_PCIE_L0_CLK (31) ++ ++ ++#define IFX_GPIO (KSEG1 | 0x1E100B00) ++#define ALT0 ((volatile u32*)(IFX_GPIO + 0x007c)) ++#define ALT1 ((volatile u32*)(IFX_GPIO + 0x0080)) ++#define OD ((volatile u32*)(IFX_GPIO + 0x0084)) ++#define DIR ((volatile u32*)(IFX_GPIO + 0x0078)) ++#define OUT ((volatile u32*)(IFX_GPIO + 0x0070)) ++ ++ ++static inline void pcie_ep_gpio_rst_init(int pcie_port) ++{ ++ ++ gpio_request(IFX_PCIE_GPIO_RESET, "pcie-reset"); ++ gpio_direction_output(IFX_PCIE_GPIO_RESET, 1); ++ gpio_set_value(IFX_PCIE_GPIO_RESET, 1); ++ ++/* ifx_gpio_pin_reserve(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_dir_out_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_altsel0_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_altsel1_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++ ifx_gpio_open_drain_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id);*/ ++} ++ ++static inline void pcie_ahb_pmu_setup(void) ++{ ++ /* Enable AHB bus master/slave */ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "ahb"); ++ clk_enable(clk); ++ ++ //AHBM_PMU_SETUP(IFX_PMU_ENABLE); ++ //AHBS_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_rcu_endian_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg |= IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#else ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg &= ~IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++ IFX_PCIE_PRINT(PCIE_MSG_REG, "%s IFX_RCU_AHB_ENDIAN: 0x%08x\n", __func__, IFX_REG_R32(IFX_RCU_AHB_ENDIAN)); ++} ++ ++static inline void pcie_phy_pmu_enable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "phy"); ++ clk_enable(clk); ++ ++ //PCIE_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_phy_pmu_disable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "phy"); ++ clk_disable(clk); ++ ++// PCIE_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++} ++ ++static inline void pcie_pdi_big_endian(int pcie_port) ++{ ++ u32 reg; ++ ++ /* SRAM2PDI endianness control. */ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_AHB_BE_PCIE_PDI; ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++} ++ ++static inline void pcie_pdi_pmu_enable(int pcie_port) ++{ ++ /* Enable PDI to access PCIe PHY register */ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "pdi"); ++ clk_enable(clk); ++ //PDI_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_core_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ ++ /* Reset PCIe PHY & Core, bit 22, bit 26 may be affected if write it directly */ ++ reg |= 0x00400000; ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_core_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ /* Reset PCIe PHY & Core, bit 22 */ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg &= ~0x00400000; ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg |= 0x00001000; /* Bit 12 */ ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg &= ~0x00001000; /* Bit 12 */ ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_device_rst_assert(int pcie_port) ++{ ++ gpio_set_value(IFX_PCIE_GPIO_RESET, 0); ++// ifx_gpio_output_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++} ++ ++static inline void pcie_device_rst_deassert(int pcie_port) ++{ ++ mdelay(100); ++ gpio_direction_output(IFX_PCIE_GPIO_RESET, 1); ++// gpio_set_value(IFX_PCIE_GPIO_RESET, 1); ++ //ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++} ++ ++static inline void pcie_core_pmu_setup(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("1d900000.pcie", "ctl"); ++ clk_enable(clk); ++ clk = clk_get_sys("1d900000.pcie", "bus"); ++ clk_enable(clk); ++ ++ /* PCIe Core controller enabled */ ++// PCIE_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ ++ /* Enable PCIe L0 Clock */ ++// PCIE_L0_CLK_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline void pcie_msi_init(int pcie_port) ++{ ++ struct clk *clk; ++ pcie_msi_pic_init(pcie_port); ++ clk = clk_get_sys("ltq_pcie", "msi"); ++ clk_enable(clk); ++// MSI_PMU_SETUP(IFX_PMU_ENABLE); ++} ++ ++static inline u32 ++ifx_pcie_bus_nr_deduct(u32 bus_number, int pcie_port) ++{ ++ u32 tbus_number = bus_number; ++ ++#ifdef CONFIG_PCI_LANTIQ ++ if (pcibios_host_nr() > 1) { ++ tbus_number -= pcibios_1st_host_bus_nr(); ++ } ++#endif /* CONFIG_PCI_LANTIQ */ ++ return tbus_number; ++} ++ ++static inline u32 ++ifx_pcie_bus_enum_hack(struct pci_bus *bus, u32 devfn, int where, u32 value, int pcie_port, int read) ++{ ++ struct pci_dev *pdev; ++ u32 tvalue = value; ++ ++ /* Sanity check */ ++ pdev = pci_get_slot(bus, devfn); ++ if (pdev == NULL) { ++ return tvalue; ++ } ++ ++ /* Only care about PCI bridge */ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { ++ return tvalue; ++ } ++ ++ if (read) { /* Read hack */ ++ #ifdef CONFIG_PCI_LANTIQ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_read_hack(where, tvalue); ++ } ++ #endif /* CONFIG_PCI_LANTIQ */ ++ } ++ else { /* Write hack */ ++ #ifdef CONFIG_PCI_LANTIQ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_write_hack(where, tvalue); ++ } ++ #endif ++ } ++ return tvalue; ++} ++ ++#endif /* IFXMIPS_PCIE_VR9_H */ ++ +--- a/arch/mips/pci/pci-legacy.c ++++ b/arch/mips/pci/pci-legacy.c +@@ -313,3 +313,30 @@ char *__init pcibios_setup(char *str) + return pcibios_plat_setup(str); + return str; + } ++ ++int pcibios_host_nr(void) ++{ ++ int count = 0; ++ struct pci_controller *hose; ++ list_for_each_entry(hose, &controllers, list) { ++ count++; ++ } ++ return count; ++} ++EXPORT_SYMBOL(pcibios_host_nr); ++ ++int pcibios_1st_host_bus_nr(void) ++{ ++ int bus_nr = 0; ++ struct pci_controller *hose; ++ ++ hose = list_first_entry_or_null(&controllers, struct pci_controller, list); ++ ++ if (hose != NULL) { ++ if (hose->bus != NULL) { ++ bus_nr = hose->bus->number + 1; ++ } ++ } ++ return bus_nr; ++} ++EXPORT_SYMBOL(pcibios_1st_host_bus_nr); +--- /dev/null ++++ b/arch/mips/pci/pcie-lantiq.h +@@ -0,0 +1,1301 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_reg.h ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++#ifndef IFXMIPS_PCIE_REG_H ++#define IFXMIPS_PCIE_REG_H ++#include <linux/version.h> ++#include <linux/types.h> ++#include <linux/pci.h> ++#include <linux/interrupt.h> ++/*! ++ \file ifxmips_pcie_reg.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe module register definition ++*/ ++/* PCIe Address Mapping Base */ ++#define PCIE_CFG_PHY_BASE 0x1D000000UL ++#define PCIE_CFG_BASE (KSEG1 + PCIE_CFG_PHY_BASE) ++#define PCIE_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE_MEM_PHY_BASE 0x1C000000UL ++#define PCIE_MEM_BASE (KSEG1 + PCIE_MEM_PHY_BASE) ++#define PCIE_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE_MEM_PHY_END (PCIE_MEM_PHY_BASE + PCIE_MEM_SIZE - 1) ++ ++#define PCIE_IO_PHY_BASE 0x1D800000UL ++#define PCIE_IO_BASE (KSEG1 + PCIE_IO_PHY_BASE) ++#define PCIE_IO_SIZE (1 * 1024 * 1024) ++#define PCIE_IO_PHY_END (PCIE_IO_PHY_BASE + PCIE_IO_SIZE - 1) ++ ++#define PCIE_RC_CFG_BASE (KSEG1 + 0x1D900000) ++#define PCIE_APP_LOGIC_REG (KSEG1 + 0x1E100900) ++#define PCIE_MSI_PHY_BASE 0x1F600000UL ++ ++#define PCIE_PDI_PHY_BASE 0x1F106800UL ++#define PCIE_PDI_BASE (KSEG1 + PCIE_PDI_PHY_BASE) ++#define PCIE_PDI_SIZE 0x400 ++ ++#define PCIE1_CFG_PHY_BASE 0x19000000UL ++#define PCIE1_CFG_BASE (KSEG1 + PCIE1_CFG_PHY_BASE) ++#define PCIE1_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE1_MEM_PHY_BASE 0x18000000UL ++#define PCIE1_MEM_BASE (KSEG1 + PCIE1_MEM_PHY_BASE) ++#define PCIE1_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE1_MEM_PHY_END (PCIE1_MEM_PHY_BASE + PCIE1_MEM_SIZE - 1) ++ ++#define PCIE1_IO_PHY_BASE 0x19800000UL ++#define PCIE1_IO_BASE (KSEG1 + PCIE1_IO_PHY_BASE) ++#define PCIE1_IO_SIZE (1 * 1024 * 1024) ++#define PCIE1_IO_PHY_END (PCIE1_IO_PHY_BASE + PCIE1_IO_SIZE - 1) ++ ++#define PCIE1_RC_CFG_BASE (KSEG1 + 0x19900000) ++#define PCIE1_APP_LOGIC_REG (KSEG1 + 0x1E100700) ++#define PCIE1_MSI_PHY_BASE 0x1F400000UL ++ ++#define PCIE1_PDI_PHY_BASE 0x1F700400UL ++#define PCIE1_PDI_BASE (KSEG1 + PCIE1_PDI_PHY_BASE) ++#define PCIE1_PDI_SIZE 0x400 ++ ++#define PCIE_CFG_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_CFG_BASE) : (PCIE_CFG_BASE)) ++#define PCIE_MEM_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_BASE) : (PCIE_MEM_BASE)) ++#define PCIE_IO_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_BASE) : (PCIE_IO_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_PHY_BASE) : (PCIE_MEM_PHY_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_MEM_PHY_END) : (PCIE_MEM_PHY_END)) ++#define PCIE_IO_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_PHY_BASE) : (PCIE_IO_PHY_BASE)) ++#define PCIE_IO_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_IO_PHY_END) : (PCIE_IO_PHY_END)) ++#define PCIE_APP_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_APP_LOGIC_REG) : (PCIE_APP_LOGIC_REG)) ++#define PCIE_RC_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_RC_CFG_BASE) : (PCIE_RC_CFG_BASE)) ++#define PCIE_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_PDI_BASE) : (PCIE_PDI_BASE)) ++ ++/* PCIe Application Logic Register */ ++/* RC Core Control Register */ ++#define PCIE_RC_CCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x10) ++/* This should be enabled after initializing configuratin registers ++ * Also should check link status retraining bit ++ */ ++#define PCIE_RC_CCR_LTSSM_ENABLE 0x00000001 /* Enable LTSSM to continue link establishment */ ++ ++/* RC Core Debug Register */ ++#define PCIE_RC_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x14) ++#define PCIE_RC_DR_DLL_UP 0x00000001 /* Data Link Layer Up */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE 0x0000000E /* Current Power State */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE_S 1 ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE 0x000001F0 /* Current LTSSM State */ ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE_S 4 ++ ++#define PCIE_RC_DR_PM_DEV_STATE 0x00000E00 /* Power Management D-State */ ++#define PCIE_RC_DR_PM_DEV_STATE_S 9 ++ ++#define PCIE_RC_DR_PM_ENABLED 0x00001000 /* Power Management State from PMU */ ++#define PCIE_RC_DR_PME_EVENT_ENABLED 0x00002000 /* Power Management Event Enable State */ ++#define PCIE_RC_DR_AUX_POWER_ENABLED 0x00004000 /* Auxiliary Power Enable */ ++ ++/* Current Power State Definition */ ++enum { ++ PCIE_RC_DR_D0 = 0, ++ PCIE_RC_DR_D1, /* Not supported */ ++ PCIE_RC_DR_D2, /* Not supported */ ++ PCIE_RC_DR_D3, ++ PCIE_RC_DR_UN, ++}; ++ ++/* PHY Link Status Register */ ++#define PCIE_PHY_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x18) ++#define PCIE_PHY_SR_PHY_LINK_UP 0x00000001 /* PHY Link Up/Down Indicator */ ++ ++/* Electromechanical Control Register */ ++#define PCIE_EM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x1C) ++#define PCIE_EM_CR_CARD_IS_PRESENT 0x00000001 /* Card Presence Detect State */ ++#define PCIE_EM_CR_MRL_OPEN 0x00000002 /* MRL Sensor State */ ++#define PCIE_EM_CR_POWER_FAULT_SET 0x00000004 /* Power Fault Detected */ ++#define PCIE_EM_CR_MRL_SENSOR_SET 0x00000008 /* MRL Sensor Changed */ ++#define PCIE_EM_CR_PRESENT_DETECT_SET 0x00000010 /* Card Presense Detect Changed */ ++#define PCIE_EM_CR_CMD_CPL_INT_SET 0x00000020 /* Command Complete Interrupt */ ++#define PCIE_EM_CR_SYS_INTERLOCK_SET 0x00000040 /* System Electromechanical IterLock Engaged */ ++#define PCIE_EM_CR_ATTENTION_BUTTON_SET 0x00000080 /* Attention Button Pressed */ ++ ++/* Interrupt Status Register */ ++#define PCIE_IR_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x20) ++#define PCIE_IR_SR_PME_CAUSE_MSI 0x00000002 /* MSI caused by PME */ ++#define PCIE_IR_SR_HP_PME_WAKE_GEN 0x00000004 /* Hotplug PME Wake Generation */ ++#define PCIE_IR_SR_HP_MSI 0x00000008 /* Hotplug MSI */ ++#define PCIE_IR_SR_AHB_LU_ERR 0x00000030 /* AHB Bridge Lookup Error Signals */ ++#define PCIE_IR_SR_AHB_LU_ERR_S 4 ++#define PCIE_IR_SR_INT_MSG_NUM 0x00003E00 /* Interrupt Message Number */ ++#define PCIE_IR_SR_INT_MSG_NUM_S 9 ++#define PCIE_IR_SR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_IR_SR_AER_INT_MSG_NUM_S 27 ++ ++/* Message Control Register */ ++#define PCIE_MSG_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x30) ++#define PCIE_MSG_CR_GEN_PME_TURN_OFF_MSG 0x00000001 /* Generate PME Turn Off Message */ ++#define PCIE_MSG_CR_GEN_UNLOCK_MSG 0x00000002 /* Generate Unlock Message */ ++ ++#define PCIE_VDM_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x34) ++ ++/* Vendor-Defined Message Requester ID Register */ ++#define PCIE_VDM_RID(X) (PCIE_APP_PORT_TO_BASE (X) + 0x38) ++#define PCIE_VDM_RID_VENROR_MSG_REQ_ID 0x0000FFFF ++#define PCIE_VDM_RID_VDMRID_S 0 ++ ++/* ASPM Control Register */ ++#define PCIE_ASPM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x40) ++#define PCIE_ASPM_CR_HOT_RST 0x00000001 /* Hot Reset Request to the downstream device */ ++#define PCIE_ASPM_CR_REQ_EXIT_L1 0x00000002 /* Request to Exit L1 */ ++#define PCIE_ASPM_CR_REQ_ENTER_L1 0x00000004 /* Request to Enter L1 */ ++ ++/* Vendor Message DW0 Register */ ++#define PCIE_VM_MSG_DW0(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x50) ++#define PCIE_VM_MSG_DW0_TYPE 0x0000001F /* Message type */ ++#define PCIE_VM_MSG_DW0_TYPE_S 0 ++#define PCIE_VM_MSG_DW0_FORMAT 0x00000060 /* Format */ ++#define PCIE_VM_MSG_DW0_FORMAT_S 5 ++#define PCIE_VM_MSG_DW0_TC 0x00007000 /* Traffic Class */ ++#define PCIE_VM_MSG_DW0_TC_S 12 ++#define PCIE_VM_MSG_DW0_ATTR 0x000C0000 /* Atrributes */ ++#define PCIE_VM_MSG_DW0_ATTR_S 18 ++#define PCIE_VM_MSG_DW0_EP_TLP 0x00100000 /* Poisoned TLP */ ++#define PCIE_VM_MSG_DW0_TD 0x00200000 /* TLP Digest */ ++#define PCIE_VM_MSG_DW0_LEN 0xFFC00000 /* Length */ ++#define PCIE_VM_MSG_DW0_LEN_S 22 ++ ++/* Format Definition */ ++enum { ++ PCIE_VM_MSG_FORMAT_00 = 0, /* 3DW Hdr, no data*/ ++ PCIE_VM_MSG_FORMAT_01, /* 4DW Hdr, no data */ ++ PCIE_VM_MSG_FORMAT_10, /* 3DW Hdr, with data */ ++ PCIE_VM_MSG_FORMAT_11, /* 4DW Hdr, with data */ ++}; ++ ++/* Traffic Class Definition */ ++enum { ++ PCIE_VM_MSG_TC0 = 0, ++ PCIE_VM_MSG_TC1, ++ PCIE_VM_MSG_TC2, ++ PCIE_VM_MSG_TC3, ++ PCIE_VM_MSG_TC4, ++ PCIE_VM_MSG_TC5, ++ PCIE_VM_MSG_TC6, ++ PCIE_VM_MSG_TC7, ++}; ++ ++/* Attributes Definition */ ++enum { ++ PCIE_VM_MSG_ATTR_00 = 0, /* RO and No Snoop cleared */ ++ PCIE_VM_MSG_ATTR_01, /* RO cleared , No Snoop set */ ++ PCIE_VM_MSG_ATTR_10, /* RO set, No Snoop cleared*/ ++ PCIE_VM_MSG_ATTR_11, /* RO and No Snoop set */ ++}; ++ ++/* Payload Size Definition */ ++#define PCIE_VM_MSG_LEN_MIN 0 ++#define PCIE_VM_MSG_LEN_MAX 1024 ++ ++/* Vendor Message DW1 Register */ ++#define PCIE_VM_MSG_DW1(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x54) ++#define PCIE_VM_MSG_DW1_FUNC_NUM 0x00000070 /* Function Number */ ++#define PCIE_VM_MSG_DW1_FUNC_NUM_S 8 ++#define PCIE_VM_MSG_DW1_CODE 0x00FF0000 /* Message Code */ ++#define PCIE_VM_MSG_DW1_CODE_S 16 ++#define PCIE_VM_MSG_DW1_TAG 0xFF000000 /* Tag */ ++#define PCIE_VM_MSG_DW1_TAG_S 24 ++ ++#define PCIE_VM_MSG_DW2(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x58) ++#define PCIE_VM_MSG_DW3(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x5C) ++ ++/* Vendor Message Request Register */ ++#define PCIE_VM_MSG_REQR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x60) ++#define PCIE_VM_MSG_REQR_REQ 0x00000001 /* Vendor Message Request */ ++ ++ ++/* AHB Slave Side Band Control Register */ ++#define PCIE_AHB_SSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x70) ++#define PCIE_AHB_SSB_REQ_BCM 0x00000001 /* Slave Reques BCM filed */ ++#define PCIE_AHB_SSB_REQ_EP 0x00000002 /* Slave Reques EP filed */ ++#define PCIE_AHB_SSB_REQ_TD 0x00000004 /* Slave Reques TD filed */ ++#define PCIE_AHB_SSB_REQ_ATTR 0x00000018 /* Slave Reques Attribute number */ ++#define PCIE_AHB_SSB_REQ_ATTR_S 3 ++#define PCIE_AHB_SSB_REQ_TC 0x000000E0 /* Slave Request TC Field */ ++#define PCIE_AHB_SSB_REQ_TC_S 5 ++ ++/* AHB Master SideBand Ctrl Register */ ++#define PCIE_AHB_MSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x74) ++#define PCIE_AHB_MSB_RESP_ATTR 0x00000003 /* Master Response Attribute number */ ++#define PCIE_AHB_MSB_RESP_ATTR_S 0 ++#define PCIE_AHB_MSB_RESP_BAD_EOT 0x00000004 /* Master Response Badeot filed */ ++#define PCIE_AHB_MSB_RESP_BCM 0x00000008 /* Master Response BCM filed */ ++#define PCIE_AHB_MSB_RESP_EP 0x00000010 /* Master Response EP filed */ ++#define PCIE_AHB_MSB_RESP_TD 0x00000020 /* Master Response TD filed */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM 0x000003C0 /* Master Response Function number */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM_S 6 ++ ++/* AHB Control Register, fixed bus enumeration exception */ ++#define PCIE_AHB_CTRL(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x78) ++#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 ++ ++/* Interrupt Enalbe Register */ ++#define PCIE_IRNEN(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF4) ++#define PCIE_IRNCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF8) ++#define PCIE_IRNICR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xFC) ++ ++/* PCIe interrupt enable/control/capture register definition */ ++#define PCIE_IRN_AER_REPORT 0x00000001 /* AER Interrupt */ ++#define PCIE_IRN_AER_MSIX 0x00000002 /* Advanced Error MSI-X Interrupt */ ++#define PCIE_IRN_PME 0x00000004 /* PME Interrupt */ ++#define PCIE_IRN_HOTPLUG 0x00000008 /* Hotplug Interrupt */ ++#define PCIE_IRN_RX_VDM_MSG 0x00000010 /* Vendor-Defined Message Interrupt */ ++#define PCIE_IRN_RX_CORRECTABLE_ERR_MSG 0x00000020 /* Correctable Error Message Interrupt */ ++#define PCIE_IRN_RX_NON_FATAL_ERR_MSG 0x00000040 /* Non-fatal Error Message */ ++#define PCIE_IRN_RX_FATAL_ERR_MSG 0x00000080 /* Fatal Error Message */ ++#define PCIE_IRN_RX_PME_MSG 0x00000100 /* PME Message Interrupt */ ++#define PCIE_IRN_RX_PME_TURNOFF_ACK 0x00000200 /* PME Turnoff Ack Message Interrupt */ ++#define PCIE_IRN_AHB_BR_FATAL_ERR 0x00000400 /* AHB Fatal Error Interrupt */ ++#define PCIE_IRN_LINK_AUTO_BW_STATUS 0x00000800 /* Link Auto Bandwidth Status Interrupt */ ++#define PCIE_IRN_BW_MGT 0x00001000 /* Bandwidth Managment Interrupt */ ++#define PCIE_IRN_INTA 0x00002000 /* INTA */ ++#define PCIE_IRN_INTB 0x00004000 /* INTB */ ++#define PCIE_IRN_INTC 0x00008000 /* INTC */ ++#define PCIE_IRN_INTD 0x00010000 /* INTD */ ++#define PCIE_IRN_WAKEUP 0x00020000 /* Wake up Interrupt */ ++ ++#define PCIE_RC_CORE_COMBINED_INT (PCIE_IRN_AER_REPORT | PCIE_IRN_AER_MSIX | PCIE_IRN_PME | \ ++ PCIE_IRN_HOTPLUG | PCIE_IRN_RX_VDM_MSG | PCIE_IRN_RX_CORRECTABLE_ERR_MSG |\ ++ PCIE_IRN_RX_NON_FATAL_ERR_MSG | PCIE_IRN_RX_FATAL_ERR_MSG | \ ++ PCIE_IRN_RX_PME_MSG | PCIE_IRN_RX_PME_TURNOFF_ACK | PCIE_IRN_AHB_BR_FATAL_ERR | \ ++ PCIE_IRN_LINK_AUTO_BW_STATUS | PCIE_IRN_BW_MGT) ++/* PCIe RC Configuration Register */ ++#define PCIE_VDID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x00) ++ ++/* Bit definition from pci_reg.h */ ++#define PCIE_PCICMDSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x04) ++#define PCIE_CCRID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x08) ++#define PCIE_CLSLTHTBR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x0C) /* EP only */ ++/* BAR0, BAR1,Only necessary if the bridges implements a device-specific register set or memory buffer */ ++#define PCIE_BAR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10) /* Not used*/ ++#define PCIE_BAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14) /* Not used */ ++ ++#define PCIE_BNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x18) /* Mandatory */ ++/* Bus Number Register bits */ ++#define PCIE_BNR_PRIMARY_BUS_NUM 0x000000FF ++#define PCIE_BNR_PRIMARY_BUS_NUM_S 0 ++#define PCIE_PNR_SECONDARY_BUS_NUM 0x0000FF00 ++#define PCIE_PNR_SECONDARY_BUS_NUM_S 8 ++#define PCIE_PNR_SUB_BUS_NUM 0x00FF0000 ++#define PCIE_PNR_SUB_BUS_NUM_S 16 ++ ++/* IO Base/Limit Register bits */ ++#define PCIE_IOBLSECS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x1C) /* RC only */ ++#define PCIE_IOBLSECS_32BIT_IO_ADDR 0x00000001 ++#define PCIE_IOBLSECS_IO_BASE_ADDR 0x000000F0 ++#define PCIE_IOBLSECS_IO_BASE_ADDR_S 4 ++#define PCIE_IOBLSECS_32BIT_IOLIMT 0x00000100 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR 0x0000F000 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR_S 12 ++ ++/* Non-prefetchable Memory Base/Limit Register bit */ ++#define PCIE_MBML(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x20) /* RC only */ ++#define PCIE_MBML_MEM_BASE_ADDR 0x0000FFF0 ++#define PCIE_MBML_MEM_BASE_ADDR_S 4 ++#define PCIE_MBML_MEM_LIMIT_ADDR 0xFFF00000 ++#define PCIE_MBML_MEM_LIMIT_ADDR_S 20 ++ ++/* Prefetchable Memory Base/Limit Register bit */ ++#define PCIE_PMBL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x24) /* RC only */ ++#define PCIE_PMBL_64BIT_ADDR 0x00000001 ++#define PCIE_PMBL_UPPER_12BIT 0x0000FFF0 ++#define PCIE_PMBL_UPPER_12BIT_S 4 ++#define PCIE_PMBL_E64MA 0x00010000 ++#define PCIE_PMBL_END_ADDR 0xFFF00000 ++#define PCIE_PMBL_END_ADDR_S 20 ++#define PCIE_PMBU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x28) /* RC only */ ++#define PCIE_PMLU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x2C) /* RC only */ ++ ++/* I/O Base/Limit Upper 16 bits register */ ++#define PCIE_IO_BANDL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x30) /* RC only */ ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE 0x0000FFFF ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE_S 0 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT 0xFFFF0000 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT_S 16 ++ ++#define PCIE_CPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x34) ++#define PCIE_EBBAR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x38) ++ ++/* Interrupt and Secondary Bridge Control Register */ ++#define PCIE_INTRBCTRL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x3C) ++ ++#define PCIE_INTRBCTRL_INT_LINE 0x000000FF ++#define PCIE_INTRBCTRL_INT_LINE_S 0 ++#define PCIE_INTRBCTRL_INT_PIN 0x0000FF00 ++#define PCIE_INTRBCTRL_INT_PIN_S 8 ++#define PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE 0x00010000 /* #PERR */ ++#define PCIE_INTRBCTRL_SERR_ENABLE 0x00020000 /* #SERR */ ++#define PCIE_INTRBCTRL_ISA_ENABLE 0x00040000 /* ISA enable, IO 64KB only */ ++#define PCIE_INTRBCTRL_VGA_ENABLE 0x00080000 /* VGA enable */ ++#define PCIE_INTRBCTRL_VGA_16BIT_DECODE 0x00100000 /* VGA 16bit decode */ ++#define PCIE_INTRBCTRL_RST_SECONDARY_BUS 0x00400000 /* Secondary bus rest, hot rest, 1ms */ ++/* Others are read only */ ++enum { ++ PCIE_INTRBCTRL_INT_NON = 0, ++ PCIE_INTRBCTRL_INTA, ++ PCIE_INTRBCTRL_INTB, ++ PCIE_INTRBCTRL_INTC, ++ PCIE_INTRBCTRL_INTD, ++}; ++ ++#define PCIE_PM_CAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x40) ++ ++/* Power Management Control and Status Register */ ++#define PCIE_PM_CSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x44) ++ ++#define PCIE_PM_CSR_POWER_STATE 0x00000003 /* Power State */ ++#define PCIE_PM_CSR_POWER_STATE_S 0 ++#define PCIE_PM_CSR_SW_RST 0x00000008 /* Soft Reset Enabled */ ++#define PCIE_PM_CSR_PME_ENABLE 0x00000100 /* PME Enable */ ++#define PCIE_PM_CSR_PME_STATUS 0x00008000 /* PME status */ ++ ++/* MSI Capability Register for EP */ ++#define PCIE_MCAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x50) ++ ++#define PCIE_MCAPR_MSI_CAP_ID 0x000000FF /* MSI Capability ID */ ++#define PCIE_MCAPR_MSI_CAP_ID_S 0 ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR_S 8 ++#define PCIE_MCAPR_MSI_ENABLE 0x00010000 /* MSI Enable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP 0x000E0000 /* Multiple Message Capable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP_S 17 ++#define PCIE_MCAPR_MULTI_MSG_ENABLE 0x00700000 /* Multiple Message Enable */ ++#define PCIE_MCAPR_MULTI_MSG_ENABLE_S 20 ++#define PCIE_MCAPR_ADDR64_CAP 0X00800000 /* 64-bit Address Capable */ ++ ++/* MSI Message Address Register */ ++#define PCIE_MA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x54) ++ ++#define PCIE_MA_ADDR_MASK 0xFFFFFFFC /* Message Address */ ++ ++/* MSI Message Upper Address Register */ ++#define PCIE_MUA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x58) ++ ++/* MSI Message Data Register */ ++#define PCIE_MD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x5C) ++ ++#define PCIE_MD_DATA 0x0000FFFF /* Message Data */ ++#define PCIE_MD_DATA_S 0 ++ ++/* PCI Express Capability Register */ ++#define PCIE_XCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70) ++ ++#define PCIE_XCAP_ID 0x000000FF /* PCI Express Capability ID */ ++#define PCIE_XCAP_ID_S 0 ++#define PCIE_XCAP_NEXT_CAP 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_XCAP_NEXT_CAP_S 8 ++#define PCIE_XCAP_VER 0x000F0000 /* PCI Express Capability Version */ ++#define PCIE_XCAP_VER_S 16 ++#define PCIE_XCAP_DEV_PORT_TYPE 0x00F00000 /* Device Port Type */ ++#define PCIE_XCAP_DEV_PORT_TYPE_S 20 ++#define PCIE_XCAP_SLOT_IMPLEMENTED 0x01000000 /* Slot Implemented */ ++#define PCIE_XCAP_MSG_INT_NUM 0x3E000000 /* Interrupt Message Number */ ++#define PCIE_XCAP_MSG_INT_NUM_S 25 ++ ++/* Device Capability Register */ ++#define PCIE_DCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74) ++ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE 0x00000007 /* Max Payload size */ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE_S 0 ++#define PCIE_DCAP_PHANTOM_FUNC 0x00000018 /* Phanton Function, not supported */ ++#define PCIE_DCAP_PHANTOM_FUNC_S 3 ++#define PCIE_DCAP_EXT_TAG 0x00000020 /* Extended Tag Field */ ++#define PCIE_DCAP_EP_L0S_LATENCY 0x000001C0 /* EP L0s latency only */ ++#define PCIE_DCAP_EP_L0S_LATENCY_S 6 ++#define PCIE_DCAP_EP_L1_LATENCY 0x00000E00 /* EP L1 latency only */ ++#define PCIE_DCAP_EP_L1_LATENCY_S 9 ++#define PCIE_DCAP_ROLE_BASE_ERR_REPORT 0x00008000 /* Role Based ERR */ ++ ++/* Maximum payload size supported */ ++enum { ++ PCIE_MAX_PAYLOAD_128 = 0, ++ PCIE_MAX_PAYLOAD_256, ++ PCIE_MAX_PAYLOAD_512, ++ PCIE_MAX_PAYLOAD_1024, ++ PCIE_MAX_PAYLOAD_2048, ++ PCIE_MAX_PAYLOAD_4096, ++}; ++ ++/* Device Control and Status Register */ ++#define PCIE_DCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x78) ++ ++#define PCIE_DCTLSTS_CORRECTABLE_ERR_EN 0x00000001 /* COR-ERR */ ++#define PCIE_DCTLSTS_NONFATAL_ERR_EN 0x00000002 /* Non-fatal ERR */ ++#define PCIE_DCTLSTS_FATAL_ERR_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_DCTLSYS_UR_REQ_EN 0x00000008 /* UR ERR */ ++#define PCIE_DCTLSTS_RELAXED_ORDERING_EN 0x00000010 /* Enable relaxing ordering */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE 0x000000E0 /* Max payload mask */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE_S 5 ++#define PCIE_DCTLSTS_EXT_TAG_EN 0x00000100 /* Extended tag field */ ++#define PCIE_DCTLSTS_PHANTOM_FUNC_EN 0x00000200 /* Phantom Function Enable */ ++#define PCIE_DCTLSTS_AUX_PM_EN 0x00000400 /* AUX Power PM Enable */ ++#define PCIE_DCTLSTS_NO_SNOOP_EN 0x00000800 /* Enable no snoop, except root port*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE 0x00007000 /* Max Read Request size*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE_S 12 ++#define PCIE_DCTLSTS_CORRECTABLE_ERR 0x00010000 /* COR-ERR Detected */ ++#define PCIE_DCTLSTS_NONFATAL_ERR 0x00020000 /* Non-Fatal ERR Detected */ ++#define PCIE_DCTLSTS_FATAL_ER 0x00040000 /* Fatal ERR Detected */ ++#define PCIE_DCTLSTS_UNSUPPORTED_REQ 0x00080000 /* UR Detected */ ++#define PCIE_DCTLSTS_AUX_POWER 0x00100000 /* Aux Power Detected */ ++#define PCIE_DCTLSTS_TRANSACT_PENDING 0x00200000 /* Transaction pending */ ++ ++#define PCIE_DCTLSTS_ERR_EN (PCIE_DCTLSTS_CORRECTABLE_ERR_EN | \ ++ PCIE_DCTLSTS_NONFATAL_ERR_EN | PCIE_DCTLSTS_FATAL_ERR_EN | \ ++ PCIE_DCTLSYS_UR_REQ_EN) ++ ++/* Link Capability Register */ ++#define PCIE_LCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7C) ++#define PCIE_LCAP_MAX_LINK_SPEED 0x0000000F /* Max link speed, 0x1 by default */ ++#define PCIE_LCAP_MAX_LINK_SPEED_S 0 ++#define PCIE_LCAP_MAX_LENGTH_WIDTH 0x000003F0 /* Maxium Length Width */ ++#define PCIE_LCAP_MAX_LENGTH_WIDTH_S 4 ++#define PCIE_LCAP_ASPM_LEVEL 0x00000C00 /* Active State Link PM Support */ ++#define PCIE_LCAP_ASPM_LEVEL_S 10 ++#define PCIE_LCAP_L0S_EIXT_LATENCY 0x00007000 /* L0s Exit Latency */ ++#define PCIE_LCAP_L0S_EIXT_LATENCY_S 12 ++#define PCIE_LCAP_L1_EXIT_LATENCY 0x00038000 /* L1 Exit Latency */ ++#define PCIE_LCAP_L1_EXIT_LATENCY_S 15 ++#define PCIE_LCAP_CLK_PM 0x00040000 /* Clock Power Management */ ++#define PCIE_LCAP_SDER 0x00080000 /* Surprise Down Error Reporting */ ++#define PCIE_LCAP_DLL_ACTIVE_REPROT 0x00100000 /* Data Link Layer Active Reporting Capable */ ++#define PCIE_LCAP_PORT_NUM 0xFF0000000 /* Port number */ ++#define PCIE_LCAP_PORT_NUM_S 24 ++ ++/* Maximum Length width definition */ ++#define PCIE_MAX_LENGTH_WIDTH_RES 0x00 ++#define PCIE_MAX_LENGTH_WIDTH_X1 0x01 /* Default */ ++#define PCIE_MAX_LENGTH_WIDTH_X2 0x02 ++#define PCIE_MAX_LENGTH_WIDTH_X4 0x04 ++#define PCIE_MAX_LENGTH_WIDTH_X8 0x08 ++#define PCIE_MAX_LENGTH_WIDTH_X12 0x0C ++#define PCIE_MAX_LENGTH_WIDTH_X16 0x10 ++#define PCIE_MAX_LENGTH_WIDTH_X32 0x20 ++ ++/* Active State Link PM definition */ ++enum { ++ PCIE_ASPM_RES0 = 0, ++ PCIE_ASPM_L0S_ENTRY_SUPPORT, /* L0s */ ++ PCIE_ASPM_RES1, ++ PCIE_ASPM_L0S_L1_ENTRY_SUPPORT, /* L0s and L1, default */ ++}; ++ ++/* L0s Exit Latency definition */ ++enum { ++ PCIE_L0S_EIXT_LATENCY_L64NS = 0, /* < 64 ns */ ++ PCIE_L0S_EIXT_LATENCY_B64A128, /* > 64 ns < 128 ns */ ++ PCIE_L0S_EIXT_LATENCY_B128A256, /* > 128 ns < 256 ns */ ++ PCIE_L0S_EIXT_LATENCY_B256A512, /* > 256 ns < 512 ns */ ++ PCIE_L0S_EIXT_LATENCY_B512TO1U, /* > 512 ns < 1 us */ ++ PCIE_L0S_EIXT_LATENCY_B1A2U, /* > 1 us < 2 us */ ++ PCIE_L0S_EIXT_LATENCY_B2A4U, /* > 2 us < 4 us */ ++ PCIE_L0S_EIXT_LATENCY_M4US, /* > 4 us */ ++}; ++ ++/* L1 Exit Latency definition */ ++enum { ++ PCIE_L1_EXIT_LATENCY_L1US = 0, /* < 1 us */ ++ PCIE_L1_EXIT_LATENCY_B1A2, /* > 1 us < 2 us */ ++ PCIE_L1_EXIT_LATENCY_B2A4, /* > 2 us < 4 us */ ++ PCIE_L1_EXIT_LATENCY_B4A8, /* > 4 us < 8 us */ ++ PCIE_L1_EXIT_LATENCY_B8A16, /* > 8 us < 16 us */ ++ PCIE_L1_EXIT_LATENCY_B16A32, /* > 16 us < 32 us */ ++ PCIE_L1_EXIT_LATENCY_B32A64, /* > 32 us < 64 us */ ++ PCIE_L1_EXIT_LATENCY_M64US, /* > 64 us */ ++}; ++ ++/* Link Control and Status Register */ ++#define PCIE_LCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x80) ++#define PCIE_LCTLSTS_ASPM_ENABLE 0x00000003 /* Active State Link PM Control */ ++#define PCIE_LCTLSTS_ASPM_ENABLE_S 0 ++#define PCIE_LCTLSTS_RCB128 0x00000008 /* Read Completion Boundary 128*/ ++#define PCIE_LCTLSTS_LINK_DISABLE 0x00000010 /* Link Disable */ ++#define PCIE_LCTLSTS_RETRIAN_LINK 0x00000020 /* Retrain Link */ ++#define PCIE_LCTLSTS_COM_CLK_CFG 0x00000040 /* Common Clock Configuration */ ++#define PCIE_LCTLSTS_EXT_SYNC 0x00000080 /* Extended Synch */ ++#define PCIE_LCTLSTS_CLK_PM_EN 0x00000100 /* Enable Clock Powerm Management */ ++#define PCIE_LCTLSTS_LINK_SPEED 0x000F0000 /* Link Speed */ ++#define PCIE_LCTLSTS_LINK_SPEED_S 16 ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH 0x03F00000 /* Negotiated Link Width */ ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH_S 20 ++#define PCIE_LCTLSTS_RETRAIN_PENDING 0x08000000 /* Link training is ongoing */ ++#define PCIE_LCTLSTS_SLOT_CLK_CFG 0x10000000 /* Slot Clock Configuration */ ++#define PCIE_LCTLSTS_DLL_ACTIVE 0x20000000 /* Data Link Layer Active */ ++ ++/* Slot Capabilities Register */ ++#define PCIE_SLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x84) ++ ++/* Slot Capabilities */ ++#define PCIE_SLCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x88) ++ ++/* Root Control and Capability Register */ ++#define PCIE_RCTLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x8C) ++#define PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR 0x00000001 /* #SERR on COR-ERR */ ++#define PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR 0x00000002 /* #SERR on Non-Fatal ERR */ ++#define PCIE_RCTLCAP_SERR_ON_FATAL_ERR 0x00000004 /* #SERR on Fatal ERR */ ++#define PCIE_RCTLCAP_PME_INT_EN 0x00000008 /* PME Interrupt Enable */ ++#define PCIE_RCTLCAP_SERR_ENABLE (PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR | \ ++ PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR | PCIE_RCTLCAP_SERR_ON_FATAL_ERR) ++/* Root Status Register */ ++#define PCIE_RSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x90) ++#define PCIE_RSTS_PME_REQ_ID 0x0000FFFF /* PME Request ID */ ++#define PCIE_RSTS_PME_REQ_ID_S 0 ++#define PCIE_RSTS_PME_STATUS 0x00010000 /* PME Status */ ++#define PCIE_RSTS_PME_PENDING 0x00020000 /* PME Pending */ ++ ++/* PCI Express Enhanced Capability Header */ ++#define PCIE_ENHANCED_CAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x100) ++#define PCIE_ENHANCED_CAP_ID 0x0000FFFF /* PCI Express Extended Capability ID */ ++#define PCIE_ENHANCED_CAP_ID_S 0 ++#define PCIE_ENHANCED_CAP_VER 0x000F0000 /* Capability Version */ ++#define PCIE_ENHANCED_CAP_VER_S 16 ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET 0xFFF00000 /* Next Capability Offset */ ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET_S 20 ++ ++/* Uncorrectable Error Status Register */ ++#define PCIE_UES_R(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x104) ++#define PCIE_DATA_LINK_PROTOCOL_ERR 0x00000010 /* Data Link Protocol Error Status */ ++#define PCIE_SURPRISE_DOWN_ERROR 0x00000020 /* Surprise Down Error Status */ ++#define PCIE_POISONED_TLP 0x00001000 /* Poisoned TLP Status */ ++#define PCIE_FC_PROTOCOL_ERR 0x00002000 /* Flow Control Protocol Error Status */ ++#define PCIE_COMPLETION_TIMEOUT 0x00004000 /* Completion Timeout Status */ ++#define PCIE_COMPLETOR_ABORT 0x00008000 /* Completer Abort Error */ ++#define PCIE_UNEXPECTED_COMPLETION 0x00010000 /* Unexpected Completion Status */ ++#define PCIE_RECEIVER_OVERFLOW 0x00020000 /* Receive Overflow Status */ ++#define PCIE_MALFORNED_TLP 0x00040000 /* Malformed TLP Stauts */ ++#define PCIE_ECRC_ERR 0x00080000 /* ECRC Error Stauts */ ++#define PCIE_UR_REQ 0x00100000 /* Unsupported Request Error Status */ ++#define PCIE_ALL_UNCORRECTABLE_ERR (PCIE_DATA_LINK_PROTOCOL_ERR | PCIE_SURPRISE_DOWN_ERROR | \ ++ PCIE_POISONED_TLP | PCIE_FC_PROTOCOL_ERR | PCIE_COMPLETION_TIMEOUT | \ ++ PCIE_COMPLETOR_ABORT | PCIE_UNEXPECTED_COMPLETION | PCIE_RECEIVER_OVERFLOW |\ ++ PCIE_MALFORNED_TLP | PCIE_ECRC_ERR | PCIE_UR_REQ) ++ ++/* Uncorrectable Error Mask Register, Mask means no report */ ++#define PCIE_UEMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x108) ++ ++/* Uncorrectable Error Severity Register */ ++#define PCIE_UESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10C) ++ ++/* Correctable Error Status Register */ ++#define PCIE_CESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x110) ++#define PCIE_RX_ERR 0x00000001 /* Receive Error Status */ ++#define PCIE_BAD_TLP 0x00000040 /* Bad TLP Status */ ++#define PCIE_BAD_DLLP 0x00000080 /* Bad DLLP Status */ ++#define PCIE_REPLAY_NUM_ROLLOVER 0x00000100 /* Replay Number Rollover Status */ ++#define PCIE_REPLAY_TIMER_TIMEOUT_ERR 0x00001000 /* Reply Timer Timeout Status */ ++#define PCIE_ADVISORY_NONFTAL_ERR 0x00002000 /* Advisory Non-Fatal Error Status */ ++#define PCIE_CORRECTABLE_ERR (PCIE_RX_ERR | PCIE_BAD_TLP | PCIE_BAD_DLLP | PCIE_REPLAY_NUM_ROLLOVER |\ ++ PCIE_REPLAY_TIMER_TIMEOUT_ERR | PCIE_ADVISORY_NONFTAL_ERR) ++ ++/* Correctable Error Mask Register */ ++#define PCIE_CEMR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x114) ++ ++/* Advanced Error Capabilities and Control Register */ ++#define PCIE_AECCR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x118) ++#define PCIE_AECCR_FIRST_ERR_PTR 0x0000001F /* First Error Pointer */ ++#define PCIE_AECCR_FIRST_ERR_PTR_S 0 ++#define PCIE_AECCR_ECRC_GEN_CAP 0x00000020 /* ECRC Generation Capable */ ++#define PCIE_AECCR_ECRC_GEN_EN 0x00000040 /* ECRC Generation Enable */ ++#define PCIE_AECCR_ECRC_CHECK_CAP 0x00000080 /* ECRC Check Capable */ ++#define PCIE_AECCR_ECRC_CHECK_EN 0x00000100 /* ECRC Check Enable */ ++ ++/* Header Log Register 1 */ ++#define PCIE_HLR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x11C) ++ ++/* Header Log Register 2 */ ++#define PCIE_HLR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x120) ++ ++/* Header Log Register 3 */ ++#define PCIE_HLR3(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x124) ++ ++/* Header Log Register 4 */ ++#define PCIE_HLR4(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x128) ++ ++/* Root Error Command Register */ ++#define PCIE_RECR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x12C) ++#define PCIE_RECR_CORRECTABLE_ERR_REPORT_EN 0x00000001 /* COR-ERR */ ++#define PCIE_RECR_NONFATAL_ERR_REPORT_EN 0x00000002 /* Non-Fatal ERR */ ++#define PCIE_RECR_FATAL_ERR_REPORT_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_RECR_ERR_REPORT_EN (PCIE_RECR_CORRECTABLE_ERR_REPORT_EN | \ ++ PCIE_RECR_NONFATAL_ERR_REPORT_EN | PCIE_RECR_FATAL_ERR_REPORT_EN) ++ ++/* Root Error Status Register */ ++#define PCIE_RESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x130) ++#define PCIE_RESR_CORRECTABLE_ERR 0x00000001 /* COR-ERR Receveid */ ++#define PCIE_RESR_MULTI_CORRECTABLE_ERR 0x00000002 /* Multiple COR-ERR Received */ ++#define PCIE_RESR_FATAL_NOFATAL_ERR 0x00000004 /* ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_MULTI_FATAL_NOFATAL_ERR 0x00000008 /* Multiple ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_FIRST_UNCORRECTABLE_FATAL_ERR 0x00000010 /* First UN-COR Fatal */ ++#define PCIR_RESR_NON_FATAL_ERR 0x00000020 /* Non-Fatal Error Message Received */ ++#define PCIE_RESR_FATAL_ERR 0x00000040 /* Fatal Message Received */ ++#define PCIE_RESR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_RESR_AER_INT_MSG_NUM_S 27 ++ ++/* Error Source Indentification Register */ ++#define PCIE_ESIR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x134) ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID 0x0000FFFF ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID_S 0 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID 0xFFFF0000 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID_S 16 ++ ++/* VC Enhanced Capability Header */ ++#define PCIE_VC_ECH(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x140) ++ ++/* Port VC Capability Register */ ++#define PCIE_PVC1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x144) ++#define PCIE_PVC1_EXT_VC_CNT 0x00000007 /* Extended VC Count */ ++#define PCIE_PVC1_EXT_VC_CNT_S 0 ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT 0x00000070 /* Low Priority Extended VC Count */ ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT_S 4 ++#define PCIE_PVC1_REF_CLK 0x00000300 /* Reference Clock */ ++#define PCIE_PVC1_REF_CLK_S 8 ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE 0x00000C00 /* Port Arbitration Table Entry Size */ ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE_S 10 ++ ++/* Extended Virtual Channel Count Defintion */ ++#define PCIE_EXT_VC_CNT_MIN 0 ++#define PCIE_EXT_VC_CNT_MAX 7 ++ ++/* Port Arbitration Table Entry Size Definition */ ++enum { ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S1BIT = 0, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S2BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S4BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S8BIT, ++}; ++ ++/* Port VC Capability Register 2 */ ++#define PCIE_PVC2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x148) ++#define PCIE_PVC2_VC_ARB_16P_FIXED_WRR 0x00000001 /* HW Fixed arbitration, 16 phase WRR */ ++#define PCIE_PVC2_VC_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_PVC2_VC_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_PVC2_VC_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_PVC2_VC_ARB_WRR 0x0000000F ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET 0xFF000000 /* VC arbitration table offset, not support */ ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET_S 24 ++ ++/* Port VC Control and Status Register */ ++#define PCIE_PVCCRSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14C) ++#define PCIE_PVCCRSR_LOAD_VC_ARB_TAB 0x00000001 /* Load VC Arbitration Table */ ++#define PCIE_PVCCRSR_VC_ARB_SEL 0x0000000E /* VC Arbitration Select */ ++#define PCIE_PVCCRSR_VC_ARB_SEL_S 1 ++#define PCIE_PVCCRSR_VC_ARB_TAB_STATUS 0x00010000 /* Arbitration Status */ ++ ++/* VC0 Resource Capability Register */ ++#define PCIE_VC0_RC(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x150) ++#define PCIE_VC0_RC_PORT_ARB_HW_FIXED 0x00000001 /* HW Fixed arbitration */ ++#define PCIE_VC0_RC_PORT_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_128P_WRR 0x00000010 /* Time-based 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_256P_WRR 0x00000020 /* Time-based 256 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB (PCIE_VC0_RC_PORT_ARB_HW_FIXED | PCIE_VC0_RC_PORT_ARB_32P_WRR |\ ++ PCIE_VC0_RC_PORT_ARB_64P_WRR | PCIE_VC0_RC_PORT_ARB_128P_WRR | \ ++ PCIE_VC0_RC_PORT_ARB_TM_128P_WRR | PCIE_VC0_RC_PORT_ARB_TM_256P_WRR) ++ ++#define PCIE_VC0_RC_REJECT_SNOOP 0x00008000 /* Reject Snoop Transactioin */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS 0x007F0000 /* Maximum time Slots */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS_S 16 ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET 0xFF000000 /* Port Arbitration Table Offset */ ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET_S 24 ++ ++/* VC0 Resource Control Register */ ++#define PCIE_VC0_RC0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x154) ++#define PCIE_VC0_RC0_TVM0 0x00000001 /* TC0 and VC0 */ ++#define PCIE_VC0_RC0_TVM1 0x00000002 /* TC1 and VC1 */ ++#define PCIE_VC0_RC0_TVM2 0x00000004 /* TC2 and VC2 */ ++#define PCIE_VC0_RC0_TVM3 0x00000008 /* TC3 and VC3 */ ++#define PCIE_VC0_RC0_TVM4 0x00000010 /* TC4 and VC4 */ ++#define PCIE_VC0_RC0_TVM5 0x00000020 /* TC5 and VC5 */ ++#define PCIE_VC0_RC0_TVM6 0x00000040 /* TC6 and VC6 */ ++#define PCIE_VC0_RC0_TVM7 0x00000080 /* TC7 and VC7 */ ++#define PCIE_VC0_RC0_TC_VC 0x000000FF /* TC/VC mask */ ++ ++#define PCIE_VC0_RC0_LOAD_PORT_ARB_TAB 0x00010000 /* Load Port Arbitration Table */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL 0x000E0000 /* Port Arbitration Select */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL_S 17 ++#define PCIE_VC0_RC0_VC_ID 0x07000000 /* VC ID */ ++#define PCIE_VC0_RC0_VC_ID_S 24 ++#define PCIE_VC0_RC0_VC_EN 0x80000000 /* VC Enable */ ++ ++/* VC0 Resource Status Register */ ++#define PCIE_VC0_RSR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x158) ++#define PCIE_VC0_RSR0_PORT_ARB_TAB_STATUS 0x00010000 /* Port Arbitration Table Status,not used */ ++#define PCIE_VC0_RSR0_VC_NEG_PENDING 0x00020000 /* VC Negotiation Pending */ ++ ++/* Ack Latency Timer and Replay Timer Register */ ++#define PCIE_ALTRT(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x700) ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT 0x0000FFFF /* Round Trip Latency Time Limit */ ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT_S 0 ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT 0xFFFF0000 /* Replay Time Limit */ ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT_S 16 ++ ++/* Other Message Register */ ++#define PCIE_OMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x704) ++ ++/* Port Force Link Register */ ++#define PCIE_PFLR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x708) ++#define PCIE_PFLR_LINK_NUM 0x000000FF /* Link Number */ ++#define PCIE_PFLR_LINK_NUM_S 0 ++#define PCIE_PFLR_FORCE_LINK 0x00008000 /* Force link */ ++#define PCIE_PFLR_LINK_STATE 0x003F0000 /* Link State */ ++#define PCIE_PFLR_LINK_STATE_S 16 ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT 0xFF000000 /* Low Power Entrance Count, only for EP */ ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT_S 24 ++ ++/* Ack Frequency Register */ ++#define PCIE_AFR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70C) ++#define PCIE_AFR_AF 0x000000FF /* Ack Frequency */ ++#define PCIE_AFR_AF_S 0 ++#define PCIE_AFR_FTS_NUM 0x0000FF00 /* The number of Fast Training Sequence from L0S to L0 */ ++#define PCIE_AFR_FTS_NUM_S 8 ++#define PCIE_AFR_COM_FTS_NUM 0x00FF0000 /* N_FTS; when common clock is used*/ ++#define PCIE_AFR_COM_FTS_NUM_S 16 ++#define PCIE_AFR_L0S_ENTRY_LATENCY 0x07000000 /* L0s Entrance Latency */ ++#define PCIE_AFR_L0S_ENTRY_LATENCY_S 24 ++#define PCIE_AFR_L1_ENTRY_LATENCY 0x38000000 /* L1 Entrance Latency */ ++#define PCIE_AFR_L1_ENTRY_LATENCY_S 27 ++#define PCIE_AFR_FTS_NUM_DEFAULT 32 ++#define PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT 7 ++#define PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT 5 ++ ++/* Port Link Control Register */ ++#define PCIE_PLCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x710) ++#define PCIE_PLCR_OTHER_MSG_REQ 0x00000001 /* Other Message Request */ ++#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ ++#define PCIE_PLCR_LOOPBACK_EN 0x00000004 /* Loopback Enable */ ++#define PCIE_PLCR_LTSSM_HOT_RST 0x00000008 /* Force LTSSM to the hot reset */ ++#define PCIE_PLCR_DLL_LINK_EN 0x00000020 /* Enable Link initialization */ ++#define PCIE_PLCR_FAST_LINK_SIM_EN 0x00000080 /* Sets all internal timers to fast mode for simulation purposes */ ++#define PCIE_PLCR_LINK_MODE 0x003F0000 /* Link Mode Enable Mask */ ++#define PCIE_PLCR_LINK_MODE_S 16 ++#define PCIE_PLCR_CORRUPTED_CRC_EN 0x02000000 /* Enabled Corrupt CRC */ ++ ++/* Lane Skew Register */ ++#define PCIE_LSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x714) ++#define PCIE_LSR_LANE_SKEW_NUM 0x00FFFFFF /* Insert Lane Skew for Transmit, not applicable */ ++#define PCIE_LSR_LANE_SKEW_NUM_S 0 ++#define PCIE_LSR_FC_DISABLE 0x01000000 /* Disable of Flow Control */ ++#define PCIE_LSR_ACKNAK_DISABLE 0x02000000 /* Disable of Ack/Nak */ ++#define PCIE_LSR_LANE_DESKEW_DISABLE 0x80000000 /* Disable of Lane-to-Lane Skew */ ++ ++/* Symbol Number Register */ ++#define PCIE_SNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x718) ++#define PCIE_SNR_TS 0x0000000F /* Number of TS Symbol */ ++#define PCIE_SNR_TS_S 0 ++#define PCIE_SNR_SKP 0x00000700 /* Number of SKP Symbol */ ++#define PCIE_SNR_SKP_S 8 ++#define PCIE_SNR_REPLAY_TIMER 0x0007C000 /* Timer Modifier for Replay Timer */ ++#define PCIE_SNR_REPLAY_TIMER_S 14 ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER 0x00F80000 /* Timer Modifier for Ack/Nak Latency Timer */ ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER_S 19 ++#define PCIE_SNR_FC_TIMER 0x1F000000 /* Timer Modifier for Flow Control Watchdog Timer */ ++#define PCIE_SNR_FC_TIMER_S 28 ++ ++/* Symbol Timer Register and Filter Mask Register 1 */ ++#define PCIE_STRFMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x71C) ++#define PCIE_STRFMR_SKP_INTERVAL 0x000007FF /* SKP lnterval Value */ ++#define PCIE_STRFMR_SKP_INTERVAL_S 0 ++#define PCIE_STRFMR_FC_WDT_DISABLE 0x00008000 /* Disable of FC Watchdog Timer */ ++#define PCIE_STRFMR_TLP_FUNC_MISMATCH_OK 0x00010000 /* Mask Function Mismatch Filtering for Incoming Requests */ ++#define PCIE_STRFMR_POISONED_TLP_OK 0x00020000 /* Mask Poisoned TLP Filtering */ ++#define PCIE_STRFMR_BAR_MATCH_OK 0x00040000 /* Mask BAR Match Filtering */ ++#define PCIE_STRFMR_TYPE1_CFG_REQ_OK 0x00080000 /* Mask Type 1 Configuration Request Filtering */ ++#define PCIE_STRFMR_LOCKED_REQ_OK 0x00100000 /* Mask Locked Request Filtering */ ++#define PCIE_STRFMR_CPL_TAG_ERR_RULES_OK 0x00200000 /* Mask Tag Error Rules for Received Completions */ ++#define PCIE_STRFMR_CPL_REQUESTOR_ID_MISMATCH_OK 0x00400000 /* Mask Requester ID Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_FUNC_MISMATCH_OK 0x00800000 /* Mask Function Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_TC_MISMATCH_OK 0x01000000 /* Mask Traffic Class Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_ATTR_MISMATCH_OK 0x02000000 /* Mask Attribute Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_LENGTH_MISMATCH_OK 0x04000000 /* Mask Length Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_TLP_ECRC_ERR_OK 0x08000000 /* Mask ECRC Error Filtering */ ++#define PCIE_STRFMR_CPL_TLP_ECRC_OK 0x10000000 /* Mask ECRC Error Filtering for Completions */ ++#define PCIE_STRFMR_RX_TLP_MSG_NO_DROP 0x20000000 /* Send Message TLPs */ ++#define PCIE_STRFMR_RX_IO_TRANS_ENABLE 0x40000000 /* Mask Filtering of received I/O Requests */ ++#define PCIE_STRFMR_RX_CFG_TRANS_ENABLE 0x80000000 /* Mask Filtering of Received Configuration Requests */ ++ ++#define PCIE_DEF_SKP_INTERVAL 700 /* 1180 ~1538 , 125MHz * 2, 250MHz * 1 */ ++ ++/* Filter Masker Register 2 */ ++#define PCIE_FMR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x720) ++#define PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1 0x00000001 /* Mask RADM Filtering and Error Handling Rules */ ++#define PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 0x00000002 /* Mask RADM Filtering and Error Handling Rules */ ++ ++/* Debug Register 0 */ ++#define PCIE_DBR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x728) ++ ++/* Debug Register 1 */ ++#define PCIE_DBR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x72C) ++ ++/* Transmit Posted FC Credit Status Register */ ++#define PCIE_TPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x730) ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS 0x00000FFF /* Transmit Posted Data FC Credits */ ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS_S 0 ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS 0x000FF000 /* Transmit Posted Header FC Credits */ ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Non-Posted FC Credit Status */ ++#define PCIE_TNPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x734) ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS 0x00000FFF /* Transmit Non-Posted Data FC Credits */ ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS_S 0 ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS 0x000FF000 /* Transmit Non-Posted Header FC Credits */ ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Complete FC Credit Status Register */ ++#define PCIE_TCFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x738) ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS 0x00000FFF /* Transmit Completion Data FC Credits */ ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS_S 0 ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS 0x000FF000 /* Transmit Completion Header FC Credits */ ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS_S 12 ++ ++/* Queue Status Register */ ++#define PCIE_QSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x73C) ++#define PCIE_QSR_WAIT_UPDATE_FC_DLL 0x00000001 /* Received TLP FC Credits Not Returned */ ++#define PCIE_QSR_TX_RETRY_BUF_NOT_EMPTY 0x00000002 /* Transmit Retry Buffer Not Empty */ ++#define PCIE_QSR_RX_QUEUE_NOT_EMPTY 0x00000004 /* Received Queue Not Empty */ ++ ++/* VC Transmit Arbitration Register 1 */ ++#define PCIE_VCTAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x740) ++#define PCIE_VCTAR1_WRR_WEIGHT_VC0 0x000000FF /* WRR Weight for VC0 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC1 0x0000FF00 /* WRR Weight for VC1 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC2 0x00FF0000 /* WRR Weight for VC2 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC3 0xFF000000 /* WRR Weight for VC3 */ ++ ++/* VC Transmit Arbitration Register 2 */ ++#define PCIE_VCTAR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x744) ++#define PCIE_VCTAR2_WRR_WEIGHT_VC4 0x000000FF /* WRR Weight for VC4 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC5 0x0000FF00 /* WRR Weight for VC5 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC6 0x00FF0000 /* WRR Weight for VC6 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC7 0xFF000000 /* WRR Weight for VC7 */ ++ ++/* VC0 Posted Receive Queue Control Register */ ++#define PCIE_VC0_PRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x748) ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS 0x00000FFF /* VC0 Posted Data Credits */ ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS_S 0 ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS 0x000FF000 /* VC0 Posted Header Credits */ ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS_S 12 ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE 0x00E00000 /* VC0 Posted TLP Queue Mode */ ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE_S 20 ++#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ ++#define PCIE_VC0_PRQCR_VC_STRICT_ORDER 0x80000000 /* VC0 Ordering for Receive Queues */ ++ ++/* VC0 Non-Posted Receive Queue Control */ ++#define PCIE_VC0_NPRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74C) ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS 0x00000FFF /* VC0 Non-Posted Data Credits */ ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS_S 0 ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS 0x000FF000 /* VC0 Non-Posted Header Credits */ ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS_S 12 ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE 0x00E00000 /* VC0 Non-Posted TLP Queue Mode */ ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE_S 20 ++ ++/* VC0 Completion Receive Queue Control */ ++#define PCIE_VC0_CRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x750) ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS 0x00000FFF /* VC0 Completion TLP Queue Mode */ ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS_S 0 ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS 0x000FF000 /* VC0 Completion Header Credits */ ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS_S 12 ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE 0x00E00000 /* VC0 Completion Data Credits */ ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE_S 21 ++ ++/* Applicable to the above three registers */ ++enum { ++ PCIE_VC0_TLP_QUEUE_MODE_STORE_FORWARD = 1, ++ PCIE_VC0_TLP_QUEUE_MODE_CUT_THROUGH = 2, ++ PCIE_VC0_TLP_QUEUE_MODE_BYPASS = 4, ++}; ++ ++/* VC0 Posted Buffer Depth Register */ ++#define PCIE_VC0_PBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7A8) ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Posted Data Queue Depth */ ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Posted Header Queue Depth */ ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Non-Posted Buffer Depth Register */ ++#define PCIE_VC0_NPBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7AC) ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Non-Posted Data Queue Depth */ ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Non-Posted Header Queue Depth */ ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Completion Buffer Depth Register */ ++#define PCIE_VC0_CBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7B0) ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES 0x00003FFF /* C0 Completion Data Queue Depth */ ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Completion Header Queue Depth */ ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES_S 16 ++ ++/* PHY Status Register, all zeros in VR9 */ ++#define PCIE_PHYSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x810) ++ ++/* PHY Control Register, all zeros in VR9 */ ++#define PCIE_PHYCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x814) ++ ++/* ++ * PCIe PDI PHY register definition, suppose all the following ++ * stuff is confidential. ++ * XXX, detailed bit definition ++ */ ++#define PCIE_PHY_PLL_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x22 << 1)) ++#define PCIE_PHY_PLL_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x23 << 1)) ++#define PCIE_PHY_PLL_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x24 << 1)) ++#define PCIE_PHY_PLL_CTRL4(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x25 << 1)) ++#define PCIE_PHY_PLL_CTRL5(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x26 << 1)) ++#define PCIE_PHY_PLL_CTRL6(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x27 << 1)) ++#define PCIE_PHY_PLL_CTRL7(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x28 << 1)) ++#define PCIE_PHY_PLL_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x29 << 1)) ++#define PCIE_PHY_PLL_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2A << 1)) ++#define PCIE_PHY_PLL_A_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2B << 1)) ++#define PCIE_PHY_PLL_STATUS(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2C << 1)) ++ ++#define PCIE_PHY_TX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x30 << 1)) ++#define PCIE_PHY_TX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x31 << 1)) ++#define PCIE_PHY_TX1_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x32 << 1)) ++#define PCIE_PHY_TX1_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x33 << 1)) ++#define PCIE_PHY_TX1_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x34 << 1)) ++#define PCIE_PHY_TX1_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x35 << 1)) ++#define PCIE_PHY_TX1_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x36 << 1)) ++#define PCIE_PHY_TX1_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x37 << 1)) ++ ++#define PCIE_PHY_TX2_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x38 << 1)) ++#define PCIE_PHY_TX2_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x39 << 1)) ++#define PCIE_PHY_TX2_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3B << 1)) ++#define PCIE_PHY_TX2_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3C << 1)) ++#define PCIE_PHY_TX2_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3D << 1)) ++#define PCIE_PHY_TX2_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3E << 1)) ++#define PCIE_PHY_TX2_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3F << 1)) ++ ++#define PCIE_PHY_RX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x50 << 1)) ++#define PCIE_PHY_RX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x51 << 1)) ++#define PCIE_PHY_RX1_CDR(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x52 << 1)) ++#define PCIE_PHY_RX1_EI(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x53 << 1)) ++#define PCIE_PHY_RX1_A_CTRL(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x55 << 1)) ++ ++/* Interrupt related stuff */ ++#define PCIE_LEGACY_DISABLE 0 ++#define PCIE_LEGACY_INTA 1 ++#define PCIE_LEGACY_INTB 2 ++#define PCIE_LEGACY_INTC 3 ++#define PCIE_LEGACY_INTD 4 ++#define PCIE_LEGACY_INT_MAX PCIE_LEGACY_INTD ++ ++#define PCIE_IRQ_LOCK(lock) do { \ ++ unsigned long flags; \ ++ spin_lock_irqsave(&(lock), flags); ++#define PCIE_IRQ_UNLOCK(lock) \ ++ spin_unlock_irqrestore(&(lock), flags); \ ++} while (0) ++ ++#define PCIE_MSG_MSI 0x00000001 ++#define PCIE_MSG_ISR 0x00000002 ++#define PCIE_MSG_FIXUP 0x00000004 ++#define PCIE_MSG_READ_CFG 0x00000008 ++#define PCIE_MSG_WRITE_CFG 0x00000010 ++#define PCIE_MSG_CFG (PCIE_MSG_READ_CFG | PCIE_MSG_WRITE_CFG) ++#define PCIE_MSG_REG 0x00000020 ++#define PCIE_MSG_INIT 0x00000040 ++#define PCIE_MSG_ERR 0x00000080 ++#define PCIE_MSG_PHY 0x00000100 ++#define PCIE_MSG_ANY 0x000001ff ++ ++#define IFX_PCIE_PORT0 0 ++#define IFX_PCIE_PORT1 1 ++ ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++#define IFX_PCIE_CORE_NR 2 ++#else ++#define IFX_PCIE_CORE_NR 1 ++#endif ++ ++//#define IFX_PCIE_ERROR_INT ++ ++//#define IFX_PCIE_DBG ++ ++#if defined(IFX_PCIE_DBG) ++#define IFX_PCIE_PRINT(_m, _fmt, args...) do { \ ++ if (g_pcie_debug_flag & (_m)) { \ ++ ifx_pcie_debug((_fmt), ##args); \ ++ } \ ++} while (0) ++ ++#define INLINE ++#else ++#define IFX_PCIE_PRINT(_m, _fmt, args...) \ ++ do {} while(0) ++#define INLINE inline ++#endif ++ ++struct ifx_pci_controller { ++ struct pci_controller pcic; ++ ++ /* RC specific, per host bus information */ ++ u32 port; /* Port index, 0 -- 1st core, 1 -- 2nd core */ ++}; ++ ++typedef struct ifx_pcie_ir_irq { ++ const unsigned int irq; ++ const char name[16]; ++}ifx_pcie_ir_irq_t; ++ ++typedef struct ifx_pcie_legacy_irq{ ++ const u32 irq_bit; ++ const int irq; ++}ifx_pcie_legacy_irq_t; ++ ++typedef struct ifx_pcie_irq { ++ ifx_pcie_ir_irq_t ir_irq; ++ ifx_pcie_legacy_irq_t legacy_irq[PCIE_LEGACY_INT_MAX]; ++}ifx_pcie_irq_t; ++ ++extern u32 g_pcie_debug_flag; ++extern void ifx_pcie_debug(const char *fmt, ...); ++extern void pcie_phy_clock_mode_setup(int pcie_port); ++extern void pcie_msi_pic_init(int pcie_port); ++extern u32 ifx_pcie_bus_enum_read_hack(int where, u32 value); ++extern u32 ifx_pcie_bus_enum_write_hack(int where, u32 value); ++ ++ ++#include <linux/types.h> ++#include <linux/delay.h> ++#include <linux/gpio.h> ++#include <linux/clk.h> ++ ++#include <lantiq_soc.h> ++ ++#define IFX_PCIE_GPIO_RESET 38 ++#define IFX_REG_R32 ltq_r32 ++#define IFX_REG_W32 ltq_w32 ++#define CONFIG_IFX_PCIE_HW_SWAP ++#define IFX_RCU_AHB_ENDIAN ((volatile u32*)(IFX_RCU + 0x004C)) ++#define IFX_RCU_RST_REQ ((volatile u32*)(IFX_RCU + 0x0010)) ++#define IFX_RCU_AHB_BE_PCIE_PDI 0x00000080 /* Configure PCIE PDI module in big endian*/ ++ ++#define IFX_RCU (KSEG1 | 0x1F203000) ++#define IFX_RCU_AHB_BE_PCIE_M 0x00000001 /* Configure AHB master port that connects to PCIe RC in big endian */ ++#define IFX_RCU_AHB_BE_PCIE_S 0x00000010 /* Configure AHB slave port that connects to PCIe RC in little endian */ ++#define IFX_RCU_AHB_BE_XBAR_M 0x00000002 /* Configure AHB master port that connects to XBAR in big endian */ ++#define CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ ++#define IFX_PMU1_MODULE_PCIE_PHY (0) ++#define IFX_PMU1_MODULE_PCIE_CTRL (1) ++#define IFX_PMU1_MODULE_PDI (4) ++#define IFX_PMU1_MODULE_MSI (5) ++ ++#define IFX_PMU_MODULE_PCIE_L0_CLK (31) ++ ++ ++static inline void pcie_ep_gpio_rst_init(int pcie_port) ++{ ++} ++ ++static inline void pcie_ahb_pmu_setup(void) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "ahb"); ++ clk_enable(clk); ++ //ltq_pmu_enable(PMU_AHBM | PMU_AHBS); ++} ++ ++static inline void pcie_rcu_endian_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg |= IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#else ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg &= ~IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++ IFX_PCIE_PRINT(PCIE_MSG_REG, "%s IFX_RCU_AHB_ENDIAN: 0x%08x\n", __func__, IFX_REG_R32(IFX_RCU_AHB_ENDIAN)); ++} ++ ++static inline void pcie_phy_pmu_enable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "phy"); ++ clk_enable(clk); ++ //ltq_pmu1_enable(1<<IFX_PMU1_MODULE_PCIE_PHY); ++} ++ ++static inline void pcie_phy_pmu_disable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "phy"); ++ clk_disable(clk); ++ //ltq_pmu1_disable(1<<IFX_PMU1_MODULE_PCIE_PHY); ++} ++ ++static inline void pcie_pdi_big_endian(int pcie_port) ++{ ++ u32 reg; ++ ++ /* SRAM2PDI endianness control. */ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++ /* Config AHB->PCIe and PDI endianness */ ++ reg |= IFX_RCU_AHB_BE_PCIE_PDI; ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++} ++ ++static inline void pcie_pdi_pmu_enable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "pdi"); ++ clk_enable(clk); ++ //ltq_pmu1_enable(1<<IFX_PMU1_MODULE_PDI); ++} ++ ++static inline void pcie_core_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ ++ /* Reset PCIe PHY & Core, bit 22, bit 26 may be affected if write it directly */ ++ reg |= 0x00400000; ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_core_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ /* Reset PCIe PHY & Core, bit 22 */ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg &= ~0x00400000; ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_assert(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg |= 0x00001000; /* Bit 12 */ ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_phy_rst_deassert(int pcie_port) ++{ ++ u32 reg; ++ ++ /* Make sure one micro-second delay */ ++ udelay(1); ++ ++ reg = IFX_REG_R32(IFX_RCU_RST_REQ); ++ reg &= ~0x00001000; /* Bit 12 */ ++ IFX_REG_W32(reg, IFX_RCU_RST_REQ); ++} ++ ++static inline void pcie_device_rst_assert(int pcie_port) ++{ ++ gpio_set_value(IFX_PCIE_GPIO_RESET, 0); ++ // ifx_gpio_output_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++} ++ ++static inline void pcie_device_rst_deassert(int pcie_port) ++{ ++ mdelay(100); ++ gpio_set_value(IFX_PCIE_GPIO_RESET, 1); ++// ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); ++} ++ ++static inline void pcie_core_pmu_setup(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "ctl"); ++ clk_enable(clk); ++ clk = clk_get_sys("ltq_pcie", "bus"); ++ clk_enable(clk); ++ ++ //ltq_pmu1_enable(1 << IFX_PMU1_MODULE_PCIE_CTRL); ++ //ltq_pmu_enable(1 << IFX_PMU_MODULE_PCIE_L0_CLK); ++} ++ ++static inline void pcie_msi_init(int pcie_port) ++{ ++ struct clk *clk; ++ pcie_msi_pic_init(pcie_port); ++ clk = clk_get_sys("ltq_pcie", "msi"); ++ clk_enable(clk); ++ //ltq_pmu1_enable(1 << IFX_PMU1_MODULE_MSI); ++} ++ ++static inline u32 ++ifx_pcie_bus_nr_deduct(u32 bus_number, int pcie_port) ++{ ++ u32 tbus_number = bus_number; ++ ++#ifdef CONFIG_PCI_LANTIQ ++ if (pcibios_host_nr() > 1) { ++ tbus_number -= pcibios_1st_host_bus_nr(); ++ } ++#endif /* CONFIG_PCI_LANTIQ */ ++ return tbus_number; ++} ++ ++static inline u32 ++ifx_pcie_bus_enum_hack(struct pci_bus *bus, u32 devfn, int where, u32 value, int pcie_port, int read) ++{ ++ struct pci_dev *pdev; ++ u32 tvalue = value; ++ ++ /* Sanity check */ ++ pdev = pci_get_slot(bus, devfn); ++ if (pdev == NULL) { ++ return tvalue; ++ } ++ ++ /* Only care about PCI bridge */ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { ++ return tvalue; ++ } ++ ++ if (read) { /* Read hack */ ++ #ifdef CONFIG_PCI_LANTIQ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_read_hack(where, tvalue); ++ } ++ #endif /* CONFIG_PCI_LANTIQ */ ++ } ++ else { /* Write hack */ ++ #ifdef CONFIG_PCI_LANTIQ ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_write_hack(where, tvalue); ++ } ++ #endif ++ } ++ return tvalue; ++} ++ ++#endif /* IFXMIPS_PCIE_VR9_H */ ++ +--- a/drivers/pci/pcie/Kconfig ++++ b/drivers/pci/pcie/Kconfig +@@ -51,6 +51,7 @@ config PCIEAER_INJECT + config PCIE_ECRC + bool "PCI Express ECRC settings control" + depends on PCIEAER ++ default n + help + Used to override firmware/bios settings for PCI Express ECRC + (transaction layer end-to-end CRC checking). +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1420,6 +1420,8 @@ void pci_walk_bus(struct pci_bus *top, i + void *userdata); + int pci_cfg_space_size(struct pci_dev *dev); + unsigned char pci_bus_max_busnr(struct pci_bus *bus); ++int pcibios_host_nr(void); ++int pcibios_1st_host_bus_nr(void); + void pci_setup_bridge(struct pci_bus *bus); + resource_size_t pcibios_window_alignment(struct pci_bus *bus, + unsigned long type); +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -1079,6 +1079,12 @@ + #define PCI_DEVICE_ID_SGI_IOC3 0x0003 + #define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 + ++#define PCI_VENDOR_ID_INFINEON 0x15D1 ++#define PCI_DEVICE_ID_INFINEON_DANUBE 0x000F ++#define PCI_DEVICE_ID_INFINEON_PCIE 0x0011 ++#define PCI_VENDOR_ID_LANTIQ 0x1BEF ++#define PCI_DEVICE_ID_LANTIQ_PCIE 0x0011 ++ + #define PCI_VENDOR_ID_WINBOND 0x10ad + #define PCI_DEVICE_ID_WINBOND_82C105 0x0105 + #define PCI_DEVICE_ID_WINBOND_83C553 0x0565 diff --git a/target/linux/lantiq/patches-5.15/0004-MIPS-lantiq-add-atm-hack.patch b/target/linux/lantiq/patches-5.15/0004-MIPS-lantiq-add-atm-hack.patch new file mode 100644 index 0000000000..e215df60ee --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0004-MIPS-lantiq-add-atm-hack.patch @@ -0,0 +1,482 @@ +From 9afadf01b1be371ee88491819aa67364684461f9 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Fri, 3 Aug 2012 10:27:25 +0200 +Subject: [PATCH 04/36] MIPS: lantiq: add atm hack + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/include/asm/mach-lantiq/lantiq_atm.h | 196 +++++++++++++++++++++++ + arch/mips/include/asm/mach-lantiq/lantiq_ptm.h | 203 ++++++++++++++++++++++++ + arch/mips/lantiq/irq.c | 2 + + arch/mips/mm/cache.c | 4 + + include/uapi/linux/atm.h | 6 + + net/atm/common.c | 6 + + net/atm/proc.c | 2 +- + 7 files changed, 416 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/include/asm/mach-lantiq/lantiq_atm.h + create mode 100644 arch/mips/include/asm/mach-lantiq/lantiq_ptm.h + +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/lantiq_atm.h +@@ -0,0 +1,196 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifx_atm.h ++** PROJECT : UEIP ++** MODULES : ATM ++** ++** DATE : 17 Jun 2009 ++** AUTHOR : Xu Liang ++** DESCRIPTION : Global ATM driver header file ++** COPYRIGHT : Copyright (c) 2006 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** ++** HISTORY ++** $Date $Author $Comment ++** 07 JUL 2009 Xu Liang Init Version ++*******************************************************************************/ ++ ++#ifndef IFX_ATM_H ++#define IFX_ATM_H ++ ++ ++ ++/*! ++ \defgroup IFX_ATM UEIP Project - ATM driver module ++ \brief UEIP Project - ATM driver module, support Danube, Amazon-SE, AR9, VR9. ++ */ ++ ++/*! ++ \defgroup IFX_ATM_IOCTL IOCTL Commands ++ \ingroup IFX_ATM ++ \brief IOCTL Commands used by user application. ++ */ ++ ++/*! ++ \defgroup IFX_ATM_STRUCT Structures ++ \ingroup IFX_ATM ++ \brief Structures used by user application. ++ */ ++ ++/*! ++ \file ifx_atm.h ++ \ingroup IFX_ATM ++ \brief ATM driver header file ++ */ ++ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++/*! ++ \addtogroup IFX_ATM_STRUCT ++ */ ++/*@{*/ ++ ++/* ++ * ATM MIB ++ */ ++ ++/*! ++ \struct atm_cell_ifEntry_t ++ \brief Structure used for Cell Level MIB Counters. ++ ++ User application use this structure to call IOCTL command "PPE_ATM_MIB_CELL". ++ */ ++typedef struct { ++ __u32 ifHCInOctets_h; /*!< byte counter of ingress cells (upper 32 bits, total 64 bits) */ ++ __u32 ifHCInOctets_l; /*!< byte counter of ingress cells (lower 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_h; /*!< byte counter of egress cells (upper 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_l; /*!< byte counter of egress cells (lower 32 bits, total 64 bits) */ ++ __u32 ifInErrors; /*!< counter of error ingress cells */ ++ __u32 ifInUnknownProtos; /*!< counter of unknown ingress cells */ ++ __u32 ifOutErrors; /*!< counter of error egress cells */ ++} atm_cell_ifEntry_t; ++ ++/*! ++ \struct atm_aal5_ifEntry_t ++ \brief Structure used for AAL5 Frame Level MIB Counters. ++ ++ User application use this structure to call IOCTL command "PPE_ATM_MIB_AAL5". ++ */ ++typedef struct { ++ __u32 ifHCInOctets_h; /*!< byte counter of ingress packets (upper 32 bits, total 64 bits) */ ++ __u32 ifHCInOctets_l; /*!< byte counter of ingress packets (lower 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_h; /*!< byte counter of egress packets (upper 32 bits, total 64 bits) */ ++ __u32 ifHCOutOctets_l; /*!< byte counter of egress packets (lower 32 bits, total 64 bits) */ ++ __u32 ifInUcastPkts; /*!< counter of ingress packets */ ++ __u32 ifOutUcastPkts; /*!< counter of egress packets */ ++ __u32 ifInErrors; /*!< counter of error ingress packets */ ++ __u32 ifInDiscards; /*!< counter of dropped ingress packets */ ++ __u32 ifOutErros; /*!< counter of error egress packets */ ++ __u32 ifOutDiscards; /*!< counter of dropped egress packets */ ++} atm_aal5_ifEntry_t; ++ ++/*! ++ \struct atm_aal5_vcc_t ++ \brief Structure used for per PVC AAL5 Frame Level MIB Counters. ++ ++ This structure is a part of structure "atm_aal5_vcc_x_t". ++ */ ++typedef struct { ++ __u32 aal5VccCrcErrors; /*!< counter of ingress packets with CRC error */ ++ __u32 aal5VccSarTimeOuts; /*!< counter of ingress packets with Re-assemble timeout */ //no timer support yet ++ __u32 aal5VccOverSizedSDUs; /*!< counter of oversized ingress packets */ ++} atm_aal5_vcc_t; ++ ++/*! ++ \struct atm_aal5_vcc_x_t ++ \brief Structure used for per PVC AAL5 Frame Level MIB Counters. ++ ++ User application use this structure to call IOCTL command "PPE_ATM_MIB_VCC". ++ */ ++typedef struct { ++ int vpi; /*!< VPI of the VCC to get MIB counters */ ++ int vci; /*!< VCI of the VCC to get MIB counters */ ++ atm_aal5_vcc_t mib_vcc; /*!< structure to get MIB counters */ ++} atm_aal5_vcc_x_t; ++ ++/*@}*/ ++ ++ ++ ++/* ++ * #################################### ++ * IOCTL ++ * #################################### ++ */ ++ ++/*! ++ \addtogroup IFX_ATM_IOCTL ++ */ ++/*@{*/ ++ ++/* ++ * ioctl Command ++ */ ++/*! ++ \brief ATM IOCTL Magic Number ++ */ ++#define PPE_ATM_IOC_MAGIC 'o' ++/*! ++ \brief ATM IOCTL Command - Get Cell Level MIB Counters ++ ++ This command is obsolete. User can get cell level MIB from DSL API. ++ This command uses structure "atm_cell_ifEntry_t" as parameter for output of MIB counters. ++ */ ++#define PPE_ATM_MIB_CELL _IOW(PPE_ATM_IOC_MAGIC, 0, atm_cell_ifEntry_t) ++/*! ++ \brief ATM IOCTL Command - Get AAL5 Level MIB Counters ++ ++ Get AAL5 packet counters. ++ This command uses structure "atm_aal5_ifEntry_t" as parameter for output of MIB counters. ++ */ ++#define PPE_ATM_MIB_AAL5 _IOW(PPE_ATM_IOC_MAGIC, 1, atm_aal5_ifEntry_t) ++/*! ++ \brief ATM IOCTL Command - Get Per PVC MIB Counters ++ ++ Get AAL5 packet counters for each PVC. ++ This command uses structure "atm_aal5_vcc_x_t" as parameter for input of VPI/VCI information and output of MIB counters. ++ */ ++#define PPE_ATM_MIB_VCC _IOWR(PPE_ATM_IOC_MAGIC, 2, atm_aal5_vcc_x_t) ++/*! ++ \brief Total Number of ATM IOCTL Commands ++ */ ++#define PPE_ATM_IOC_MAXNR 3 ++ ++/*@}*/ ++ ++ ++ ++/* ++ * #################################### ++ * API ++ * #################################### ++ */ ++ ++#ifdef __KERNEL__ ++struct port_cell_info { ++ unsigned int port_num; ++ unsigned int tx_link_rate[2]; ++}; ++#endif ++ ++ ++ ++#endif // IFX_ATM_H ++ +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/lantiq_ptm.h +@@ -0,0 +1,203 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifx_ptm.h ++** PROJECT : UEIP ++** MODULES : PTM ++** ++** DATE : 17 Jun 2009 ++** AUTHOR : Xu Liang ++** DESCRIPTION : Global PTM driver header file ++** COPYRIGHT : Copyright (c) 2006 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** ++** HISTORY ++** $Date $Author $Comment ++** 07 JUL 2009 Xu Liang Init Version ++*******************************************************************************/ ++ ++#ifndef IFX_PTM_H ++#define IFX_PTM_H ++ ++ ++ ++/*! ++ \defgroup IFX_PTM UEIP Project - PTM driver module ++ \brief UEIP Project - PTM driver module, support Danube, Amazon-SE, AR9, VR9. ++ */ ++ ++/*! ++ \defgroup IFX_PTM_IOCTL IOCTL Commands ++ \ingroup IFX_PTM ++ \brief IOCTL Commands used by user application. ++ */ ++ ++/*! ++ \defgroup IFX_PTM_STRUCT Structures ++ \ingroup IFX_PTM ++ \brief Structures used by user application. ++ */ ++ ++/*! ++ \file ifx_ptm.h ++ \ingroup IFX_PTM ++ \brief PTM driver header file ++ */ ++ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++ ++ ++/* ++ * #################################### ++ * IOCTL ++ * #################################### ++ */ ++ ++/*! ++ \addtogroup IFX_PTM_IOCTL ++ */ ++/*@{*/ ++ ++/* ++ * ioctl Command ++ */ ++/*! ++ \brief PTM IOCTL Command - Get codeword MIB counters. ++ ++ This command uses structure "PTM_CW_IF_ENTRY_T" to get codeword level MIB counters. ++ */ ++#define IFX_PTM_MIB_CW_GET SIOCDEVPRIVATE + 1 ++/*! ++ \brief PTM IOCTL Command - Get packet MIB counters. ++ ++ This command uses structure "PTM_FRAME_MIB_T" to get packet level MIB counters. ++ */ ++#define IFX_PTM_MIB_FRAME_GET SIOCDEVPRIVATE + 2 ++/*! ++ \brief PTM IOCTL Command - Get firmware configuration (CRC). ++ ++ This command uses structure "IFX_PTM_CFG_T" to get firmware configuration (CRC). ++ */ ++#define IFX_PTM_CFG_GET SIOCDEVPRIVATE + 3 ++/*! ++ \brief PTM IOCTL Command - Set firmware configuration (CRC). ++ ++ This command uses structure "IFX_PTM_CFG_T" to set firmware configuration (CRC). ++ */ ++#define IFX_PTM_CFG_SET SIOCDEVPRIVATE + 4 ++/*! ++ \brief PTM IOCTL Command - Program priority value to TX queue mapping. ++ ++ This command uses structure "IFX_PTM_PRIO_Q_MAP_T" to program priority value to TX queue mapping. ++ */ ++#define IFX_PTM_MAP_PKT_PRIO_TO_Q SIOCDEVPRIVATE + 14 ++ ++/*@}*/ ++ ++ ++/*! ++ \addtogroup IFX_PTM_STRUCT ++ */ ++/*@{*/ ++ ++/* ++ * ioctl Data Type ++ */ ++ ++/*! ++ \typedef PTM_CW_IF_ENTRY_T ++ \brief Wrapping of structure "ptm_cw_ifEntry_t". ++ */ ++/*! ++ \struct ptm_cw_ifEntry_t ++ \brief Structure used for CodeWord level MIB counters. ++ */ ++typedef struct ptm_cw_ifEntry_t { ++ uint32_t ifRxNoIdleCodewords; /*!< output, number of ingress user codeword */ ++ uint32_t ifRxIdleCodewords; /*!< output, number of ingress idle codeword */ ++ uint32_t ifRxCodingViolation; /*!< output, number of error ingress codeword */ ++ uint32_t ifTxNoIdleCodewords; /*!< output, number of egress user codeword */ ++ uint32_t ifTxIdleCodewords; /*!< output, number of egress idle codeword */ ++} PTM_CW_IF_ENTRY_T; ++ ++/*! ++ \typedef PTM_FRAME_MIB_T ++ \brief Wrapping of structure "ptm_frame_mib_t". ++ */ ++/*! ++ \struct ptm_frame_mib_t ++ \brief Structure used for packet level MIB counters. ++ */ ++typedef struct ptm_frame_mib_t { ++ uint32_t RxCorrect; /*!< output, number of ingress packet */ ++ uint32_t TC_CrcError; /*!< output, number of egress packet with CRC error */ ++ uint32_t RxDropped; /*!< output, number of dropped ingress packet */ ++ uint32_t TxSend; /*!< output, number of egress packet */ ++} PTM_FRAME_MIB_T; ++ ++/*! ++ \typedef IFX_PTM_CFG_T ++ \brief Wrapping of structure "ptm_cfg_t". ++ */ ++/*! ++ \struct ptm_cfg_t ++ \brief Structure used for ETH/TC CRC configuration. ++ */ ++typedef struct ptm_cfg_t { ++ uint32_t RxEthCrcPresent; /*!< input/output, ingress packet has ETH CRC */ ++ uint32_t RxEthCrcCheck; /*!< input/output, check ETH CRC of ingress packet */ ++ uint32_t RxTcCrcCheck; /*!< input/output, check TC CRC of ingress codeword */ ++ uint32_t RxTcCrcLen; /*!< input/output, length of TC CRC of ingress codeword */ ++ uint32_t TxEthCrcGen; /*!< input/output, generate ETH CRC for egress packet */ ++ uint32_t TxTcCrcGen; /*!< input/output, generate TC CRC for egress codeword */ ++ uint32_t TxTcCrcLen; /*!< input/output, length of TC CRC of egress codeword */ ++} IFX_PTM_CFG_T; ++ ++/*! ++ \typedef IFX_PTM_PRIO_Q_MAP_T ++ \brief Wrapping of structure "ppe_prio_q_map". ++ */ ++/*! ++ \struct ppe_prio_q_map ++ \brief Structure used for Priority Value to TX Queue mapping. ++ */ ++typedef struct ppe_prio_q_map { ++ int pkt_prio; ++ int qid; ++ int vpi; // ignored in eth interface ++ int vci; // ignored in eth interface ++} IFX_PTM_PRIO_Q_MAP_T; ++ ++/*@}*/ ++ ++ ++ ++/* ++ * #################################### ++ * API ++ * #################################### ++ */ ++ ++#ifdef __KERNEL__ ++struct port_cell_info { ++ unsigned int port_num; ++ unsigned int tx_link_rate[2]; ++}; ++#endif ++ ++ ++ ++#endif // IFX_PTM_H ++ +--- a/arch/mips/lantiq/irq.c ++++ b/arch/mips/lantiq/irq.c +@@ -12,6 +12,7 @@ + #include <linux/of_platform.h> + #include <linux/of_address.h> + #include <linux/of_irq.h> ++#include <linux/module.h> + + #include <asm/bootinfo.h> + #include <asm/irq_cpu.h> +@@ -91,6 +92,7 @@ void ltq_disable_irq(struct irq_data *d) + } + raw_spin_unlock_irqrestore(<q_icu_lock, flags); + } ++EXPORT_SYMBOL(ltq_mask_and_ack_irq); + + void ltq_mask_and_ack_irq(struct irq_data *d) + { +--- a/arch/mips/mm/cache.c ++++ b/arch/mips/mm/cache.c +@@ -61,6 +61,10 @@ void (*_dma_cache_wback_inv)(unsigned lo + void (*_dma_cache_wback)(unsigned long start, unsigned long size); + void (*_dma_cache_inv)(unsigned long start, unsigned long size); + ++EXPORT_SYMBOL(_dma_cache_wback_inv); ++EXPORT_SYMBOL(_dma_cache_wback); ++EXPORT_SYMBOL(_dma_cache_inv); ++ + #endif /* CONFIG_DMA_NONCOHERENT */ + + /* +--- a/include/uapi/linux/atm.h ++++ b/include/uapi/linux/atm.h +@@ -131,8 +131,14 @@ + #define ATM_ABR 4 + #define ATM_ANYCLASS 5 /* compatible with everything */ + ++#define ATM_VBR_NRT ATM_VBR ++#define ATM_VBR_RT 6 ++#define ATM_UBR_PLUS 7 ++#define ATM_GFR 8 ++ + #define ATM_MAX_PCR -1 /* maximum available PCR */ + ++ + struct atm_trafprm { + unsigned char traffic_class; /* traffic class (ATM_UBR, ...) */ + int max_pcr; /* maximum PCR in cells per second */ +--- a/net/atm/proc.c ++++ b/net/atm/proc.c +@@ -141,7 +141,7 @@ static void *vcc_seq_next(struct seq_fil + static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc) + { + static const char *const class_name[] = { +- "off", "UBR", "CBR", "VBR", "ABR"}; ++ "off","UBR","CBR","NTR-VBR","ABR","ANY","RT-VBR","UBR+","GFR"}; + static const char *const aal_name[] = { + "---", "1", "2", "3/4", /* 0- 3 */ + "???", "5", "???", "???", /* 4- 7 */ diff --git a/target/linux/lantiq/patches-5.15/0008-MIPS-lantiq-backport-old-timer-code.patch b/target/linux/lantiq/patches-5.15/0008-MIPS-lantiq-backport-old-timer-code.patch new file mode 100644 index 0000000000..1d869afd9a --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0008-MIPS-lantiq-backport-old-timer-code.patch @@ -0,0 +1,1035 @@ +From 94800350cb8d2f29dda2206b5e9a3772024ee168 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 7 Aug 2014 18:30:56 +0200 +Subject: [PATCH 08/36] MIPS: lantiq: backport old timer code + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + arch/mips/include/asm/mach-lantiq/lantiq_timer.h | 155 ++++ + arch/mips/lantiq/xway/Makefile | 2 +- + arch/mips/lantiq/xway/timer.c | 845 ++++++++++++++++++++++ + 3 files changed, 1001 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/include/asm/mach-lantiq/lantiq_timer.h + create mode 100644 arch/mips/lantiq/xway/timer.c + +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/lantiq_timer.h +@@ -0,0 +1,155 @@ ++#ifndef __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ ++#define __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ ++ ++ ++/****************************************************************************** ++ Copyright (c) 2002, Infineon Technologies. All rights reserved. ++ ++ No Warranty ++ Because the program is licensed free of charge, there is no warranty for ++ the program, to the extent permitted by applicable law. Except when ++ otherwise stated in writing the copyright holders and/or other parties ++ provide the program "as is" without warranty of any kind, either ++ expressed or implied, including, but not limited to, the implied ++ warranties of merchantability and fitness for a particular purpose. The ++ entire risk as to the quality and performance of the program is with ++ you. should the program prove defective, you assume the cost of all ++ necessary servicing, repair or correction. ++ ++ In no event unless required by applicable law or agreed to in writing ++ will any copyright holder, or any other party who may modify and/or ++ redistribute the program as permitted above, be liable to you for ++ damages, including any general, special, incidental or consequential ++ damages arising out of the use or inability to use the program ++ (including but not limited to loss of data or data being rendered ++ inaccurate or losses sustained by you or third parties or a failure of ++ the program to operate with any other programs), even if such holder or ++ other party has been advised of the possibility of such damages. ++******************************************************************************/ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++/* ++ * Available Timer/Counter Index ++ */ ++#define TIMER(n, X) (n * 2 + (X ? 1 : 0)) ++#define TIMER_ANY 0x00 ++#define TIMER1A TIMER(1, 0) ++#define TIMER1B TIMER(1, 1) ++#define TIMER2A TIMER(2, 0) ++#define TIMER2B TIMER(2, 1) ++#define TIMER3A TIMER(3, 0) ++#define TIMER3B TIMER(3, 1) ++ ++/* ++ * Flag of Timer/Counter ++ * These flags specify the way in which timer is configured. ++ */ ++/* Bit size of timer/counter. */ ++#define TIMER_FLAG_16BIT 0x0000 ++#define TIMER_FLAG_32BIT 0x0001 ++/* Switch between timer and counter. */ ++#define TIMER_FLAG_TIMER 0x0000 ++#define TIMER_FLAG_COUNTER 0x0002 ++/* Stop or continue when overflowing/underflowing. */ ++#define TIMER_FLAG_ONCE 0x0000 ++#define TIMER_FLAG_CYCLIC 0x0004 ++/* Count up or counter down. */ ++#define TIMER_FLAG_UP 0x0000 ++#define TIMER_FLAG_DOWN 0x0008 ++/* Count on specific level or edge. */ ++#define TIMER_FLAG_HIGH_LEVEL_SENSITIVE 0x0000 ++#define TIMER_FLAG_LOW_LEVEL_SENSITIVE 0x0040 ++#define TIMER_FLAG_RISE_EDGE 0x0010 ++#define TIMER_FLAG_FALL_EDGE 0x0020 ++#define TIMER_FLAG_ANY_EDGE 0x0030 ++/* Signal is syncronous to module clock or not. */ ++#define TIMER_FLAG_UNSYNC 0x0000 ++#define TIMER_FLAG_SYNC 0x0080 ++/* Different interrupt handle type. */ ++#define TIMER_FLAG_NO_HANDLE 0x0000 ++#if defined(__KERNEL__) ++ #define TIMER_FLAG_CALLBACK_IN_IRQ 0x0100 ++#endif // defined(__KERNEL__) ++#define TIMER_FLAG_SIGNAL 0x0300 ++/* Internal clock source or external clock source */ ++#define TIMER_FLAG_INT_SRC 0x0000 ++#define TIMER_FLAG_EXT_SRC 0x1000 ++ ++ ++/* ++ * ioctl Command ++ */ ++#define GPTU_REQUEST_TIMER 0x01 /* General method to setup timer/counter. */ ++#define GPTU_FREE_TIMER 0x02 /* Free timer/counter. */ ++#define GPTU_START_TIMER 0x03 /* Start or resume timer/counter. */ ++#define GPTU_STOP_TIMER 0x04 /* Suspend timer/counter. */ ++#define GPTU_GET_COUNT_VALUE 0x05 /* Get current count value. */ ++#define GPTU_CALCULATE_DIVIDER 0x06 /* Calculate timer divider from given freq.*/ ++#define GPTU_SET_TIMER 0x07 /* Simplified method to setup timer. */ ++#define GPTU_SET_COUNTER 0x08 /* Simplified method to setup counter. */ ++ ++/* ++ * Data Type Used to Call ioctl ++ */ ++struct gptu_ioctl_param { ++ unsigned int timer; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and * ++ * GPTU_SET_COUNTER, this field is ID of expected * ++ * timer/counter. If it's zero, a timer/counter would * ++ * be dynamically allocated and ID would be stored in * ++ * this field. * ++ * In command GPTU_GET_COUNT_VALUE, this field is * ++ * ignored. * ++ * In other command, this field is ID of timer/counter * ++ * allocated. */ ++ unsigned int flag; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and * ++ * GPTU_SET_COUNTER, this field contains flags to * ++ * specify how to configure timer/counter. * ++ * In command GPTU_START_TIMER, zero indicate start * ++ * and non-zero indicate resume timer/counter. * ++ * In other command, this field is ignored. */ ++ unsigned long value; /* In command GPTU_REQUEST_TIMER, this field contains * ++ * init/reload value. * ++ * In command GPTU_SET_TIMER, this field contains * ++ * frequency (0.001Hz) of timer. * ++ * In command GPTU_GET_COUNT_VALUE, current count * ++ * value would be stored in this field. * ++ * In command GPTU_CALCULATE_DIVIDER, this field * ++ * contains frequency wanted, and after calculation, * ++ * divider would be stored in this field to overwrite * ++ * the frequency. * ++ * In other command, this field is ignored. */ ++ int pid; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, * ++ * if signal is required, this field contains process * ++ * ID to which signal would be sent. * ++ * In other command, this field is ignored. */ ++ int sig; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, * ++ * if signal is required, this field contains signal * ++ * number which would be sent. * ++ * In other command, this field is ignored. */ ++}; ++ ++/* ++ * #################################### ++ * Data Type ++ * #################################### ++ */ ++typedef void (*timer_callback)(unsigned long arg); ++ ++extern int lq_request_timer(unsigned int, unsigned int, unsigned long, unsigned long, unsigned long); ++extern int lq_free_timer(unsigned int); ++extern int lq_start_timer(unsigned int, int); ++extern int lq_stop_timer(unsigned int); ++extern int lq_reset_counter_flags(u32 timer, u32 flags); ++extern int lq_get_count_value(unsigned int, unsigned long *); ++extern u32 lq_cal_divider(unsigned long); ++extern int lq_set_timer(unsigned int, unsigned int, int, int, unsigned int, unsigned long, unsigned long); ++extern int lq_set_counter(unsigned int timer, unsigned int flag, ++ u32 reload, unsigned long arg1, unsigned long arg2); ++ ++#endif /* __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ */ +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,4 +1,10 @@ + # SPDX-License-Identifier: GPL-2.0-only +-obj-y := prom.o sysctrl.o clk.o dma.o gptu.o dcdc.o ++obj-y := prom.o sysctrl.o clk.o dma.o dcdc.o ++ ++ifdef CONFIG_SOC_AMAZON_SE ++obj-y += gptu.o ++else ++obj-y += timer.o ++endif + + obj-y += vmmc.o +--- /dev/null ++++ b/arch/mips/lantiq/xway/timer.c +@@ -0,0 +1,846 @@ ++#ifndef CONFIG_SOC_AMAZON_SE ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/types.h> ++#include <linux/fs.h> ++#include <linux/miscdevice.h> ++#include <linux/init.h> ++#include <linux/uaccess.h> ++#include <linux/unistd.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/sched.h> ++#include <linux/sched/signal.h> ++ ++#include <asm/irq.h> ++#include <asm/div64.h> ++#include "../clk.h" ++ ++#include <lantiq_soc.h> ++#include <lantiq_irq.h> ++#include <lantiq_timer.h> ++ ++#define MAX_NUM_OF_32BIT_TIMER_BLOCKS 6 ++ ++#ifdef TIMER1A ++#define FIRST_TIMER TIMER1A ++#else ++#define FIRST_TIMER 2 ++#endif ++ ++/* ++ * GPTC divider is set or not. ++ */ ++#define GPTU_CLC_RMC_IS_SET 0 ++ ++/* ++ * Timer Interrupt (IRQ) ++ */ ++/* Must be adjusted when ICU driver is available */ ++#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22) ++ ++/* ++ * Bits Operation ++ */ ++#define GET_BITS(x, msb, lsb) \ ++ (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb)) ++#define SET_BITS(x, msb, lsb, value) \ ++ (((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | \ ++ (((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb))) ++ ++/* ++ * GPTU Register Mapping ++ */ ++#define LQ_GPTU (KSEG1 + 0x1E100A00) ++#define LQ_GPTU_CLC ((volatile u32 *)(LQ_GPTU + 0x0000)) ++#define LQ_GPTU_ID ((volatile u32 *)(LQ_GPTU + 0x0008)) ++#define LQ_GPTU_CON(n, X) ((volatile u32 *)(LQ_GPTU + 0x0010 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_RUN(n, X) ((volatile u32 *)(LQ_GPTU + 0x0018 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_RELOAD(n, X) ((volatile u32 *)(LQ_GPTU + 0x0020 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_COUNT(n, X) ((volatile u32 *)(LQ_GPTU + 0x0028 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_IRNEN ((volatile u32 *)(LQ_GPTU + 0x00F4)) ++#define LQ_GPTU_IRNICR ((volatile u32 *)(LQ_GPTU + 0x00F8)) ++#define LQ_GPTU_IRNCR ((volatile u32 *)(LQ_GPTU + 0x00FC)) ++ ++/* ++ * Clock Control Register ++ */ ++#define GPTU_CLC_SMC GET_BITS(*LQ_GPTU_CLC, 23, 16) ++#define GPTU_CLC_RMC GET_BITS(*LQ_GPTU_CLC, 15, 8) ++#define GPTU_CLC_FSOE (*LQ_GPTU_CLC & (1 << 5)) ++#define GPTU_CLC_EDIS (*LQ_GPTU_CLC & (1 << 3)) ++#define GPTU_CLC_SPEN (*LQ_GPTU_CLC & (1 << 2)) ++#define GPTU_CLC_DISS (*LQ_GPTU_CLC & (1 << 1)) ++#define GPTU_CLC_DISR (*LQ_GPTU_CLC & (1 << 0)) ++ ++#define GPTU_CLC_SMC_SET(value) SET_BITS(0, 23, 16, (value)) ++#define GPTU_CLC_RMC_SET(value) SET_BITS(0, 15, 8, (value)) ++#define GPTU_CLC_FSOE_SET(value) ((value) ? (1 << 5) : 0) ++#define GPTU_CLC_SBWE_SET(value) ((value) ? (1 << 4) : 0) ++#define GPTU_CLC_EDIS_SET(value) ((value) ? (1 << 3) : 0) ++#define GPTU_CLC_SPEN_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_CLC_DISR_SET(value) ((value) ? (1 << 0) : 0) ++ ++/* ++ * ID Register ++ */ ++#define GPTU_ID_ID GET_BITS(*LQ_GPTU_ID, 15, 8) ++#define GPTU_ID_CFG GET_BITS(*LQ_GPTU_ID, 7, 5) ++#define GPTU_ID_REV GET_BITS(*LQ_GPTU_ID, 4, 0) ++ ++/* ++ * Control Register of Timer/Counter nX ++ * n is the index of block (1 based index) ++ * X is either A or B ++ */ ++#define GPTU_CON_SRC_EG(n, X) (*LQ_GPTU_CON(n, X) & (1 << 10)) ++#define GPTU_CON_SRC_EXT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 9)) ++#define GPTU_CON_SYNC(n, X) (*LQ_GPTU_CON(n, X) & (1 << 8)) ++#define GPTU_CON_EDGE(n, X) GET_BITS(*LQ_GPTU_CON(n, X), 7, 6) ++#define GPTU_CON_INV(n, X) (*LQ_GPTU_CON(n, X) & (1 << 5)) ++#define GPTU_CON_EXT(n, X) (*LQ_GPTU_CON(n, A) & (1 << 4)) /* Timer/Counter B does not have this bit */ ++#define GPTU_CON_STP(n, X) (*LQ_GPTU_CON(n, X) & (1 << 3)) ++#define GPTU_CON_CNT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 2)) ++#define GPTU_CON_DIR(n, X) (*LQ_GPTU_CON(n, X) & (1 << 1)) ++#define GPTU_CON_EN(n, X) (*LQ_GPTU_CON(n, X) & (1 << 0)) ++ ++#define GPTU_CON_SRC_EG_SET(value) ((value) ? 0 : (1 << 10)) ++#define GPTU_CON_SRC_EXT_SET(value) ((value) ? (1 << 9) : 0) ++#define GPTU_CON_SYNC_SET(value) ((value) ? (1 << 8) : 0) ++#define GPTU_CON_EDGE_SET(value) SET_BITS(0, 7, 6, (value)) ++#define GPTU_CON_INV_SET(value) ((value) ? (1 << 5) : 0) ++#define GPTU_CON_EXT_SET(value) ((value) ? (1 << 4) : 0) ++#define GPTU_CON_STP_SET(value) ((value) ? (1 << 3) : 0) ++#define GPTU_CON_CNT_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_CON_DIR_SET(value) ((value) ? (1 << 1) : 0) ++ ++#define GPTU_RUN_RL_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_RUN_CEN_SET(value) ((value) ? (1 << 1) : 0) ++#define GPTU_RUN_SEN_SET(value) ((value) ? (1 << 0) : 0) ++ ++#define GPTU_IRNEN_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0) ++#define GPTU_IRNCR_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0) ++ ++#define TIMER_FLAG_MASK_SIZE(x) (x & 0x0001) ++#define TIMER_FLAG_MASK_TYPE(x) (x & 0x0002) ++#define TIMER_FLAG_MASK_STOP(x) (x & 0x0004) ++#define TIMER_FLAG_MASK_DIR(x) (x & 0x0008) ++#define TIMER_FLAG_NONE_EDGE 0x0000 ++#define TIMER_FLAG_MASK_EDGE(x) (x & 0x0030) ++#define TIMER_FLAG_REAL 0x0000 ++#define TIMER_FLAG_INVERT 0x0040 ++#define TIMER_FLAG_MASK_INVERT(x) (x & 0x0040) ++#define TIMER_FLAG_MASK_TRIGGER(x) (x & 0x0070) ++#define TIMER_FLAG_MASK_SYNC(x) (x & 0x0080) ++#define TIMER_FLAG_CALLBACK_IN_HB 0x0200 ++#define TIMER_FLAG_MASK_HANDLE(x) (x & 0x0300) ++#define TIMER_FLAG_MASK_SRC(x) (x & 0x1000) ++ ++struct timer_dev_timer { ++ unsigned int f_irq_on; ++ unsigned int irq; ++ unsigned int flag; ++ unsigned long arg1; ++ unsigned long arg2; ++}; ++ ++struct timer_dev { ++ struct mutex gptu_mutex; ++ unsigned int number_of_timers; ++ unsigned int occupation; ++ unsigned int f_gptu_on; ++ struct timer_dev_timer timer[MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2]; ++}; ++ ++ ++unsigned int ltq_get_fpi_bus_clock(int fpi) { ++ struct clk *clk = clk_get_fpi(); ++ return clk_get_rate(clk); ++} ++ ++ ++static long gptu_ioctl(struct file *, unsigned int, unsigned long); ++static int gptu_open(struct inode *, struct file *); ++static int gptu_release(struct inode *, struct file *); ++ ++static struct file_operations gptu_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = gptu_ioctl, ++ .open = gptu_open, ++ .release = gptu_release ++}; ++ ++static struct miscdevice gptu_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "gptu", ++ .fops = &gptu_fops, ++}; ++ ++static struct timer_dev timer_dev; ++ ++static irqreturn_t timer_irq_handler(int irq, void *p) ++{ ++ unsigned int timer; ++ unsigned int flag; ++ struct timer_dev_timer *dev_timer = (struct timer_dev_timer *)p; ++ ++ timer = irq - TIMER_INTERRUPT; ++ if (timer < timer_dev.number_of_timers ++ && dev_timer == &timer_dev.timer[timer]) { ++ /* Clear interrupt. */ ++ ltq_w32(1 << timer, LQ_GPTU_IRNCR); ++ ++ /* Call user hanler or signal. */ ++ flag = dev_timer->flag; ++ if (!(timer & 0x01) ++ || TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) { ++ /* 16-bit timer or timer A of 32-bit timer */ ++ switch (TIMER_FLAG_MASK_HANDLE(flag)) { ++ case TIMER_FLAG_CALLBACK_IN_IRQ: ++ case TIMER_FLAG_CALLBACK_IN_HB: ++ if (dev_timer->arg1) ++ (*(timer_callback)dev_timer->arg1)(dev_timer->arg2); ++ break; ++ case TIMER_FLAG_SIGNAL: ++ send_sig((int)dev_timer->arg2, (struct task_struct *)dev_timer->arg1, 0); ++ break; ++ } ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++static inline void lq_enable_gptu(void) ++{ ++ struct clk *clk = clk_get_sys("1e100a00.gptu", NULL); ++ clk_enable(clk); ++ ++ //ltq_pmu_enable(PMU_GPT); ++ ++ /* Set divider as 1, disable write protection for SPEN, enable module. */ ++ *LQ_GPTU_CLC = ++ GPTU_CLC_SMC_SET(0x00) | ++ GPTU_CLC_RMC_SET(0x01) | ++ GPTU_CLC_FSOE_SET(0) | ++ GPTU_CLC_SBWE_SET(1) | ++ GPTU_CLC_EDIS_SET(0) | ++ GPTU_CLC_SPEN_SET(0) | ++ GPTU_CLC_DISR_SET(0); ++} ++ ++static inline void lq_disable_gptu(void) ++{ ++ struct clk *clk = clk_get_sys("1e100a00.gptu", NULL); ++ ltq_w32(0x00, LQ_GPTU_IRNEN); ++ ltq_w32(0xfff, LQ_GPTU_IRNCR); ++ ++ /* Set divider as 0, enable write protection for SPEN, disable module. */ ++ *LQ_GPTU_CLC = ++ GPTU_CLC_SMC_SET(0x00) | ++ GPTU_CLC_RMC_SET(0x00) | ++ GPTU_CLC_FSOE_SET(0) | ++ GPTU_CLC_SBWE_SET(0) | ++ GPTU_CLC_EDIS_SET(0) | ++ GPTU_CLC_SPEN_SET(0) | ++ GPTU_CLC_DISR_SET(1); ++ ++ clk_enable(clk); ++} ++ ++int lq_request_timer(unsigned int timer, unsigned int flag, ++ unsigned long value, unsigned long arg1, unsigned long arg2) ++{ ++ int ret = 0; ++ unsigned int con_reg, irnen_reg; ++ int n, X; ++ ++ if (timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ printk(KERN_INFO "request_timer(%d, 0x%08X, %lu)...", ++ timer, flag, value); ++ ++ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) ++ value &= 0xFFFF; ++ else ++ timer &= ~0x01; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ /* ++ * Allocate timer. ++ */ ++ if (timer < FIRST_TIMER) { ++ unsigned int mask; ++ unsigned int shift; ++ /* This takes care of TIMER1B which is the only choice for Voice TAPI system */ ++ unsigned int offset = TIMER2A; ++ ++ /* ++ * Pick up a free timer. ++ */ ++ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) { ++ mask = 1 << offset; ++ shift = 1; ++ } else { ++ mask = 3 << offset; ++ shift = 2; ++ } ++ for (timer = offset; ++ timer < offset + timer_dev.number_of_timers; ++ timer += shift, mask <<= shift) ++ if (!(timer_dev.occupation & mask)) { ++ timer_dev.occupation |= mask; ++ break; ++ } ++ if (timer >= offset + timer_dev.number_of_timers) { ++ printk("failed![%d]\n", __LINE__); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } else ++ ret = timer; ++ } else { ++ register unsigned int mask; ++ ++ /* ++ * Check if the requested timer is free. ++ */ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if ((timer_dev.occupation & mask)) { ++ printk("failed![%d] mask %#x, timer_dev.occupation %#x\n", ++ __LINE__, mask, timer_dev.occupation); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EBUSY; ++ } else { ++ timer_dev.occupation |= mask; ++ ret = 0; ++ } ++ } ++ ++ /* ++ * Prepare control register value. ++ */ ++ switch (TIMER_FLAG_MASK_EDGE(flag)) { ++ default: ++ case TIMER_FLAG_NONE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x00); ++ break; ++ case TIMER_FLAG_RISE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x01); ++ break; ++ case TIMER_FLAG_FALL_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x02); ++ break; ++ case TIMER_FLAG_ANY_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x03); ++ break; ++ } ++ if (TIMER_FLAG_MASK_TYPE(flag) == TIMER_FLAG_TIMER) ++ con_reg |= ++ TIMER_FLAG_MASK_SRC(flag) == ++ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : ++ GPTU_CON_SRC_EXT_SET(0); ++ else ++ con_reg |= ++ TIMER_FLAG_MASK_SRC(flag) == ++ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : ++ GPTU_CON_SRC_EG_SET(0); ++ con_reg |= ++ TIMER_FLAG_MASK_SYNC(flag) == ++ TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : ++ GPTU_CON_SYNC_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_INVERT(flag) == ++ TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_SIZE(flag) == ++ TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : ++ GPTU_CON_EXT_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_STOP(flag) == ++ TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0); ++ con_reg |= ++ TIMER_FLAG_MASK_TYPE(flag) == ++ TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : ++ GPTU_CON_CNT_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_DIR(flag) == ++ TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0); ++ ++ /* ++ * Fill up running data. ++ */ ++ timer_dev.timer[timer - FIRST_TIMER].flag = flag; ++ timer_dev.timer[timer - FIRST_TIMER].arg1 = arg1; ++ timer_dev.timer[timer - FIRST_TIMER].arg2 = arg2; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flag; ++ ++ /* ++ * Enable GPTU module. ++ */ ++ if (!timer_dev.f_gptu_on) { ++ lq_enable_gptu(); ++ timer_dev.f_gptu_on = 1; ++ } ++ ++ /* ++ * Enable IRQ. ++ */ ++ if (TIMER_FLAG_MASK_HANDLE(flag) != TIMER_FLAG_NO_HANDLE) { ++ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL) ++ timer_dev.timer[timer - FIRST_TIMER].arg1 = ++ (unsigned long) find_task_by_vpid((int) arg1); ++ ++ irnen_reg = 1 << (timer - FIRST_TIMER); ++ ++ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL ++ || (TIMER_FLAG_MASK_HANDLE(flag) == ++ TIMER_FLAG_CALLBACK_IN_IRQ ++ && timer_dev.timer[timer - FIRST_TIMER].arg1)) { ++ enable_irq(timer_dev.timer[timer - FIRST_TIMER].irq); ++ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 1; ++ } ++ } else ++ irnen_reg = 0; ++ ++ /* ++ * Write config register, reload value and enable interrupt. ++ */ ++ n = timer >> 1; ++ X = timer & 0x01; ++ *LQ_GPTU_CON(n, X) = con_reg; ++ *LQ_GPTU_RELOAD(n, X) = value; ++ /* printk("reload value = %d\n", (u32)value); */ ++ *LQ_GPTU_IRNEN |= irnen_reg; ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ printk("successful!\n"); ++ return ret; ++} ++EXPORT_SYMBOL(lq_request_timer); ++ ++int lq_free_timer(unsigned int timer) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ if (GPTU_CON_EN(n, X)) ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1); ++ ++ *LQ_GPTU_IRNEN &= ~GPTU_IRNEN_TC_SET(n, X, 1); ++ *LQ_GPTU_IRNCR |= GPTU_IRNCR_TC_SET(n, X, 1); ++ ++ if (timer_dev.timer[timer - FIRST_TIMER].f_irq_on) { ++ disable_irq(timer_dev.timer[timer - FIRST_TIMER].irq); ++ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 0; ++ } ++ ++ timer_dev.occupation &= ~mask; ++ if (!timer_dev.occupation && timer_dev.f_gptu_on) { ++ lq_disable_gptu(); ++ timer_dev.f_gptu_on = 0; ++ } ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_free_timer); ++ ++int lq_start_timer(unsigned int timer, int is_resume) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == ++ TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_RL_SET(!is_resume) | GPTU_RUN_SEN_SET(1); ++ ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_start_timer); ++ ++int lq_stop_timer(unsigned int timer) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER ++ || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1); ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_stop_timer); ++ ++int lq_reset_counter_flags(u32 timer, u32 flags) ++{ ++ unsigned int oflag; ++ unsigned int mask, con_reg; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ oflag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(oflag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(oflag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ switch (TIMER_FLAG_MASK_EDGE(flags)) { ++ default: ++ case TIMER_FLAG_NONE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x00); ++ break; ++ case TIMER_FLAG_RISE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x01); ++ break; ++ case TIMER_FLAG_FALL_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x02); ++ break; ++ case TIMER_FLAG_ANY_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x03); ++ break; ++ } ++ if (TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER) ++ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : GPTU_CON_SRC_EXT_SET(0); ++ else ++ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : GPTU_CON_SRC_EG_SET(0); ++ con_reg |= TIMER_FLAG_MASK_SYNC(flags) == TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : GPTU_CON_SYNC_SET(1); ++ con_reg |= TIMER_FLAG_MASK_INVERT(flags) == TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1); ++ con_reg |= TIMER_FLAG_MASK_SIZE(flags) == TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : GPTU_CON_EXT_SET(1); ++ con_reg |= TIMER_FLAG_MASK_STOP(flags) == TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0); ++ con_reg |= TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : GPTU_CON_CNT_SET(1); ++ con_reg |= TIMER_FLAG_MASK_DIR(flags) == TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0); ++ ++ timer_dev.timer[timer - FIRST_TIMER].flag = flags; ++ if (TIMER_FLAG_MASK_SIZE(flags) != TIMER_FLAG_16BIT) ++ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flags; ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_CON(n, X) = con_reg; ++ smp_wmb(); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return 0; ++} ++EXPORT_SYMBOL(lq_reset_counter_flags); ++ ++int lq_get_count_value(unsigned int timer, unsigned long *value) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER ++ || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *value = *LQ_GPTU_COUNT(n, X); ++ ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_get_count_value); ++ ++u32 lq_cal_divider(unsigned long freq) ++{ ++ u64 module_freq, fpi = ltq_get_fpi_bus_clock(2); ++ u32 clock_divider = 1; ++ module_freq = fpi * 1000; ++ do_div(module_freq, clock_divider * freq); ++ return module_freq; ++} ++EXPORT_SYMBOL(lq_cal_divider); ++ ++int lq_set_timer(unsigned int timer, unsigned int freq, int is_cyclic, ++ int is_ext_src, unsigned int handle_flag, unsigned long arg1, ++ unsigned long arg2) ++{ ++ unsigned long divider; ++ unsigned int flag; ++ ++ divider = lq_cal_divider(freq); ++ if (divider == 0) ++ return -EINVAL; ++ flag = ((divider & ~0xFFFF) ? TIMER_FLAG_32BIT : TIMER_FLAG_16BIT) ++ | (is_cyclic ? TIMER_FLAG_CYCLIC : TIMER_FLAG_ONCE) ++ | (is_ext_src ? TIMER_FLAG_EXT_SRC : TIMER_FLAG_INT_SRC) ++ | TIMER_FLAG_TIMER | TIMER_FLAG_DOWN ++ | TIMER_FLAG_MASK_HANDLE(handle_flag); ++ ++ printk(KERN_INFO "lq_set_timer(%d, %d), divider = %lu\n", ++ timer, freq, divider); ++ return lq_request_timer(timer, flag, divider, arg1, arg2); ++} ++EXPORT_SYMBOL(lq_set_timer); ++ ++int lq_set_counter(unsigned int timer, unsigned int flag, u32 reload, ++ unsigned long arg1, unsigned long arg2) ++{ ++ printk(KERN_INFO "lq_set_counter(%d, %#x, %d)\n", timer, flag, reload); ++ return lq_request_timer(timer, flag, reload, arg1, arg2); ++} ++EXPORT_SYMBOL(lq_set_counter); ++ ++static long gptu_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret; ++ struct gptu_ioctl_param param; ++ ++ if (!access_ok((void __user *)arg, sizeof(struct gptu_ioctl_param))) ++ return -EFAULT; ++ copy_from_user(¶m, (void __user *)arg, sizeof(param)); ++ ++ if ((((cmd == GPTU_REQUEST_TIMER || cmd == GPTU_SET_TIMER ++ || GPTU_SET_COUNTER) && param.timer < 2) ++ || cmd == GPTU_GET_COUNT_VALUE || cmd == GPTU_CALCULATE_DIVIDER) ++ && !access_ok((void __user *)arg, ++ sizeof(struct gptu_ioctl_param))) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case GPTU_REQUEST_TIMER: ++ ret = lq_request_timer(param.timer, param.flag, param.value, ++ (unsigned long) param.pid, ++ (unsigned long) param.sig); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ case GPTU_FREE_TIMER: ++ ret = lq_free_timer(param.timer); ++ break; ++ case GPTU_START_TIMER: ++ ret = lq_start_timer(param.timer, param.flag); ++ break; ++ case GPTU_STOP_TIMER: ++ ret = lq_stop_timer(param.timer); ++ break; ++ case GPTU_GET_COUNT_VALUE: ++ ret = lq_get_count_value(param.timer, ¶m.value); ++ if (!ret) ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ value, ¶m.value, ++ sizeof(param.value)); ++ break; ++ case GPTU_CALCULATE_DIVIDER: ++ param.value = lq_cal_divider(param.value); ++ if (param.value == 0) ++ ret = -EINVAL; ++ else { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ value, ¶m.value, ++ sizeof(param.value)); ++ ret = 0; ++ } ++ break; ++ case GPTU_SET_TIMER: ++ ret = lq_set_timer(param.timer, param.value, ++ TIMER_FLAG_MASK_STOP(param.flag) != ++ TIMER_FLAG_ONCE ? 1 : 0, ++ TIMER_FLAG_MASK_SRC(param.flag) == ++ TIMER_FLAG_EXT_SRC ? 1 : 0, ++ TIMER_FLAG_MASK_HANDLE(param.flag) == ++ TIMER_FLAG_SIGNAL ? TIMER_FLAG_SIGNAL : ++ TIMER_FLAG_NO_HANDLE, ++ (unsigned long) param.pid, ++ (unsigned long) param.sig); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ case GPTU_SET_COUNTER: ++ lq_set_counter(param.timer, param.flag, param.value, 0, 0); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ default: ++ ret = -ENOTTY; ++ } ++ ++ return ret; ++} ++ ++static int gptu_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int gptu_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++int __init lq_gptu_init(void) ++{ ++ int ret; ++ unsigned int i; ++ ++ ltq_w32(0, LQ_GPTU_IRNEN); ++ ltq_w32(0xfff, LQ_GPTU_IRNCR); ++ ++ memset(&timer_dev, 0, sizeof(timer_dev)); ++ mutex_init(&timer_dev.gptu_mutex); ++ ++ lq_enable_gptu(); ++ timer_dev.number_of_timers = GPTU_ID_CFG * 2; ++ lq_disable_gptu(); ++ if (timer_dev.number_of_timers > MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2) ++ timer_dev.number_of_timers = MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2; ++ printk(KERN_INFO "gptu: totally %d 16-bit timers/counters\n", timer_dev.number_of_timers); ++ ++ ret = misc_register(&gptu_miscdev); ++ if (ret) { ++ printk(KERN_ERR "gptu: can't misc_register, get error %d\n", -ret); ++ return ret; ++ } else { ++ printk(KERN_INFO "gptu: misc_register on minor %d\n", gptu_miscdev.minor); ++ } ++ ++ for (i = 0; i < timer_dev.number_of_timers; i++) { ++ ret = request_irq(TIMER_INTERRUPT + i, timer_irq_handler, IRQF_TIMER, gptu_miscdev.name, &timer_dev.timer[i]); ++ if (ret) { ++ for (; i >= 0; i--) ++ free_irq(TIMER_INTERRUPT + i, &timer_dev.timer[i]); ++ misc_deregister(&gptu_miscdev); ++ printk(KERN_ERR "gptu: failed in requesting irq (%d), get error %d\n", i, -ret); ++ return ret; ++ } else { ++ timer_dev.timer[i].irq = TIMER_INTERRUPT + i; ++ disable_irq(timer_dev.timer[i].irq); ++ printk(KERN_INFO "gptu: succeeded to request irq %d\n", timer_dev.timer[i].irq); ++ } ++ } ++ ++ return 0; ++} ++ ++void __exit lq_gptu_exit(void) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < timer_dev.number_of_timers; i++) { ++ if (timer_dev.timer[i].f_irq_on) ++ disable_irq(timer_dev.timer[i].irq); ++ free_irq(timer_dev.timer[i].irq, &timer_dev.timer[i]); ++ } ++ lq_disable_gptu(); ++ misc_deregister(&gptu_miscdev); ++} ++ ++module_init(lq_gptu_init); ++module_exit(lq_gptu_exit); ++ ++#endif diff --git a/target/linux/lantiq/patches-5.15/0018-MTD-nand-lots-of-xrx200-fixes.patch b/target/linux/lantiq/patches-5.15/0018-MTD-nand-lots-of-xrx200-fixes.patch new file mode 100644 index 0000000000..35f656da6e --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0018-MTD-nand-lots-of-xrx200-fixes.patch @@ -0,0 +1,121 @@ +From 997a8965db8417266bea3fbdcfa3e5655a1b52fa Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Tue, 9 Sep 2014 23:12:15 +0200 +Subject: [PATCH 18/36] MTD: nand: lots of xrx200 fixes + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/mtd/nand/raw/xway_nand.c | 63 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 63 insertions(+) + +--- a/drivers/mtd/nand/raw/xway_nand.c ++++ b/drivers/mtd/nand/raw/xway_nand.c +@@ -61,6 +61,24 @@ + #define NAND_CON_CSMUX (1 << 1) + #define NAND_CON_NANDM 1 + ++#define DANUBE_PCI_REG32( addr ) (*(volatile u32 *)(addr)) ++#define PCI_CR_PR_OFFSET (KSEG1+0x1E105400) ++#define PCI_CR_PC_ARB (PCI_CR_PR_OFFSET + 0x0080) ++ ++/* ++ * req_mask provides a mechanism to prevent interference between ++ * nand and pci (probably only relevant for the BT Home Hub 2B). ++ * Setting it causes the corresponding pci req pins to be masked ++ * during nand access, and also moves ebu locking from the read/write ++ * functions to the chip select function to ensure that the whole ++ * operation runs with interrupts disabled. ++ * In addition it switches on some extra waiting in xway_cmd_ctrl(). ++ * This seems to be necessary if the ebu_cs1 pin has open-drain disabled, ++ * which in turn seems to be necessary for the nor chip to be recognised ++ * reliably, on a board (Home Hub 2B again) which has both nor and nand. ++ */ ++static __be32 req_mask = 0; ++ + struct xway_nand_data { + struct nand_controller controller; + struct nand_chip chip; +@@ -92,10 +110,22 @@ static void xway_select_chip(struct nand + case -1: + ltq_ebu_w32_mask(NAND_CON_CE, 0, EBU_NAND_CON); + ltq_ebu_w32_mask(NAND_CON_NANDM, 0, EBU_NAND_CON); ++ ++ if (req_mask) { ++ /* Unmask all external PCI request */ ++ DANUBE_PCI_REG32(PCI_CR_PC_ARB) &= ~(req_mask << 16); ++ } ++ + spin_unlock_irqrestore(&ebu_lock, data->csflags); + break; + case 0: + spin_lock_irqsave(&ebu_lock, data->csflags); ++ ++ if (req_mask) { ++ /* Mask all external PCI request */ ++ DANUBE_PCI_REG32(PCI_CR_PC_ARB) |= (req_mask << 16); ++ } ++ + ltq_ebu_w32_mask(0, NAND_CON_NANDM, EBU_NAND_CON); + ltq_ebu_w32_mask(0, NAND_CON_CE, EBU_NAND_CON); + break; +@@ -108,6 +138,11 @@ static void xway_cmd_ctrl(struct nand_ch + { + struct mtd_info *mtd = nand_to_mtd(chip); + ++ if (req_mask) { ++ if (cmd != NAND_CMD_STATUS) ++ ltq_ebu_w32(0, EBU_NAND_WAIT); /* Clear nand ready */ ++ } ++ + if (cmd == NAND_CMD_NONE) + return; + +@@ -118,6 +153,24 @@ static void xway_cmd_ctrl(struct nand_ch + + while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) + ; ++ ++ if (req_mask) { ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (cmd) { ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_SEQIN: ++ case NAND_CMD_STATUS: ++ case NAND_CMD_READID: ++ return; ++ } ++ ++ /* wait until command is processed */ ++ while ((ltq_ebu_r32(EBU_NAND_WAIT) & NAND_WAIT_RD) == 0) ++ ; ++ } + } + + static int xway_dev_ready(struct nand_chip *chip) +@@ -170,6 +223,7 @@ static int xway_nand_probe(struct platfo + int err; + u32 cs; + u32 cs_flag = 0; ++ const __be32 *req_mask_ptr; + + /* Allocate memory for the device structure (and zero it) */ + data = devm_kzalloc(&pdev->dev, sizeof(struct xway_nand_data), +@@ -206,6 +260,15 @@ static int xway_nand_probe(struct platfo + if (!err && cs == 1) + cs_flag = NAND_CON_IN_CS1 | NAND_CON_OUT_CS1; + ++ req_mask_ptr = of_get_property(pdev->dev.of_node, ++ "req-mask", NULL); ++ ++ /* ++ * Load the PCI req lines to mask from the device tree. If the ++ * property is not present, setting req_mask to 0 disables masking. ++ */ ++ req_mask = (req_mask_ptr ? *req_mask_ptr : 0); ++ + /* setup the EBU to run in NAND mode on our base addr */ + ltq_ebu_w32(CPHYSADDR(data->nandaddr) + | ADDSEL1_MASK(3) | ADDSEL1_REGEN, EBU_ADDSEL1); diff --git a/target/linux/lantiq/patches-5.15/0020-MTD-lantiq-handle-NO_XIP-on-cfi0001-flash.patch b/target/linux/lantiq/patches-5.15/0020-MTD-lantiq-handle-NO_XIP-on-cfi0001-flash.patch new file mode 100644 index 0000000000..c1fc59487a --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0020-MTD-lantiq-handle-NO_XIP-on-cfi0001-flash.patch @@ -0,0 +1,25 @@ +From e3b20f04e9f9cae1babe091fdc1d08d7703ae344 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 7 Aug 2014 18:18:00 +0200 +Subject: [PATCH 20/36] MTD: lantiq: handle NO_XIP on cfi0001 flash + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/mtd/maps/lantiq-flash.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/maps/lantiq-flash.c ++++ b/drivers/mtd/maps/lantiq-flash.c +@@ -129,7 +129,11 @@ ltq_mtd_probe(struct platform_device *pd + if (!ltq_mtd->map) + return -ENOMEM; + +- ltq_mtd->map->phys = ltq_mtd->res->start; ++ if (of_find_property(pdev->dev.of_node, "lantiq,noxip", NULL)) ++ ltq_mtd->map->phys = NO_XIP; ++ else ++ ltq_mtd->map->phys = ltq_mtd->res->start; ++ ltq_mtd->res->start; + ltq_mtd->map->size = resource_size(ltq_mtd->res); + ltq_mtd->map->virt = devm_ioremap_resource(&pdev->dev, ltq_mtd->res); + if (IS_ERR(ltq_mtd->map->virt)) diff --git a/target/linux/lantiq/patches-5.15/0023-NET-PHY-add-led-support-for-intel-xway.patch b/target/linux/lantiq/patches-5.15/0023-NET-PHY-add-led-support-for-intel-xway.patch new file mode 100644 index 0000000000..fb8d975110 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0023-NET-PHY-add-led-support-for-intel-xway.patch @@ -0,0 +1,294 @@ +From 0a63ab263725c427051a8bbaa0732b749627da27 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 7 Aug 2014 18:15:36 +0200 +Subject: [PATCH 23/36] NET: PHY: adds driver for lantiq PHY11G + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/net/phy/Kconfig | 5 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/lantiq.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 237 insertions(+) + create mode 100644 drivers/net/phy/lantiq.c + +--- a/drivers/net/phy/intel-xway.c ++++ b/drivers/net/phy/intel-xway.c +@@ -157,6 +157,51 @@ + #define PHY_ID_PHY11G_VR9_1_2 0xD565A409 + #define PHY_ID_PHY22F_VR9_1_2 0xD565A419 + ++#if IS_ENABLED(CONFIG_OF_MDIO) ++static int vr9_gphy_of_reg_init(struct phy_device *phydev) ++{ ++ u32 tmp; ++ ++ /* store the led values if one was passed by the devicetree */ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,ledch", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,ledcl", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led0h", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led0l", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led1h", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led1l", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led2h", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led2l", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led3h", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED3H, tmp); ++ ++ if (!of_property_read_u32(phydev->mdio.dev.of_node, "lantiq,led3l", &tmp)) ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED3L, tmp); ++ ++ return 0; ++} ++#else ++static int vr9_gphy_of_reg_init(struct phy_device *phydev) ++{ ++ return 0; ++} ++#endif /* CONFIG_OF_MDIO */ ++ + static int xway_gphy_config_init(struct phy_device *phydev) + { + int err; +@@ -204,6 +249,7 @@ static int xway_gphy_config_init(struct + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl); + ++ vr9_gphy_of_reg_init(phydev); + return 0; + } + +--- /dev/null ++++ b/Documentation/devicetree/bindings/phy/phy-lanitq.txt +@@ -0,0 +1,216 @@ ++Lanitq PHY binding ++============================================ ++ ++This devicetree binding controls the lantiq ethernet phys led functionality. ++ ++Example: ++ mdio@0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "lantiq,xrx200-mdio"; ++ phy5: ethernet-phy@5 { ++ reg = <0x1>; ++ compatible = "lantiq,phy11g", "ethernet-phy-ieee802.3-c22"; ++ }; ++ phy11: ethernet-phy@11 { ++ reg = <0x11>; ++ compatible = "lantiq,phy22f", "ethernet-phy-ieee802.3-c22"; ++ lantiq,led2h = <0x00>; ++ lantiq,led2l = <0x03>; ++ }; ++ phy12: ethernet-phy@12 { ++ reg = <0x12>; ++ compatible = "lantiq,phy22f", "ethernet-phy-ieee802.3-c22"; ++ lantiq,led1h = <0x00>; ++ lantiq,led1l = <0x03>; ++ }; ++ phy13: ethernet-phy@13 { ++ reg = <0x13>; ++ compatible = "lantiq,phy22f", "ethernet-phy-ieee802.3-c22"; ++ lantiq,led2h = <0x00>; ++ lantiq,led2l = <0x03>; ++ }; ++ phy14: ethernet-phy@14 { ++ reg = <0x14>; ++ compatible = "lantiq,phy22f", "ethernet-phy-ieee802.3-c22"; ++ lantiq,led1h = <0x00>; ++ lantiq,led1l = <0x03>; ++ }; ++ }; ++ ++Register Description ++============================================ ++ ++LEDCH: ++ ++Name Hardware Reset Value ++LEDCH 0x00C5 ++ ++| 15 | | | | | | | 8 | ++========================================= ++| RES | ++========================================= ++ ++| 7 | | | | | | | 0 | ++========================================= ++| FBF | SBF |RES | NACS | ++========================================= ++ ++Field Bits Type Description ++FBF 7:6 RW Fast Blink Frequency ++ --- ++ 0x0 (00b) F02HZ 2 Hz blinking frequency ++ 0x1 (01b) F04HZ 4 Hz blinking frequency ++ 0x2 (10b) F08HZ 8 Hz blinking frequency ++ 0x3 (11b) F16HZ 16 Hz blinking frequency ++ ++SBF 5:4 RW Slow Blink Frequency ++ --- ++ 0x0 (00b) F02HZ 2 Hz blinking frequency ++ 0x1 (01b) F04HZ 4 Hz blinking frequency ++ 0x2 (10b) F08HZ 8 Hz blinking frequency ++ 0x3 (11b) F16HZ 16 Hz blinking frequency ++ ++NACS 2:0 RW Inverse of Scan Function ++ --- ++ 0x0 (000b) NONE No Function ++ 0x1 (001b) LINK Complex function enabled when link is up ++ 0x2 (010b) PDOWN Complex function enabled when device is powered-down ++ 0x3 (011b) EEE Complex function enabled when device is in EEE mode ++ 0x4 (100b) ANEG Complex function enabled when auto-negotiation is running ++ 0x5 (101b) ABIST Complex function enabled when analog self-test is running ++ 0x6 (110b) CDIAG Complex function enabled when cable diagnostics are running ++ 0x7 (111b) TEST Complex function enabled when test mode is running ++ ++LEDCL: ++ ++Name Hardware Reset Value ++LEDCL 0x0067 ++ ++| 15 | | | | | | | 8 | ++========================================= ++| RES | ++========================================= ++ ++| 7 | | | | | | | 0 | ++========================================= ++|RES | SCAN |RES | CBLINK | ++========================================= ++ ++Field Bits Type Description ++SCAN 6:4 RW Complex Scan Configuration ++ --- ++ 000 B NONE No Function ++ 001 B LINK Complex function enabled when link is up ++ 010 B PDOWN Complex function enabled when device is powered-down ++ 011 B EEE Complex function enabled when device is in EEE mode ++ 100 B ANEG Complex function enabled when auto-negotiation is running ++ 101 B ABIST Complex function enabled when analog self-test is running ++ 110 B CDIAG Complex function enabled when cable diagnostics are running ++ 111 B TEST Complex function enabled when test mode is running ++ ++CBLINK 2:0 RW Complex Blinking Configuration ++ --- ++ 000 B NONE No Function ++ 001 B LINK Complex function enabled when link is up ++ 010 B PDOWN Complex function enabled when device is powered-down ++ 011 B EEE Complex function enabled when device is in EEE mode ++ 100 B ANEG Complex function enabled when auto-negotiation is running ++ 101 B ABIST Complex function enabled when analog self-test is running ++ 110 B CDIAG Complex function enabled when cable diagnostics are running ++ 111 B TEST Complex function enabled when test mode is running ++ ++LEDxH: ++ ++Name Hardware Reset Value ++LED0H 0x0070 ++LED1H 0x0020 ++LED2H 0x0040 ++LED3H 0x0040 ++ ++| 15 | | | | | | | 8 | ++========================================= ++| RES | ++========================================= ++ ++| 7 | | | | | | | 0 | ++========================================= ++| CON | BLINKF | ++========================================= ++ ++Field Bits Type Description ++CON 7:4 RW Constant On Configuration ++ --- ++ 0x0 (0000b) NONE LED does not light up constantly ++ 0x1 (0001b) LINK10 LED is on when link is 10 Mbit/s ++ 0x2 (0010b) LINK100 LED is on when link is 100 Mbit/s ++ 0x3 (0011b) LINK10X LED is on when link is 10/100 Mbit/s ++ 0x4 (0100b) LINK1000 LED is on when link is 1000 Mbit/s ++ 0x5 (0101b) LINK10_0 LED is on when link is 10/1000 Mbit/s ++ 0x6 (0110b) LINK100X LED is on when link is 100/1000 Mbit/s ++ 0x7 (0111b) LINK10XX LED is on when link is 10/100/1000 Mbit/s ++ 0x8 (1000b) PDOWN LED is on when device is powered-down ++ 0x9 (1001b) EEE LED is on when device is in EEE mode ++ 0xA (1010b) ANEG LED is on when auto-negotiation is running ++ 0xB (1011b) ABIST LED is on when analog self-test is running ++ 0xC (1100b) CDIAG LED is on when cable diagnostics are running ++ ++BLINKF 3:0 RW Fast Blinking Configuration ++ --- ++ 0x0 (0000b) NONE No Blinking ++ 0x1 (0001b) LINK10 Blink when link is 10 Mbit/s ++ 0x2 (0010b) LINK100 Blink when link is 100 Mbit/s ++ 0x3 (0011b) LINK10X Blink when link is 10/100 Mbit/s ++ 0x4 (0100b) LINK1000 Blink when link is 1000 Mbit/s ++ 0x5 (0101b) LINK10_0 Blink when link is 10/1000 Mbit/s ++ 0x6 (0110b) LINK100X Blink when link is 100/1000 Mbit/s ++ 0x7 (0111b) LINK10XX Blink when link is 10/100/1000 Mbit/s ++ 0x8 (1000b) PDOWN Blink when device is powered-down ++ 0x9 (1001b) EEE Blink when device is in EEE mode ++ 0xA (1010b) ANEG Blink when auto-negotiation is running ++ 0xB (1011b) ABIST Blink when analog self-test is running ++ 0xC (1100b) CDIAG Blink when cable diagnostics are running ++ ++LEDxL: ++ ++Name Hardware Reset Value ++LED0L 0x0003 ++LED1L 0x0000 ++LED2L 0x0000 ++LED3L 0x0020 ++ ++| 15 | | | | | | | 8 | ++========================================= ++| RES | ++========================================= ++ ++| 7 | | | | | | | 0 | ++========================================= ++| BLINKS | PULSE | ++========================================= ++ ++Field Bits Type Description ++BLINKS 7:4 RW Slow Blinkin Configuration ++ --- ++ 0x0 (0000b) NONE No Blinking ++ 0x1 (0001b) LINK10 Blink when link is 10 Mbit/s ++ 0x2 (0010b) LINK100 Blink when link is 100 Mbit/s ++ 0x3 (0011b) LINK10X Blink when link is 10/100 Mbit/s ++ 0x4 (0100b) LINK1000 Blink when link is 1000 Mbit/s ++ 0x5 (0101b) LINK10_0 Blink when link is 10/1000 Mbit/s ++ 0x6 (0110b) LINK100X Blink when link is 100/1000 Mbit/s ++ 0x7 (0111b) LINK10XX Blink when link is 10/100/1000 Mbit/s ++ 0x8 (1000b) PDOWN Blink when device is powered-down ++ 0x9 (1001b) EEE Blink when device is in EEE mode ++ 0xA (1010b) ANEG Blink when auto-negotiation is running ++ 0xB (1011b) ABIST Blink when analog self-test is running ++ 0xC (1100b) CDIAG Blink when cable diagnostics are runningning ++ ++PULSE 3:0 RW Pulsing Configuration ++ The pulse field is a mask field by which certain events can be combined ++ --- ++ 0x0 (0000b) NONE No pulsing ++ 0x1 (0001b) TXACT Transmit activity ++ 0x2 (0010b) RXACT Receive activity ++ 0x4 (0100b) COL Collision ++ 0x8 (1000b) RES Reserved diff --git a/target/linux/lantiq/patches-5.15/0028-NET-lantiq-various-etop-fixes.patch b/target/linux/lantiq/patches-5.15/0028-NET-lantiq-various-etop-fixes.patch new file mode 100644 index 0000000000..eb47010fa0 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0028-NET-lantiq-various-etop-fixes.patch @@ -0,0 +1,865 @@ +From 870ed9cae083ff8a60a739ef7e74c5a1800533be Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Tue, 9 Sep 2014 22:45:34 +0200 +Subject: [PATCH 28/36] NET: lantiq: various etop fixes + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/net/ethernet/lantiq_etop.c | 555 +++++++++++++++++++++++++----------- + 1 file changed, 389 insertions(+), 166 deletions(-) + +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: GPL-2.0-only + /* + * +- * Copyright (C) 2011 John Crispin <blogic@openwrt.org> ++ * Copyright (C) 2011-12 John Crispin <blogic@openwrt.org> + */ + + #include <linux/kernel.h> +@@ -20,11 +20,16 @@ + #include <linux/mm.h> + #include <linux/platform_device.h> + #include <linux/ethtool.h> ++#include <linux/if_vlan.h> + #include <linux/init.h> + #include <linux/delay.h> + #include <linux/io.h> + #include <linux/dma-mapping.h> + #include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/of_net.h> ++#include <linux/of_irq.h> ++#include <linux/of_platform.h> + + #include <asm/checksum.h> + +@@ -32,7 +37,7 @@ + #include <xway_dma.h> + #include <lantiq_platform.h> + +-#define LTQ_ETOP_MDIO 0x11804 ++#define LTQ_ETOP_MDIO_ACC 0x11804 + #define MDIO_REQUEST 0x80000000 + #define MDIO_READ 0x40000000 + #define MDIO_ADDR_MASK 0x1f +@@ -41,44 +46,91 @@ + #define MDIO_REG_OFFSET 0x10 + #define MDIO_VAL_MASK 0xffff + +-#define PPE32_CGEN 0x800 +-#define LQ_PPE32_ENET_MAC_CFG 0x1840 ++#define LTQ_ETOP_MDIO_CFG 0x11800 ++#define MDIO_CFG_MASK 0x6 ++ ++#define LTQ_ETOP_CFG 0x11808 ++#define LTQ_ETOP_IGPLEN 0x11820 ++#define LTQ_ETOP_MAC_CFG 0x11840 + + #define LTQ_ETOP_ENETS0 0x11850 + #define LTQ_ETOP_MAC_DA0 0x1186C + #define LTQ_ETOP_MAC_DA1 0x11870 +-#define LTQ_ETOP_CFG 0x16020 +-#define LTQ_ETOP_IGPLEN 0x16080 ++ ++#define MAC_CFG_MASK 0xfff ++#define MAC_CFG_CGEN (1 << 11) ++#define MAC_CFG_DUPLEX (1 << 2) ++#define MAC_CFG_SPEED (1 << 1) ++#define MAC_CFG_LINK (1 << 0) + + #define MAX_DMA_CHAN 0x8 + #define MAX_DMA_CRC_LEN 0x4 + #define MAX_DMA_DATA_LEN 0x600 + + #define ETOP_FTCU BIT(28) +-#define ETOP_MII_MASK 0xf +-#define ETOP_MII_NORMAL 0xd +-#define ETOP_MII_REVERSE 0xe + #define ETOP_PLEN_UNDER 0x40 +-#define ETOP_CGEN 0x800 ++#define ETOP_CFG_MII0 0x01 + +-/* use 2 static channels for TX/RX */ +-#define LTQ_ETOP_TX_CHANNEL 1 +-#define LTQ_ETOP_RX_CHANNEL 6 +-#define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL) +-#define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL) ++#define ETOP_CFG_MASK 0xfff ++#define ETOP_CFG_FEN0 (1 << 8) ++#define ETOP_CFG_SEN0 (1 << 6) ++#define ETOP_CFG_OFF1 (1 << 3) ++#define ETOP_CFG_REMII0 (1 << 1) ++#define ETOP_CFG_OFF0 (1 << 0) ++ ++#define LTQ_GBIT_MDIO_CTL 0xCC ++#define LTQ_GBIT_MDIO_DATA 0xd0 ++#define LTQ_GBIT_GCTL0 0x68 ++#define LTQ_GBIT_PMAC_HD_CTL 0x8c ++#define LTQ_GBIT_P0_CTL 0x4 ++#define LTQ_GBIT_PMAC_RX_IPG 0xa8 ++#define LTQ_GBIT_RGMII_CTL 0x78 ++ ++#define PMAC_HD_CTL_AS (1 << 19) ++#define PMAC_HD_CTL_RXSH (1 << 22) ++ ++/* Switch Enable (0=disable, 1=enable) */ ++#define GCTL0_SE 0x80000000 ++/* Disable MDIO auto polling (0=disable, 1=enable) */ ++#define PX_CTL_DMDIO 0x00400000 ++ ++/* MDC clock divider, clock = 25MHz/((MDC_CLOCK + 1) * 2) */ ++#define MDC_CLOCK_MASK 0xff000000 ++#define MDC_CLOCK_OFFSET 24 ++ ++/* register information for the gbit's MDIO bus */ ++#define MDIO_XR9_REQUEST 0x00008000 ++#define MDIO_XR9_READ 0x00000800 ++#define MDIO_XR9_WRITE 0x00000400 ++#define MDIO_XR9_REG_MASK 0x1f ++#define MDIO_XR9_ADDR_MASK 0x1f ++#define MDIO_XR9_RD_MASK 0xffff ++#define MDIO_XR9_REG_OFFSET 0 ++#define MDIO_XR9_ADDR_OFFSET 5 ++#define MDIO_XR9_WR_OFFSET 16 + ++#define LTQ_DMA_ETOP ((of_machine_is_compatible("lantiq,ase")) ? \ ++ (INT_NUM_IM3_IRL0) : (INT_NUM_IM2_IRL0)) ++ ++/* the newer xway socks have a embedded 3/7 port gbit multiplexer */ + #define ltq_etop_r32(x) ltq_r32(ltq_etop_membase + (x)) + #define ltq_etop_w32(x, y) ltq_w32(x, ltq_etop_membase + (y)) + #define ltq_etop_w32_mask(x, y, z) \ + ltq_w32_mask(x, y, ltq_etop_membase + (z)) + +-#define DRV_VERSION "1.0" ++#define ltq_gbit_r32(x) ltq_r32(ltq_gbit_membase + (x)) ++#define ltq_gbit_w32(x, y) ltq_w32(x, ltq_gbit_membase + (y)) ++#define ltq_gbit_w32_mask(x, y, z) \ ++ ltq_w32_mask(x, y, ltq_gbit_membase + (z)) ++ ++#define DRV_VERSION "1.2" + + static void __iomem *ltq_etop_membase; ++static void __iomem *ltq_gbit_membase; + + struct ltq_etop_chan { +- int idx; + int tx_free; ++ int irq; + struct net_device *netdev; + struct napi_struct napi; + struct ltq_dma_channel dma; +@@ -88,23 +140,36 @@ struct ltq_etop_chan { + struct ltq_etop_priv { + struct net_device *netdev; + struct platform_device *pdev; +- struct ltq_eth_data *pldata; + struct resource *res; + + struct mii_bus *mii_bus; + +- struct ltq_etop_chan ch[MAX_DMA_CHAN]; +- int tx_free[MAX_DMA_CHAN >> 1]; ++ struct ltq_etop_chan txch; ++ struct ltq_etop_chan rxch; + +- spinlock_t lock; ++ int tx_irq; ++ int rx_irq; ++ ++ unsigned char mac[6]; ++ phy_interface_t mii_mode; ++ ++ spinlock_t lock; ++ ++ struct clk *clk_ppe; ++ struct clk *clk_switch; ++ struct clk *clk_ephy; ++ struct clk *clk_ephycgu; + }; + ++static int ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, ++ int phy_reg, u16 phy_data); ++ + static int + ltq_etop_alloc_skb(struct ltq_etop_chan *ch) + { + struct ltq_etop_priv *priv = netdev_priv(ch->netdev); + +- ch->skb[ch->dma.desc] = netdev_alloc_skb(ch->netdev, MAX_DMA_DATA_LEN); ++ ch->skb[ch->dma.desc] = dev_alloc_skb(MAX_DMA_DATA_LEN); + if (!ch->skb[ch->dma.desc]) + return -ENOMEM; + ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(&priv->pdev->dev, +@@ -139,8 +204,11 @@ ltq_etop_hw_receive(struct ltq_etop_chan + spin_unlock_irqrestore(&priv->lock, flags); + + skb_put(skb, len); ++ skb->dev = ch->netdev; + skb->protocol = eth_type_trans(skb, ch->netdev); + netif_receive_skb(skb); ++ ch->netdev->stats.rx_packets++; ++ ch->netdev->stats.rx_bytes += len; + } + + static int +@@ -148,7 +216,9 @@ ltq_etop_poll_rx(struct napi_struct *nap + { + struct ltq_etop_chan *ch = container_of(napi, + struct ltq_etop_chan, napi); ++ struct ltq_etop_priv *priv = netdev_priv(ch->netdev); + int work_done = 0; ++ unsigned long flags; + + while (work_done < budget) { + struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; +@@ -160,7 +230,9 @@ ltq_etop_poll_rx(struct napi_struct *nap + } + if (work_done < budget) { + napi_complete_done(&ch->napi, work_done); ++ spin_lock_irqsave(&priv->lock, flags); + ltq_dma_ack_irq(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); + } + return work_done; + } +@@ -172,12 +244,14 @@ ltq_etop_poll_tx(struct napi_struct *nap + container_of(napi, struct ltq_etop_chan, napi); + struct ltq_etop_priv *priv = netdev_priv(ch->netdev); + struct netdev_queue *txq = +- netdev_get_tx_queue(ch->netdev, ch->idx >> 1); ++ netdev_get_tx_queue(ch->netdev, ch->dma.nr >> 1); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + while ((ch->dma.desc_base[ch->tx_free].ctl & + (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { ++ ch->netdev->stats.tx_packets++; ++ ch->netdev->stats.tx_bytes += ch->skb[ch->tx_free]->len; + dev_kfree_skb_any(ch->skb[ch->tx_free]); + ch->skb[ch->tx_free] = NULL; + memset(&ch->dma.desc_base[ch->tx_free], 0, +@@ -190,7 +264,9 @@ ltq_etop_poll_tx(struct napi_struct *nap + if (netif_tx_queue_stopped(txq)) + netif_tx_start_queue(txq); + napi_complete(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); + ltq_dma_ack_irq(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); + return 1; + } + +@@ -198,9 +274,10 @@ static irqreturn_t + ltq_etop_dma_irq(int irq, void *_priv) + { + struct ltq_etop_priv *priv = _priv; +- int ch = irq - LTQ_DMA_CH0_INT; +- +- napi_schedule(&priv->ch[ch].napi); ++ if (irq == priv->txch.dma.irq) ++ napi_schedule(&priv->txch.napi); ++ else ++ napi_schedule(&priv->rxch.napi); + return IRQ_HANDLED; + } + +@@ -212,7 +289,7 @@ ltq_etop_free_channel(struct net_device + ltq_dma_free(&ch->dma); + if (ch->dma.irq) + free_irq(ch->dma.irq, priv); +- if (IS_RX(ch->idx)) { ++ if (ch == &priv->txch) { + int desc; + for (desc = 0; desc < LTQ_DESC_NUM; desc++) + dev_kfree_skb_any(ch->skb[ch->dma.desc]); +@@ -223,66 +300,135 @@ static void + ltq_etop_hw_exit(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; + +- ltq_pmu_disable(PMU_PPE); +- for (i = 0; i < MAX_DMA_CHAN; i++) +- if (IS_TX(i) || IS_RX(i)) +- ltq_etop_free_channel(dev, &priv->ch[i]); ++ clk_disable(priv->clk_ppe); ++ ++ if (of_machine_is_compatible("lantiq,ar9")) ++ clk_disable(priv->clk_switch); ++ ++ if (of_machine_is_compatible("lantiq,ase")) { ++ clk_disable(priv->clk_ephy); ++ clk_disable(priv->clk_ephycgu); ++ } ++ ++ ltq_etop_free_channel(dev, &priv->txch); ++ ltq_etop_free_channel(dev, &priv->rxch); ++} ++ ++static void ++ltq_etop_gbit_init(struct net_device *dev) ++{ ++ struct ltq_etop_priv *priv = netdev_priv(dev); ++ ++ clk_enable(priv->clk_switch); ++ ++ /* enable gbit port0 on the SoC */ ++ ltq_gbit_w32_mask((1 << 17), (1 << 18), LTQ_GBIT_P0_CTL); ++ ++ ltq_gbit_w32_mask(0, GCTL0_SE, LTQ_GBIT_GCTL0); ++ /* disable MDIO auto polling mode */ ++ ltq_gbit_w32_mask(0, PX_CTL_DMDIO, LTQ_GBIT_P0_CTL); ++ /* set 1522 packet size */ ++ ltq_gbit_w32_mask(0x300, 0, LTQ_GBIT_GCTL0); ++ /* disable pmac & dmac headers */ ++ ltq_gbit_w32_mask(PMAC_HD_CTL_AS | PMAC_HD_CTL_RXSH, 0, ++ LTQ_GBIT_PMAC_HD_CTL); ++ /* Due to traffic halt when burst length 8, ++ replace default IPG value with 0x3B */ ++ ltq_gbit_w32(0x3B, LTQ_GBIT_PMAC_RX_IPG); ++ /* set mdc clock to 2.5 MHz */ ++ ltq_gbit_w32_mask(MDC_CLOCK_MASK, 4 << MDC_CLOCK_OFFSET, ++ LTQ_GBIT_RGMII_CTL); + } + + static int + ltq_etop_hw_init(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; ++ phy_interface_t mii_mode = priv->mii_mode; + +- ltq_pmu_enable(PMU_PPE); ++ clk_enable(priv->clk_ppe); + +- switch (priv->pldata->mii_mode) { ++ if (of_machine_is_compatible("lantiq,ar9")) { ++ ltq_etop_gbit_init(dev); ++ /* force the etops link to the gbit to MII */ ++ mii_mode = PHY_INTERFACE_MODE_MII; ++ } ++ ltq_etop_w32_mask(MDIO_CFG_MASK, 0, LTQ_ETOP_MDIO_CFG); ++ ltq_etop_w32_mask(MAC_CFG_MASK, MAC_CFG_CGEN | MAC_CFG_DUPLEX | ++ MAC_CFG_SPEED | MAC_CFG_LINK, LTQ_ETOP_MAC_CFG); ++ ++ switch (mii_mode) { + case PHY_INTERFACE_MODE_RMII: +- ltq_etop_w32_mask(ETOP_MII_MASK, +- ETOP_MII_REVERSE, LTQ_ETOP_CFG); ++ ltq_etop_w32_mask(ETOP_CFG_MASK, ETOP_CFG_REMII0 | ETOP_CFG_OFF1 | ++ ETOP_CFG_SEN0 | ETOP_CFG_FEN0, LTQ_ETOP_CFG); + break; + + case PHY_INTERFACE_MODE_MII: +- ltq_etop_w32_mask(ETOP_MII_MASK, +- ETOP_MII_NORMAL, LTQ_ETOP_CFG); ++ ltq_etop_w32_mask(ETOP_CFG_MASK, ETOP_CFG_OFF1 | ++ ETOP_CFG_SEN0 | ETOP_CFG_FEN0, LTQ_ETOP_CFG); + break; + + default: ++ if (of_machine_is_compatible("lantiq,ase")) { ++ clk_enable(priv->clk_ephy); ++ /* disable external MII */ ++ ltq_etop_w32_mask(0, ETOP_CFG_MII0, LTQ_ETOP_CFG); ++ /* enable clock for internal PHY */ ++ clk_enable(priv->clk_ephycgu); ++ /* we need to write this magic to the internal phy to ++ make it work */ ++ ltq_etop_mdio_wr(NULL, 0x8, 0x12, 0xC020); ++ pr_info("Selected EPHY mode\n"); ++ break; ++ } + netdev_err(dev, "unknown mii mode %d\n", +- priv->pldata->mii_mode); ++ mii_mode); + return -ENOTSUPP; + } + +- /* enable crc generation */ +- ltq_etop_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG); ++ return 0; ++} ++ ++static int ++ltq_etop_dma_init(struct net_device *dev) ++{ ++ struct ltq_etop_priv *priv = netdev_priv(dev); ++ int tx = priv->tx_irq - LTQ_DMA_ETOP; ++ int rx = priv->rx_irq - LTQ_DMA_ETOP; ++ int err; + + ltq_dma_init_port(DMA_PORT_ETOP); + +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- int irq = LTQ_DMA_CH0_INT + i; +- struct ltq_etop_chan *ch = &priv->ch[i]; +- +- ch->idx = ch->dma.nr = i; +- ch->dma.dev = &priv->pdev->dev; +- +- if (IS_TX(i)) { +- ltq_dma_alloc_tx(&ch->dma); +- request_irq(irq, ltq_etop_dma_irq, 0, "etop_tx", priv); +- } else if (IS_RX(i)) { +- ltq_dma_alloc_rx(&ch->dma); +- for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM; +- ch->dma.desc++) +- if (ltq_etop_alloc_skb(ch)) +- return -ENOMEM; +- ch->dma.desc = 0; +- request_irq(irq, ltq_etop_dma_irq, 0, "etop_rx", priv); ++ priv->txch.dma.nr = tx; ++ priv->txch.dma.dev = &priv->pdev->dev; ++ ltq_dma_alloc_tx(&priv->txch.dma); ++ err = request_irq(priv->tx_irq, ltq_etop_dma_irq, 0, "eth_tx", priv); ++ if (err) { ++ netdev_err(dev, "failed to allocate tx irq\n"); ++ goto err_out; ++ } ++ priv->txch.dma.irq = priv->tx_irq; ++ ++ priv->rxch.dma.nr = rx; ++ priv->rxch.dma.dev = &priv->pdev->dev; ++ ltq_dma_alloc_rx(&priv->rxch.dma); ++ for (priv->rxch.dma.desc = 0; priv->rxch.dma.desc < LTQ_DESC_NUM; ++ priv->rxch.dma.desc++) { ++ if (ltq_etop_alloc_skb(&priv->rxch)) { ++ netdev_err(dev, "failed to allocate skbs\n"); ++ err = -ENOMEM; ++ goto err_out; + } +- ch->dma.irq = irq; + } +- return 0; ++ priv->rxch.dma.desc = 0; ++ err = request_irq(priv->rx_irq, ltq_etop_dma_irq, 0, "eth_rx", priv); ++ if (err) ++ netdev_err(dev, "failed to allocate rx irq\n"); ++ else ++ priv->rxch.dma.irq = priv->rx_irq; ++err_out: ++ return err; + } + + static void +@@ -301,6 +447,39 @@ static const struct ethtool_ops ltq_etop + }; + + static int ++ltq_etop_mdio_wr_xr9(struct mii_bus *bus, int phy_addr, ++ int phy_reg, u16 phy_data) ++{ ++ u32 val = MDIO_XR9_REQUEST | MDIO_XR9_WRITE | ++ (phy_data << MDIO_XR9_WR_OFFSET) | ++ ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) | ++ ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET); ++ ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL); ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ return 0; ++} ++ ++static int ++ltq_etop_mdio_rd_xr9(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ u32 val = MDIO_XR9_REQUEST | MDIO_XR9_READ | ++ ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) | ++ ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET); ++ ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL); ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ val = ltq_gbit_r32(LTQ_GBIT_MDIO_DATA) & MDIO_XR9_RD_MASK; ++ return val; ++} ++ ++static int + ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, int phy_reg, u16 phy_data) + { + u32 val = MDIO_REQUEST | +@@ -308,9 +487,9 @@ ltq_etop_mdio_wr(struct mii_bus *bus, in + ((phy_reg & MDIO_REG_MASK) << MDIO_REG_OFFSET) | + phy_data; + +- while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST) ++ while (ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_REQUEST) + ; +- ltq_etop_w32(val, LTQ_ETOP_MDIO); ++ ltq_etop_w32(val, LTQ_ETOP_MDIO_ACC); + return 0; + } + +@@ -321,12 +500,12 @@ ltq_etop_mdio_rd(struct mii_bus *bus, in + ((phy_addr & MDIO_ADDR_MASK) << MDIO_ADDR_OFFSET) | + ((phy_reg & MDIO_REG_MASK) << MDIO_REG_OFFSET); + +- while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST) ++ while (ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_REQUEST) + ; +- ltq_etop_w32(val, LTQ_ETOP_MDIO); +- while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST) ++ ltq_etop_w32(val, LTQ_ETOP_MDIO_ACC); ++ while (ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_REQUEST) + ; +- val = ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_VAL_MASK; ++ val = ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_VAL_MASK; + return val; + } + +@@ -342,7 +521,10 @@ ltq_etop_mdio_probe(struct net_device *d + struct ltq_etop_priv *priv = netdev_priv(dev); + struct phy_device *phydev; + +- phydev = phy_find_first(priv->mii_bus); ++ if (of_machine_is_compatible("lantiq,ase")) ++ phydev = mdiobus_get_phy(priv->mii_bus, 8); ++ else ++ phydev = mdiobus_get_phy(priv->mii_bus, 0); + + if (!phydev) { + netdev_err(dev, "no PHY found\n"); +@@ -350,14 +532,17 @@ ltq_etop_mdio_probe(struct net_device *d + } + + phydev = phy_connect(dev, phydev_name(phydev), +- <q_etop_mdio_link, priv->pldata->mii_mode); ++ <q_etop_mdio_link, priv->mii_mode); + + if (IS_ERR(phydev)) { + netdev_err(dev, "Could not attach to PHY\n"); + return PTR_ERR(phydev); + } + +- phy_set_max_speed(phydev, SPEED_100); ++ if (of_machine_is_compatible("lantiq,ar9")) ++ phy_set_max_speed(phydev, SPEED_1000); ++ else ++ phy_set_max_speed(phydev, SPEED_100); + + phy_attached_info(phydev); + +@@ -378,8 +563,13 @@ ltq_etop_mdio_init(struct net_device *de + } + + priv->mii_bus->priv = dev; +- priv->mii_bus->read = ltq_etop_mdio_rd; +- priv->mii_bus->write = ltq_etop_mdio_wr; ++ if (of_machine_is_compatible("lantiq,ar9")) { ++ priv->mii_bus->read = ltq_etop_mdio_rd_xr9; ++ priv->mii_bus->write = ltq_etop_mdio_wr_xr9; ++ } else { ++ priv->mii_bus->read = ltq_etop_mdio_rd; ++ priv->mii_bus->write = ltq_etop_mdio_wr; ++ } + priv->mii_bus->name = "ltq_mii"; + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + priv->pdev->name, priv->pdev->id); +@@ -416,18 +606,21 @@ static int + ltq_etop_open(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; ++ unsigned long flags; + +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- struct ltq_etop_chan *ch = &priv->ch[i]; ++ napi_enable(&priv->txch.napi); ++ napi_enable(&priv->rxch.napi); ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_open(&priv->txch.dma); ++ ltq_dma_enable_irq(&priv->txch.dma); ++ ltq_dma_open(&priv->rxch.dma); ++ ltq_dma_enable_irq(&priv->rxch.dma); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ if (dev->phydev) ++ phy_start(dev->phydev); + +- if (!IS_TX(i) && (!IS_RX(i))) +- continue; +- ltq_dma_open(&ch->dma); +- ltq_dma_enable_irq(&ch->dma); +- napi_enable(&ch->napi); +- } +- phy_start(dev->phydev); + netif_tx_start_all_queues(dev); + return 0; + } +@@ -436,18 +629,19 @@ static int + ltq_etop_stop(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; ++ unsigned long flags; + + netif_tx_stop_all_queues(dev); +- phy_stop(dev->phydev); +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- struct ltq_etop_chan *ch = &priv->ch[i]; +- +- if (!IS_RX(i) && !IS_TX(i)) +- continue; +- napi_disable(&ch->napi); +- ltq_dma_close(&ch->dma); +- } ++ if (dev->phydev) ++ phy_stop(dev->phydev); ++ napi_disable(&priv->txch.napi); ++ napi_disable(&priv->rxch.napi); ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_close(&priv->txch.dma); ++ ltq_dma_close(&priv->rxch.dma); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ + return 0; + } + +@@ -457,16 +651,16 @@ ltq_etop_tx(struct sk_buff *skb, struct + int queue = skb_get_queue_mapping(skb); + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue); + struct ltq_etop_priv *priv = netdev_priv(dev); +- struct ltq_etop_chan *ch = &priv->ch[(queue << 1) | 1]; +- struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; +- int len; ++ struct ltq_dma_desc *desc = ++ &priv->txch.dma.desc_base[priv->txch.dma.desc]; + unsigned long flags; + u32 byte_offset; ++ int len; + + len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + +- if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) { +- dev_kfree_skb_any(skb); ++ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ++ priv->txch.skb[priv->txch.dma.desc]) { + netdev_err(dev, "tx ring full\n"); + netif_tx_stop_queue(txq); + return NETDEV_TX_BUSY; +@@ -474,7 +668,7 @@ ltq_etop_tx(struct sk_buff *skb, struct + + /* dma needs to start on a 16 byte aligned address */ + byte_offset = CPHYSADDR(skb->data) % 16; +- ch->skb[ch->dma.desc] = skb; ++ priv->txch.skb[priv->txch.dma.desc] = skb; + + netif_trans_update(dev); + +@@ -484,11 +678,11 @@ ltq_etop_tx(struct sk_buff *skb, struct + wmb(); + desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP | + LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK); +- ch->dma.desc++; +- ch->dma.desc %= LTQ_DESC_NUM; ++ priv->txch.dma.desc++; ++ priv->txch.dma.desc %= LTQ_DESC_NUM; + spin_unlock_irqrestore(&priv->lock, flags); + +- if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN) ++ if (priv->txch.dma.desc_base[priv->txch.dma.desc].ctl & LTQ_DMA_OWN) + netif_tx_stop_queue(txq); + + return NETDEV_TX_OK; +@@ -499,11 +693,14 @@ ltq_etop_change_mtu(struct net_device *d + { + struct ltq_etop_priv *priv = netdev_priv(dev); + unsigned long flags; ++ int max; + + dev->mtu = new_mtu; + ++ max = ETH_HLEN + VLAN_HLEN + new_mtu + ETH_FCS_LEN; ++ + spin_lock_irqsave(&priv->lock, flags); +- ltq_etop_w32((ETOP_PLEN_UNDER << 16) | new_mtu, LTQ_ETOP_IGPLEN); ++ ltq_etop_w32((ETOP_PLEN_UNDER << 16) | max, LTQ_ETOP_IGPLEN); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +@@ -556,6 +753,9 @@ ltq_etop_init(struct net_device *dev) + if (err) + goto err_hw; + ltq_etop_change_mtu(dev, 1500); ++ err = ltq_etop_dma_init(dev); ++ if (err) ++ goto err_hw; + + memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr)); + if (!is_valid_ether_addr(mac.sa_data)) { +@@ -573,9 +773,10 @@ ltq_etop_init(struct net_device *dev) + dev->addr_assign_type = NET_ADDR_RANDOM; + + ltq_etop_set_multicast_list(dev); +- err = ltq_etop_mdio_init(dev); +- if (err) +- goto err_netdev; ++ if (!ltq_etop_mdio_init(dev)) ++ dev->ethtool_ops = <q_etop_ethtool_ops; ++ else ++ pr_warn("etop: mdio probe failed\n");; + return 0; + + err_netdev: +@@ -595,6 +796,9 @@ ltq_etop_tx_timeout(struct net_device *d + err = ltq_etop_hw_init(dev); + if (err) + goto err_hw; ++ err = ltq_etop_dma_init(dev); ++ if (err) ++ goto err_hw; + netif_trans_update(dev); + netif_wake_queue(dev); + return; +@@ -618,14 +822,18 @@ static const struct net_device_ops ltq_e + .ndo_tx_timeout = ltq_etop_tx_timeout, + }; + +-static int __init +-ltq_etop_probe(struct platform_device *pdev) ++static int ltq_etop_probe(struct platform_device *pdev) + { + struct net_device *dev; + struct ltq_etop_priv *priv; +- struct resource *res; ++ struct resource *res, *gbit_res, irqres[2]; + int err; +- int i; ++ ++ err = of_irq_to_resource_table(pdev->dev.of_node, irqres, 2); ++ if (err != 2) { ++ dev_err(&pdev->dev, "failed to get etop irqs\n"); ++ return -EINVAL; ++ } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { +@@ -651,31 +859,62 @@ ltq_etop_probe(struct platform_device *p + goto err_out; + } + +- dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4); +- if (!dev) { +- err = -ENOMEM; +- goto err_out; ++ if (of_machine_is_compatible("lantiq,ar9")) { ++ gbit_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!gbit_res) { ++ dev_err(&pdev->dev, "failed to get gbit resource\n"); ++ err = -ENOENT; ++ goto err_out; ++ } ++ ltq_gbit_membase = devm_ioremap(&pdev->dev, ++ gbit_res->start, resource_size(gbit_res)); ++ if (!ltq_gbit_membase) { ++ dev_err(&pdev->dev, "failed to remap gigabit switch %d\n", ++ pdev->id); ++ err = -ENOMEM; ++ goto err_out; ++ } + } ++ ++ dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4); + strcpy(dev->name, "eth%d"); + dev->netdev_ops = <q_eth_netdev_ops; +- dev->ethtool_ops = <q_etop_ethtool_ops; + priv = netdev_priv(dev); + priv->res = res; + priv->pdev = pdev; +- priv->pldata = dev_get_platdata(&pdev->dev); + priv->netdev = dev; ++ priv->tx_irq = irqres[0].start; ++ priv->rx_irq = irqres[1].start; ++ err = of_get_phy_mode(pdev->dev.of_node, &priv->mii_mode); ++ if (err) ++ pr_err("Can't find phy-mode for port\n"); ++ ++ of_get_mac_address(pdev->dev.of_node, priv->mac); ++ ++ priv->clk_ppe = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk_ppe)) ++ return PTR_ERR(priv->clk_ppe); ++ if (of_machine_is_compatible("lantiq,ar9")) { ++ priv->clk_switch = clk_get(&pdev->dev, "switch"); ++ if (IS_ERR(priv->clk_switch)) ++ return PTR_ERR(priv->clk_switch); ++ } ++ if (of_machine_is_compatible("lantiq,ase")) { ++ priv->clk_ephy = clk_get(&pdev->dev, "ephy"); ++ if (IS_ERR(priv->clk_ephy)) ++ return PTR_ERR(priv->clk_ephy); ++ priv->clk_ephycgu = clk_get(&pdev->dev, "ephycgu"); ++ if (IS_ERR(priv->clk_ephycgu)) ++ return PTR_ERR(priv->clk_ephycgu); ++ } ++ + spin_lock_init(&priv->lock); + SET_NETDEV_DEV(dev, &pdev->dev); + +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- if (IS_TX(i)) +- netif_napi_add(dev, &priv->ch[i].napi, +- ltq_etop_poll_tx, 8); +- else if (IS_RX(i)) +- netif_napi_add(dev, &priv->ch[i].napi, +- ltq_etop_poll_rx, 32); +- priv->ch[i].netdev = dev; +- } ++ netif_napi_add(dev, &priv->txch.napi, ltq_etop_poll_tx, 8); ++ netif_napi_add(dev, &priv->rxch.napi, ltq_etop_poll_rx, 32); ++ priv->txch.netdev = dev; ++ priv->rxch.netdev = dev; + + err = register_netdev(dev); + if (err) +@@ -704,31 +943,22 @@ ltq_etop_remove(struct platform_device * + return 0; + } + ++static const struct of_device_id ltq_etop_match[] = { ++ { .compatible = "lantiq,etop-xway" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ltq_etop_match); ++ + static struct platform_driver ltq_mii_driver = { ++ .probe = ltq_etop_probe, + .remove = ltq_etop_remove, + .driver = { + .name = "ltq_etop", ++ .of_match_table = ltq_etop_match, + }, + }; + +-int __init +-init_ltq_etop(void) +-{ +- int ret = platform_driver_probe(<q_mii_driver, ltq_etop_probe); +- +- if (ret) +- pr_err("ltq_etop: Error registering platform driver!"); +- return ret; +-} +- +-static void __exit +-exit_ltq_etop(void) +-{ +- platform_driver_unregister(<q_mii_driver); +-} +- +-module_init(init_ltq_etop); +-module_exit(exit_ltq_etop); ++module_platform_driver(ltq_mii_driver); + + MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); + MODULE_DESCRIPTION("Lantiq SoC ETOP"); diff --git a/target/linux/lantiq/patches-5.15/0031-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch b/target/linux/lantiq/patches-5.15/0031-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch new file mode 100644 index 0000000000..154cf226c1 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0031-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch @@ -0,0 +1,1034 @@ +From f17e50f67fa3c77624edf2ca03fae0d50f0ce39b Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Thu, 7 Aug 2014 18:26:42 +0200 +Subject: [PATCH 31/36] I2C: MIPS: lantiq: add FALC-ON i2c bus master + +This patch adds the driver needed to make the I2C bus work on FALC-ON SoCs. + +Signed-off-by: Thomas Langer <thomas.langer@lantiq.com> +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + drivers/i2c/busses/Kconfig | 10 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-lantiq.c | 747 +++++++++++++++++++++++++++++++++++++++ + drivers/i2c/busses/i2c-lantiq.h | 234 ++++++++++++ + 4 files changed, 992 insertions(+) + create mode 100644 drivers/i2c/busses/i2c-lantiq.c + create mode 100644 drivers/i2c/busses/i2c-lantiq.h + +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -753,6 +753,16 @@ config I2C_MESON + If you say yes to this option, support will be included for the + I2C interface on the Amlogic Meson family of SoCs. + ++config I2C_LANTIQ ++ tristate "Lantiq I2C interface" ++ depends on LANTIQ && SOC_FALCON ++ help ++ If you say yes to this option, support will be included for the ++ Lantiq I2C core. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-lantiq. ++ + config I2C_MPC + tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" + depends on PPC +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-l + obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o + obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o + obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o ++obj-$(CONFIG_I2C_LANTIQ) += i2c-lantiq.o + obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o + obj-$(CONFIG_I2C_MESON) += i2c-meson.o + obj-$(CONFIG_I2C_MPC) += i2c-mpc.o +--- /dev/null ++++ b/drivers/i2c/busses/i2c-lantiq.c +@@ -0,0 +1,747 @@ ++ ++/* ++ * Lantiq I2C bus adapter ++ * ++ * Parts based on i2c-designware.c and other i2c drivers from Linux 2.6.33 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Copyright (C) 2012 Thomas Langer <thomas.langer@lantiq.com> ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/delay.h> ++#include <linux/slab.h> /* for kzalloc, kfree */ ++#include <linux/i2c.h> ++#include <linux/errno.h> ++#include <linux/completion.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/io.h> ++#include <linux/of_irq.h> ++ ++#include <lantiq_soc.h> ++#include "i2c-lantiq.h" ++ ++/* ++ * CURRENT ISSUES: ++ * - no high speed support ++ * - ten bit mode is not tested (no slave devices) ++ */ ++ ++/* access macros */ ++#define i2c_r32(reg) \ ++ __raw_readl(&(priv->membase)->reg) ++#define i2c_w32(val, reg) \ ++ __raw_writel(val, &(priv->membase)->reg) ++#define i2c_w32_mask(clear, set, reg) \ ++ i2c_w32((i2c_r32(reg) & ~(clear)) | (set), reg) ++ ++#define DRV_NAME "i2c-lantiq" ++#define DRV_VERSION "1.00" ++ ++#define LTQ_I2C_BUSY_TIMEOUT 20 /* ms */ ++ ++#ifdef DEBUG ++#define LTQ_I2C_XFER_TIMEOUT (25*HZ) ++#else ++#define LTQ_I2C_XFER_TIMEOUT HZ ++#endif ++ ++#define LTQ_I2C_IMSC_DEFAULT_MASK (I2C_IMSC_I2C_P_INT_EN | \ ++ I2C_IMSC_I2C_ERR_INT_EN) ++ ++#define LTQ_I2C_ARB_LOST (1 << 0) ++#define LTQ_I2C_NACK (1 << 1) ++#define LTQ_I2C_RX_UFL (1 << 2) ++#define LTQ_I2C_RX_OFL (1 << 3) ++#define LTQ_I2C_TX_UFL (1 << 4) ++#define LTQ_I2C_TX_OFL (1 << 5) ++ ++struct ltq_i2c { ++ struct mutex mutex; ++ ++ ++ /* active clock settings */ ++ unsigned int input_clock; /* clock input for i2c hardware block */ ++ unsigned int i2c_clock; /* approximated bus clock in kHz */ ++ ++ struct clk *clk_gate; ++ struct clk *clk_input; ++ ++ ++ /* resources (memory and interrupts) */ ++ int irq_lb; /* last burst irq */ ++ ++ struct lantiq_reg_i2c __iomem *membase; /* base of mapped registers */ ++ ++ struct i2c_adapter adap; ++ struct device *dev; ++ ++ struct completion cmd_complete; ++ ++ ++ /* message transfer data */ ++ struct i2c_msg *current_msg; /* current message */ ++ int msgs_num; /* number of messages to handle */ ++ u8 *msg_buf; /* current buffer */ ++ u32 msg_buf_len; /* remaining length of current buffer */ ++ int msg_err; /* error status of the current transfer */ ++ ++ ++ /* master status codes */ ++ enum { ++ STATUS_IDLE, ++ STATUS_ADDR, /* address phase */ ++ STATUS_WRITE, ++ STATUS_READ, ++ STATUS_READ_END, ++ STATUS_STOP ++ } status; ++}; ++ ++static irqreturn_t ltq_i2c_isr(int irq, void *dev_id); ++ ++static inline void enable_burst_irq(struct ltq_i2c *priv) ++{ ++ i2c_w32_mask(0, I2C_IMSC_LBREQ_INT_EN | I2C_IMSC_BREQ_INT_EN, imsc); ++} ++static inline void disable_burst_irq(struct ltq_i2c *priv) ++{ ++ i2c_w32_mask(I2C_IMSC_LBREQ_INT_EN | I2C_IMSC_BREQ_INT_EN, 0, imsc); ++} ++ ++static void prepare_msg_send_addr(struct ltq_i2c *priv) ++{ ++ struct i2c_msg *msg = priv->current_msg; ++ int rd = !!(msg->flags & I2C_M_RD); /* extends to 0 or 1 */ ++ u16 addr = msg->addr; ++ ++ /* new i2c_msg */ ++ priv->msg_buf = msg->buf; ++ priv->msg_buf_len = msg->len; ++ if (rd) ++ priv->status = STATUS_READ; ++ else ++ priv->status = STATUS_WRITE; ++ ++ /* send slave address */ ++ if (msg->flags & I2C_M_TEN) { ++ i2c_w32(0xf0 | ((addr & 0x300) >> 7) | rd, txd); ++ i2c_w32(addr & 0xff, txd); ++ } else { ++ i2c_w32((addr & 0x7f) << 1 | rd, txd); ++ } ++} ++ ++static void ltq_i2c_set_tx_len(struct ltq_i2c *priv) ++{ ++ struct i2c_msg *msg = priv->current_msg; ++ int len = (msg->flags & I2C_M_TEN) ? 2 : 1; ++ ++ pr_debug("set_tx_len %cX\n", (msg->flags & I2C_M_RD) ? 'R' : 'T'); ++ ++ priv->status = STATUS_ADDR; ++ ++ if (!(msg->flags & I2C_M_RD)) ++ len += msg->len; ++ else ++ /* set maximum received packet size (before rx int!) */ ++ i2c_w32(msg->len, mrps_ctrl); ++ i2c_w32(len, tps_ctrl); ++ enable_burst_irq(priv); ++} ++ ++static int ltq_i2c_hw_set_clock(struct i2c_adapter *adap) ++{ ++ struct ltq_i2c *priv = i2c_get_adapdata(adap); ++ unsigned int input_clock = clk_get_rate(priv->clk_input); ++ u32 dec, inc = 1; ++ ++ /* clock changed? */ ++ if (priv->input_clock == input_clock) ++ return 0; ++ ++ /* ++ * this formula is only an approximation, found by the recommended ++ * values in the "I2C Architecture Specification 1.7.1" ++ */ ++ dec = input_clock / (priv->i2c_clock * 2); ++ if (dec <= 6) ++ return -ENXIO; ++ ++ i2c_w32(0, fdiv_high_cfg); ++ i2c_w32((inc << I2C_FDIV_CFG_INC_OFFSET) | ++ (dec << I2C_FDIV_CFG_DEC_OFFSET), ++ fdiv_cfg); ++ ++ dev_info(priv->dev, "setup clocks (in %d kHz, bus %d kHz, dec=%d)\n", ++ input_clock, priv->i2c_clock, dec); ++ ++ priv->input_clock = input_clock; ++ return 0; ++} ++ ++static int ltq_i2c_hw_init(struct i2c_adapter *adap) ++{ ++ int ret = 0; ++ struct ltq_i2c *priv = i2c_get_adapdata(adap); ++ ++ /* disable bus */ ++ i2c_w32_mask(I2C_RUN_CTRL_RUN_EN, 0, run_ctrl); ++ ++#ifndef DEBUG ++ /* set normal operation clock divider */ ++ i2c_w32(1 << I2C_CLC_RMC_OFFSET, clc); ++#else ++ /* for debugging a higher divider value! */ ++ i2c_w32(0xF0 << I2C_CLC_RMC_OFFSET, clc); ++#endif ++ ++ /* setup clock */ ++ ret = ltq_i2c_hw_set_clock(adap); ++ if (ret != 0) { ++ dev_warn(priv->dev, "invalid clock settings\n"); ++ return ret; ++ } ++ ++ /* configure fifo */ ++ i2c_w32(I2C_FIFO_CFG_TXFC | /* tx fifo as flow controller */ ++ I2C_FIFO_CFG_RXFC | /* rx fifo as flow controller */ ++ I2C_FIFO_CFG_TXFA_TXFA2 | /* tx fifo 4-byte aligned */ ++ I2C_FIFO_CFG_RXFA_RXFA2 | /* rx fifo 4-byte aligned */ ++ I2C_FIFO_CFG_TXBS_TXBS0 | /* tx fifo burst size is 1 word */ ++ I2C_FIFO_CFG_RXBS_RXBS0, /* rx fifo burst size is 1 word */ ++ fifo_cfg); ++ ++ /* configure address */ ++ i2c_w32(I2C_ADDR_CFG_SOPE_EN | /* generate stop when no more data in ++ the fifo */ ++ I2C_ADDR_CFG_SONA_EN | /* generate stop when NA received */ ++ I2C_ADDR_CFG_MnS_EN | /* we are master device */ ++ 0, /* our slave address (not used!) */ ++ addr_cfg); ++ ++ /* enable bus */ ++ i2c_w32_mask(0, I2C_RUN_CTRL_RUN_EN, run_ctrl); ++ ++ return 0; ++} ++ ++static int ltq_i2c_wait_bus_not_busy(struct ltq_i2c *priv) ++{ ++ unsigned long timeout; ++ ++ timeout = jiffies + msecs_to_jiffies(LTQ_I2C_BUSY_TIMEOUT); ++ ++ do { ++ u32 stat = i2c_r32(bus_stat); ++ ++ if ((stat & I2C_BUS_STAT_BS_MASK) == I2C_BUS_STAT_BS_FREE) ++ return 0; ++ ++ cond_resched(); ++ } while (!time_after_eq(jiffies, timeout)); ++ ++ dev_err(priv->dev, "timeout waiting for bus ready\n"); ++ return -ETIMEDOUT; ++} ++ ++static void ltq_i2c_tx(struct ltq_i2c *priv, int last) ++{ ++ if (priv->msg_buf_len && priv->msg_buf) { ++ i2c_w32(*priv->msg_buf, txd); ++ ++ if (--priv->msg_buf_len) ++ priv->msg_buf++; ++ else ++ priv->msg_buf = NULL; ++ } else { ++ last = 1; ++ } ++ ++ if (last) ++ disable_burst_irq(priv); ++} ++ ++static void ltq_i2c_rx(struct ltq_i2c *priv, int last) ++{ ++ u32 fifo_stat, timeout; ++ if (priv->msg_buf_len && priv->msg_buf) { ++ timeout = 5000000; ++ do { ++ fifo_stat = i2c_r32(ffs_stat); ++ } while (!fifo_stat && --timeout); ++ if (!timeout) { ++ last = 1; ++ pr_debug("\nrx timeout\n"); ++ goto err; ++ } ++ while (fifo_stat) { ++ *priv->msg_buf = i2c_r32(rxd); ++ if (--priv->msg_buf_len) { ++ priv->msg_buf++; ++ } else { ++ priv->msg_buf = NULL; ++ last = 1; ++ break; ++ } ++ /* ++ * do not read more than burst size, otherwise no "last ++ * burst" is generated and the transaction is blocked! ++ */ ++ fifo_stat = 0; ++ } ++ } else { ++ last = 1; ++ } ++err: ++ if (last) { ++ disable_burst_irq(priv); ++ ++ if (priv->status == STATUS_READ_END) { ++ /* ++ * do the STATUS_STOP and complete() here, as sometimes ++ * the tx_end is already seen before this is finished ++ */ ++ priv->status = STATUS_STOP; ++ complete(&priv->cmd_complete); ++ } else { ++ i2c_w32(I2C_ENDD_CTRL_SETEND, endd_ctrl); ++ priv->status = STATUS_READ_END; ++ } ++ } ++} ++ ++static void ltq_i2c_xfer_init(struct ltq_i2c *priv) ++{ ++ /* enable interrupts */ ++ i2c_w32(LTQ_I2C_IMSC_DEFAULT_MASK, imsc); ++ ++ /* trigger transfer of first msg */ ++ ltq_i2c_set_tx_len(priv); ++} ++ ++static void dump_msgs(struct i2c_msg msgs[], int num, int rx) ++{ ++#if defined(DEBUG) ++ int i, j; ++ pr_debug("Messages %d %s\n", num, rx ? "out" : "in"); ++ for (i = 0; i < num; i++) { ++ pr_debug("%2d %cX Msg(%d) addr=0x%X: ", i, ++ (msgs[i].flags & I2C_M_RD) ? 'R' : 'T', ++ msgs[i].len, msgs[i].addr); ++ if (!(msgs[i].flags & I2C_M_RD) || rx) { ++ for (j = 0; j < msgs[i].len; j++) ++ pr_debug("%02X ", msgs[i].buf[j]); ++ } ++ pr_debug("\n"); ++ } ++#endif ++} ++ ++static void ltq_i2c_release_bus(struct ltq_i2c *priv) ++{ ++ if ((i2c_r32(bus_stat) & I2C_BUS_STAT_BS_MASK) == I2C_BUS_STAT_BS_BM) ++ i2c_w32(I2C_ENDD_CTRL_SETEND, endd_ctrl); ++} ++ ++static int ltq_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], ++ int num) ++{ ++ struct ltq_i2c *priv = i2c_get_adapdata(adap); ++ int ret; ++ ++ dev_dbg(priv->dev, "xfer %u messages\n", num); ++ dump_msgs(msgs, num, 0); ++ ++ mutex_lock(&priv->mutex); ++ ++ init_completion(&priv->cmd_complete); ++ priv->current_msg = msgs; ++ priv->msgs_num = num; ++ priv->msg_err = 0; ++ priv->status = STATUS_IDLE; ++ ++ /* wait for the bus to become ready */ ++ ret = ltq_i2c_wait_bus_not_busy(priv); ++ if (ret) ++ goto done; ++ ++ while (priv->msgs_num) { ++ /* start the transfers */ ++ ltq_i2c_xfer_init(priv); ++ ++ /* wait for transfers to complete */ ++ ret = wait_for_completion_interruptible_timeout( ++ &priv->cmd_complete, LTQ_I2C_XFER_TIMEOUT); ++ if (ret == 0) { ++ dev_err(priv->dev, "controller timed out\n"); ++ ltq_i2c_hw_init(adap); ++ ret = -ETIMEDOUT; ++ goto done; ++ } else if (ret < 0) ++ goto done; ++ ++ if (priv->msg_err) { ++ if (priv->msg_err & LTQ_I2C_NACK) ++ ret = -ENXIO; ++ else ++ ret = -EREMOTEIO; ++ goto done; ++ } ++ if (--priv->msgs_num) ++ priv->current_msg++; ++ } ++ /* no error? */ ++ ret = num; ++ ++done: ++ ltq_i2c_release_bus(priv); ++ ++ mutex_unlock(&priv->mutex); ++ ++ if (ret >= 0) ++ dump_msgs(msgs, num, 1); ++ ++ pr_debug("XFER ret %d\n", ret); ++ return ret; ++} ++ ++static irqreturn_t ltq_i2c_isr_burst(int irq, void *dev_id) ++{ ++ struct ltq_i2c *priv = dev_id; ++ struct i2c_msg *msg = priv->current_msg; ++ int last = (irq == priv->irq_lb); ++ ++ if (last) ++ pr_debug("LB "); ++ else ++ pr_debug("B "); ++ ++ if (msg->flags & I2C_M_RD) { ++ switch (priv->status) { ++ case STATUS_ADDR: ++ pr_debug("X"); ++ prepare_msg_send_addr(priv); ++ disable_burst_irq(priv); ++ break; ++ case STATUS_READ: ++ case STATUS_READ_END: ++ pr_debug("R"); ++ ltq_i2c_rx(priv, last); ++ break; ++ default: ++ disable_burst_irq(priv); ++ pr_warn("Status R %d\n", priv->status); ++ break; ++ } ++ } else { ++ switch (priv->status) { ++ case STATUS_ADDR: ++ pr_debug("x"); ++ prepare_msg_send_addr(priv); ++ break; ++ case STATUS_WRITE: ++ pr_debug("w"); ++ ltq_i2c_tx(priv, last); ++ break; ++ default: ++ disable_burst_irq(priv); ++ pr_warn("Status W %d\n", priv->status); ++ break; ++ } ++ } ++ ++ i2c_w32(I2C_ICR_BREQ_INT_CLR | I2C_ICR_LBREQ_INT_CLR, icr); ++ return IRQ_HANDLED; ++} ++ ++static void ltq_i2c_isr_prot(struct ltq_i2c *priv) ++{ ++ u32 i_pro = i2c_r32(p_irqss); ++ ++ pr_debug("i2c-p"); ++ ++ /* not acknowledge */ ++ if (i_pro & I2C_P_IRQSS_NACK) { ++ priv->msg_err |= LTQ_I2C_NACK; ++ pr_debug(" nack"); ++ } ++ ++ /* arbitration lost */ ++ if (i_pro & I2C_P_IRQSS_AL) { ++ priv->msg_err |= LTQ_I2C_ARB_LOST; ++ pr_debug(" arb-lost"); ++ } ++ /* tx -> rx switch */ ++ if (i_pro & I2C_P_IRQSS_RX) ++ pr_debug(" rx"); ++ ++ /* tx end */ ++ if (i_pro & I2C_P_IRQSS_TX_END) ++ pr_debug(" txend"); ++ pr_debug("\n"); ++ ++ if (!priv->msg_err) { ++ /* tx -> rx switch */ ++ if (i_pro & I2C_P_IRQSS_RX) { ++ priv->status = STATUS_READ; ++ enable_burst_irq(priv); ++ } ++ if (i_pro & I2C_P_IRQSS_TX_END) { ++ if (priv->status == STATUS_READ) ++ priv->status = STATUS_READ_END; ++ else { ++ disable_burst_irq(priv); ++ priv->status = STATUS_STOP; ++ } ++ } ++ } ++ ++ i2c_w32(i_pro, p_irqsc); ++} ++ ++static irqreturn_t ltq_i2c_isr(int irq, void *dev_id) ++{ ++ u32 i_raw, i_err = 0; ++ struct ltq_i2c *priv = dev_id; ++ ++ i_raw = i2c_r32(mis); ++ pr_debug("i_raw 0x%08X\n", i_raw); ++ ++ /* error interrupt */ ++ if (i_raw & I2C_RIS_I2C_ERR_INT_INTOCC) { ++ i_err = i2c_r32(err_irqss); ++ pr_debug("i_err 0x%08X bus_stat 0x%04X\n", ++ i_err, i2c_r32(bus_stat)); ++ ++ /* tx fifo overflow (8) */ ++ if (i_err & I2C_ERR_IRQSS_TXF_OFL) ++ priv->msg_err |= LTQ_I2C_TX_OFL; ++ ++ /* tx fifo underflow (4) */ ++ if (i_err & I2C_ERR_IRQSS_TXF_UFL) ++ priv->msg_err |= LTQ_I2C_TX_UFL; ++ ++ /* rx fifo overflow (2) */ ++ if (i_err & I2C_ERR_IRQSS_RXF_OFL) ++ priv->msg_err |= LTQ_I2C_RX_OFL; ++ ++ /* rx fifo underflow (1) */ ++ if (i_err & I2C_ERR_IRQSS_RXF_UFL) ++ priv->msg_err |= LTQ_I2C_RX_UFL; ++ ++ i2c_w32(i_err, err_irqsc); ++ } ++ ++ /* protocol interrupt */ ++ if (i_raw & I2C_RIS_I2C_P_INT_INTOCC) ++ ltq_i2c_isr_prot(priv); ++ ++ if ((priv->msg_err) || (priv->status == STATUS_STOP)) ++ complete(&priv->cmd_complete); ++ ++ return IRQ_HANDLED; ++} ++ ++static u32 ltq_i2c_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | ++ I2C_FUNC_10BIT_ADDR | ++ I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm ltq_i2c_algorithm = { ++ .master_xfer = ltq_i2c_xfer, ++ .functionality = ltq_i2c_functionality, ++}; ++ ++static int ltq_i2c_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct ltq_i2c *priv; ++ struct i2c_adapter *adap; ++ struct resource *mmres, irqres[4]; ++ int ret = 0; ++ ++ dev_dbg(&pdev->dev, "probing\n"); ++ ++ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ret = of_irq_to_resource_table(node, irqres, 4); ++ if (!mmres || (ret != 4)) { ++ dev_err(&pdev->dev, "no resources\n"); ++ return -ENODEV; ++ } ++ ++ /* allocate private data */ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&pdev->dev, "can't allocate private data\n"); ++ return -ENOMEM; ++ } ++ ++ adap = &priv->adap; ++ i2c_set_adapdata(adap, priv); ++ adap->owner = THIS_MODULE; ++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ strlcpy(adap->name, DRV_NAME "-adapter", sizeof(adap->name)); ++ adap->algo = <q_i2c_algorithm; ++ adap->dev.parent = &pdev->dev; ++ adap->dev.of_node = pdev->dev.of_node; ++ ++ if (of_property_read_u32(node, "clock-frequency", &priv->i2c_clock)) { ++ dev_warn(&pdev->dev, "No I2C speed selected, using 100kHz\n"); ++ priv->i2c_clock = 100000; ++ } ++ ++ init_completion(&priv->cmd_complete); ++ mutex_init(&priv->mutex); ++ ++ priv->membase = devm_ioremap_resource(&pdev->dev, mmres); ++ if (IS_ERR(priv->membase)) ++ return PTR_ERR(priv->membase); ++ ++ priv->dev = &pdev->dev; ++ priv->irq_lb = irqres[0].start; ++ ++ ret = devm_request_irq(&pdev->dev, irqres[0].start, ltq_i2c_isr_burst, ++ 0x0, "i2c lb", priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get last burst IRQ %d\n", ++ irqres[0].start); ++ return -ENODEV; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, irqres[1].start, ltq_i2c_isr_burst, ++ 0x0, "i2c b", priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get burst IRQ %d\n", ++ irqres[1].start); ++ return -ENODEV; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, irqres[2].start, ltq_i2c_isr, ++ 0x0, "i2c err", priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get error IRQ %d\n", ++ irqres[2].start); ++ return -ENODEV; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, irqres[3].start, ltq_i2c_isr, ++ 0x0, "i2c p", priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get protocol IRQ %d\n", ++ irqres[3].start); ++ return -ENODEV; ++ } ++ ++ dev_dbg(&pdev->dev, "mapped io-space to %p\n", priv->membase); ++ dev_dbg(&pdev->dev, "use IRQs %d, %d, %d, %d\n", irqres[0].start, ++ irqres[1].start, irqres[2].start, irqres[3].start); ++ ++ priv->clk_gate = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk_gate)) { ++ dev_err(&pdev->dev, "failed to get i2c clk\n"); ++ return -ENOENT; ++ } ++ ++ /* this is a static clock, which has no refcounting */ ++ priv->clk_input = clk_get_fpi(); ++ if (IS_ERR(priv->clk_input)) { ++ dev_err(&pdev->dev, "failed to get fpi clk\n"); ++ return -ENOENT; ++ } ++ ++ clk_activate(priv->clk_gate); ++ ++ /* add our adapter to the i2c stack */ ++ ret = i2c_add_numbered_adapter(adap); ++ if (ret) { ++ dev_err(&pdev->dev, "can't register I2C adapter\n"); ++ goto out; ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ i2c_set_adapdata(adap, priv); ++ ++ /* print module version information */ ++ dev_dbg(&pdev->dev, "module id=%u revision=%u\n", ++ (i2c_r32(id) & I2C_ID_ID_MASK) >> I2C_ID_ID_OFFSET, ++ (i2c_r32(id) & I2C_ID_REV_MASK) >> I2C_ID_REV_OFFSET); ++ ++ /* initialize HW */ ++ ret = ltq_i2c_hw_init(adap); ++ if (ret) { ++ dev_err(&pdev->dev, "can't configure adapter\n"); ++ i2c_del_adapter(adap); ++ platform_set_drvdata(pdev, NULL); ++ goto out; ++ } else { ++ dev_info(&pdev->dev, "version %s\n", DRV_VERSION); ++ } ++ ++out: ++ /* if init failed, we need to deactivate the clock gate */ ++ if (ret) ++ clk_deactivate(priv->clk_gate); ++ ++ return ret; ++} ++ ++static int ltq_i2c_remove(struct platform_device *pdev) ++{ ++ struct ltq_i2c *priv = platform_get_drvdata(pdev); ++ ++ /* disable bus */ ++ i2c_w32_mask(I2C_RUN_CTRL_RUN_EN, 0, run_ctrl); ++ ++ /* power down the core */ ++ clk_deactivate(priv->clk_gate); ++ ++ /* remove driver */ ++ i2c_del_adapter(&priv->adap); ++ kfree(priv); ++ ++ dev_dbg(&pdev->dev, "removed\n"); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++static const struct of_device_id ltq_i2c_match[] = { ++ { .compatible = "lantiq,lantiq-i2c" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ltq_i2c_match); ++ ++static struct platform_driver ltq_i2c_driver = { ++ .probe = ltq_i2c_probe, ++ .remove = ltq_i2c_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = ltq_i2c_match, ++ }, ++}; ++ ++module_platform_driver(ltq_i2c_driver); ++ ++MODULE_DESCRIPTION("Lantiq I2C bus adapter"); ++MODULE_AUTHOR("Thomas Langer <thomas.langer@lantiq.com>"); ++MODULE_ALIAS("platform:" DRV_NAME); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); +--- /dev/null ++++ b/drivers/i2c/busses/i2c-lantiq.h +@@ -0,0 +1,234 @@ ++#ifndef I2C_LANTIQ_H ++#define I2C_LANTIQ_H ++ ++/* I2C register structure */ ++struct lantiq_reg_i2c { ++ /* I2C Kernel Clock Control Register */ ++ unsigned int clc; /* 0x00000000 */ ++ /* Reserved */ ++ unsigned int res_0; /* 0x00000004 */ ++ /* I2C Identification Register */ ++ unsigned int id; /* 0x00000008 */ ++ /* Reserved */ ++ unsigned int res_1; /* 0x0000000C */ ++ /* ++ * I2C RUN Control Register ++ * This register enables and disables the I2C peripheral. Before ++ * enabling, the I2C has to be configured properly. After enabling ++ * no configuration is possible ++ */ ++ unsigned int run_ctrl; /* 0x00000010 */ ++ /* ++ * I2C End Data Control Register ++ * This register is used to either turn around the data transmission ++ * direction or to address another slave without sending a stop ++ * condition. Also the software can stop the slave-transmitter by ++ * sending a not-accolade when working as master-receiver or even ++ * stop data transmission immediately when operating as ++ * master-transmitter. The writing to the bits of this control ++ * register is only effective when in MASTER RECEIVES BYTES, MASTER ++ * TRANSMITS BYTES, MASTER RESTART or SLAVE RECEIVE BYTES state ++ */ ++ unsigned int endd_ctrl; /* 0x00000014 */ ++ /* ++ * I2C Fractional Divider Configuration Register ++ * These register is used to program the fractional divider of the I2C ++ * bus. Before the peripheral is switched on by setting the RUN-bit the ++ * two (fixed) values for the two operating frequencies are programmed ++ * into these (configuration) registers. The Register FDIV_HIGH_CFG has ++ * the same layout as I2C_FDIV_CFG. ++ */ ++ unsigned int fdiv_cfg; /* 0x00000018 */ ++ /* ++ * I2C Fractional Divider (highspeed mode) Configuration Register ++ * These register is used to program the fractional divider of the I2C ++ * bus. Before the peripheral is switched on by setting the RUN-bit the ++ * two (fixed) values for the two operating frequencies are programmed ++ * into these (configuration) registers. The Register FDIV_CFG has the ++ * same layout as I2C_FDIV_CFG. ++ */ ++ unsigned int fdiv_high_cfg; /* 0x0000001C */ ++ /* I2C Address Configuration Register */ ++ unsigned int addr_cfg; /* 0x00000020 */ ++ /* I2C Bus Status Register ++ * This register gives a status information of the I2C. This additional ++ * information can be used by the software to start proper actions. ++ */ ++ unsigned int bus_stat; /* 0x00000024 */ ++ /* I2C FIFO Configuration Register */ ++ unsigned int fifo_cfg; /* 0x00000028 */ ++ /* I2C Maximum Received Packet Size Register */ ++ unsigned int mrps_ctrl; /* 0x0000002C */ ++ /* I2C Received Packet Size Status Register */ ++ unsigned int rps_stat; /* 0x00000030 */ ++ /* I2C Transmit Packet Size Register */ ++ unsigned int tps_ctrl; /* 0x00000034 */ ++ /* I2C Filled FIFO Stages Status Register */ ++ unsigned int ffs_stat; /* 0x00000038 */ ++ /* Reserved */ ++ unsigned int res_2; /* 0x0000003C */ ++ /* I2C Timing Configuration Register */ ++ unsigned int tim_cfg; /* 0x00000040 */ ++ /* Reserved */ ++ unsigned int res_3[7]; /* 0x00000044 */ ++ /* I2C Error Interrupt Request Source Mask Register */ ++ unsigned int err_irqsm; /* 0x00000060 */ ++ /* I2C Error Interrupt Request Source Status Register */ ++ unsigned int err_irqss; /* 0x00000064 */ ++ /* I2C Error Interrupt Request Source Clear Register */ ++ unsigned int err_irqsc; /* 0x00000068 */ ++ /* Reserved */ ++ unsigned int res_4; /* 0x0000006C */ ++ /* I2C Protocol Interrupt Request Source Mask Register */ ++ unsigned int p_irqsm; /* 0x00000070 */ ++ /* I2C Protocol Interrupt Request Source Status Register */ ++ unsigned int p_irqss; /* 0x00000074 */ ++ /* I2C Protocol Interrupt Request Source Clear Register */ ++ unsigned int p_irqsc; /* 0x00000078 */ ++ /* Reserved */ ++ unsigned int res_5; /* 0x0000007C */ ++ /* I2C Raw Interrupt Status Register */ ++ unsigned int ris; /* 0x00000080 */ ++ /* I2C Interrupt Mask Control Register */ ++ unsigned int imsc; /* 0x00000084 */ ++ /* I2C Masked Interrupt Status Register */ ++ unsigned int mis; /* 0x00000088 */ ++ /* I2C Interrupt Clear Register */ ++ unsigned int icr; /* 0x0000008C */ ++ /* I2C Interrupt Set Register */ ++ unsigned int isr; /* 0x00000090 */ ++ /* I2C DMA Enable Register */ ++ unsigned int dmae; /* 0x00000094 */ ++ /* Reserved */ ++ unsigned int res_6[8154]; /* 0x00000098 */ ++ /* I2C Transmit Data Register */ ++ unsigned int txd; /* 0x00008000 */ ++ /* Reserved */ ++ unsigned int res_7[4095]; /* 0x00008004 */ ++ /* I2C Receive Data Register */ ++ unsigned int rxd; /* 0x0000C000 */ ++ /* Reserved */ ++ unsigned int res_8[4095]; /* 0x0000C004 */ ++}; ++ ++/* ++ * Clock Divider for Normal Run Mode ++ * Max 8-bit divider value. IF RMC is 0 the module is disabled. Note: As long ++ * as the new divider value RMC is not valid, the register returns 0x0000 00xx ++ * on reading. ++ */ ++#define I2C_CLC_RMC_MASK 0x0000FF00 ++/* field offset */ ++#define I2C_CLC_RMC_OFFSET 8 ++ ++/* Fields of "I2C Identification Register" */ ++/* Module ID */ ++#define I2C_ID_ID_MASK 0x0000FF00 ++/* field offset */ ++#define I2C_ID_ID_OFFSET 8 ++/* Revision */ ++#define I2C_ID_REV_MASK 0x000000FF ++/* field offset */ ++#define I2C_ID_REV_OFFSET 0 ++ ++/* Fields of "I2C Interrupt Mask Control Register" */ ++/* Enable */ ++#define I2C_IMSC_BREQ_INT_EN 0x00000008 ++/* Enable */ ++#define I2C_IMSC_LBREQ_INT_EN 0x00000004 ++ ++/* Fields of "I2C Fractional Divider Configuration Register" */ ++/* field offset */ ++#define I2C_FDIV_CFG_INC_OFFSET 16 ++ ++/* Fields of "I2C Interrupt Mask Control Register" */ ++/* Enable */ ++#define I2C_IMSC_I2C_P_INT_EN 0x00000020 ++/* Enable */ ++#define I2C_IMSC_I2C_ERR_INT_EN 0x00000010 ++ ++/* Fields of "I2C Error Interrupt Request Source Status Register" */ ++/* TXF_OFL */ ++#define I2C_ERR_IRQSS_TXF_OFL 0x00000008 ++/* TXF_UFL */ ++#define I2C_ERR_IRQSS_TXF_UFL 0x00000004 ++/* RXF_OFL */ ++#define I2C_ERR_IRQSS_RXF_OFL 0x00000002 ++/* RXF_UFL */ ++#define I2C_ERR_IRQSS_RXF_UFL 0x00000001 ++ ++/* Fields of "I2C Raw Interrupt Status Register" */ ++/* Read: Interrupt occurred. */ ++#define I2C_RIS_I2C_ERR_INT_INTOCC 0x00000010 ++/* Read: Interrupt occurred. */ ++#define I2C_RIS_I2C_P_INT_INTOCC 0x00000020 ++ ++/* Fields of "I2C FIFO Configuration Register" */ ++/* TX FIFO Flow Control */ ++#define I2C_FIFO_CFG_TXFC 0x00020000 ++/* RX FIFO Flow Control */ ++#define I2C_FIFO_CFG_RXFC 0x00010000 ++/* Word aligned (character alignment of four characters) */ ++#define I2C_FIFO_CFG_TXFA_TXFA2 0x00002000 ++/* Word aligned (character alignment of four characters) */ ++#define I2C_FIFO_CFG_RXFA_RXFA2 0x00000200 ++/* 1 word */ ++#define I2C_FIFO_CFG_TXBS_TXBS0 0x00000000 ++ ++/* Fields of "I2C FIFO Configuration Register" */ ++/* 1 word */ ++#define I2C_FIFO_CFG_RXBS_RXBS0 0x00000000 ++/* Stop on Packet End Enable */ ++#define I2C_ADDR_CFG_SOPE_EN 0x00200000 ++/* Stop on Not Acknowledge Enable */ ++#define I2C_ADDR_CFG_SONA_EN 0x00100000 ++/* Enable */ ++#define I2C_ADDR_CFG_MnS_EN 0x00080000 ++ ++/* Fields of "I2C Interrupt Clear Register" */ ++/* Clear */ ++#define I2C_ICR_BREQ_INT_CLR 0x00000008 ++/* Clear */ ++#define I2C_ICR_LBREQ_INT_CLR 0x00000004 ++ ++/* Fields of "I2C Fractional Divider Configuration Register" */ ++/* field offset */ ++#define I2C_FDIV_CFG_DEC_OFFSET 0 ++ ++/* Fields of "I2C Bus Status Register" */ ++/* Bus Status */ ++#define I2C_BUS_STAT_BS_MASK 0x00000003 ++/* Read from I2C Bus. */ ++#define I2C_BUS_STAT_RNW_READ 0x00000004 ++/* I2C Bus is free. */ ++#define I2C_BUS_STAT_BS_FREE 0x00000000 ++/* ++ * The device is working as master and has claimed the control on the ++ * I2C-bus (busy master). ++ */ ++#define I2C_BUS_STAT_BS_BM 0x00000002 ++ ++/* Fields of "I2C RUN Control Register" */ ++/* Enable */ ++#define I2C_RUN_CTRL_RUN_EN 0x00000001 ++ ++/* Fields of "I2C End Data Control Register" */ ++/* ++ * Set End of Transmission ++ * Note:Do not write '1' to this bit when bus is free. This will cause an ++ * abort after the first byte when a new transfer is started. ++ */ ++#define I2C_ENDD_CTRL_SETEND 0x00000002 ++ ++/* Fields of "I2C Protocol Interrupt Request Source Status Register" */ ++/* NACK */ ++#define I2C_P_IRQSS_NACK 0x00000010 ++/* AL */ ++#define I2C_P_IRQSS_AL 0x00000008 ++/* RX */ ++#define I2C_P_IRQSS_RX 0x00000040 ++/* TX_END */ ++#define I2C_P_IRQSS_TX_END 0x00000020 ++ ++ ++#endif /* I2C_LANTIQ_H */ diff --git a/target/linux/lantiq/patches-5.15/0035-owrt-lantiq-wifi-and-ethernet-eeprom-handling.patch b/target/linux/lantiq/patches-5.15/0035-owrt-lantiq-wifi-and-ethernet-eeprom-handling.patch new file mode 100644 index 0000000000..c80045e878 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0035-owrt-lantiq-wifi-and-ethernet-eeprom-handling.patch @@ -0,0 +1,218 @@ +From f8c5db89e793a4bc6c1e87bd7b3a5cec16b75bc3 Mon Sep 17 00:00:00 2001 +From: John Crispin <blogic@openwrt.org> +Date: Wed, 10 Sep 2014 22:42:14 +0200 +Subject: [PATCH 35/36] owrt: lantiq: wifi and ethernet eeprom handling + +Signed-off-by: John Crispin <blogic@openwrt.org> +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 3 + + arch/mips/lantiq/xway/Makefile | 3 + + arch/mips/lantiq/xway/ath5k_eep.c | 136 +++++++++++++++++++++ + arch/mips/lantiq/xway/eth_mac.c | 25 ++++ + drivers/net/ethernet/lantiq_etop.c | 6 +- + 5 files changed, 172 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/lantiq/xway/ath5k_eep.c + create mode 100644 arch/mips/lantiq/xway/eth_mac.c + +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -102,5 +102,8 @@ int xrx200_gphy_boot(struct device *dev, + extern void ltq_pmu_enable(unsigned int module); + extern void ltq_pmu_disable(unsigned int module); + ++/* allow the ethernet driver to load a flash mapped mac addr */ ++const u8* ltq_get_eth_mac(void); ++ + #endif /* CONFIG_SOC_TYPE_XWAY */ + #endif /* _LTQ_XWAY_H__ */ +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -8,3 +8,6 @@ obj-y += timer.o + endif + + obj-y += vmmc.o ++ ++obj-y += eth_mac.o ++obj-$(CONFIG_PCI) += ath5k_eep.o +--- /dev/null ++++ b/arch/mips/lantiq/xway/ath5k_eep.c +@@ -0,0 +1,136 @@ ++/* ++ * Copyright (C) 2011 Luca Olivetti <luca@ventoso.org> ++ * Copyright (C) 2011 John Crispin <blogic@openwrt.org> ++ * Copyright (C) 2011 Andrej Vlašić <andrej.vlasic0@gmail.com> ++ * Copyright (C) 2013 Álvaro Fernández Rojas <noltari@gmail.com> ++ * Copyright (C) 2013 Daniel Gimpelevich <daniel@gimpelevich.san-francisco.ca.us> ++ * Copyright (C) 2015 Vittorio Gambaletta <openwrt@vittgam.net> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/etherdevice.h> ++#include <linux/ath5k_platform.h> ++#include <linux/pci.h> ++#include <linux/err.h> ++#include <linux/mtd/mtd.h> ++#include <lantiq_soc.h> ++ ++extern int (*ltq_pci_plat_dev_init)(struct pci_dev *dev); ++struct ath5k_platform_data ath5k_pdata; ++static u8 athxk_eeprom_mac[6]; ++ ++static int ath5k_pci_plat_dev_init(struct pci_dev *dev) ++{ ++ dev->dev.platform_data = &ath5k_pdata; ++ return 0; ++} ++ ++static int ath5k_eep_load; ++int __init of_ath5k_eeprom_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node, *mtd_np = NULL; ++ int mac_offset; ++ u32 mac_inc = 0; ++ int i; ++ struct mtd_info *the_mtd; ++ size_t flash_readlen; ++ const __be32 *list; ++ const char *part; ++ phandle phandle; ++ ++ list = of_get_property(np, "ath,eep-flash", &i); ++ if (!list || (i != (2 * sizeof(*list)))) ++ return -ENODEV; ++ ++ phandle = be32_to_cpup(list++); ++ if (phandle) ++ mtd_np = of_find_node_by_phandle(phandle); ++ ++ if (!mtd_np) ++ return -ENODEV; ++ ++ part = of_get_property(mtd_np, "label", NULL); ++ if (!part) ++ part = mtd_np->name; ++ ++ the_mtd = get_mtd_device_nm(part); ++ if (IS_ERR(the_mtd)) ++ return -ENODEV; ++ ++ ath5k_pdata.eeprom_data = kmalloc(ATH5K_PLAT_EEP_MAX_WORDS<<1, GFP_KERNEL); ++ ++ i = mtd_read(the_mtd, be32_to_cpup(list), ATH5K_PLAT_EEP_MAX_WORDS << 1, ++ &flash_readlen, (void *) ath5k_pdata.eeprom_data); ++ ++ if (!of_property_read_u32(np, "ath,mac-offset", &mac_offset)) { ++ size_t mac_readlen; ++ mtd_read(the_mtd, mac_offset, 6, &mac_readlen, ++ (void *) athxk_eeprom_mac); ++ } ++ put_mtd_device(the_mtd); ++ ++ if (((ATH5K_PLAT_EEP_MAX_WORDS<<1) != flash_readlen) || i) { ++ dev_err(&pdev->dev, "failed to load eeprom from mtd\n"); ++ return -ENODEV; ++ } ++ ++ if (of_find_property(np, "ath,eep-swap", NULL)) ++ for (i = 0; i < ATH5K_PLAT_EEP_MAX_WORDS; i++) ++ ath5k_pdata.eeprom_data[i] = swab16(ath5k_pdata.eeprom_data[i]); ++ ++ if (!is_valid_ether_addr(athxk_eeprom_mac) && ltq_get_eth_mac()) ++ ether_addr_copy(athxk_eeprom_mac, ltq_get_eth_mac()); ++ ++ if (!is_valid_ether_addr(athxk_eeprom_mac)) { ++ dev_warn(&pdev->dev, "using random mac\n"); ++ random_ether_addr(athxk_eeprom_mac); ++ } ++ ++ if (!of_property_read_u32(np, "ath,mac-increment", &mac_inc)) ++ athxk_eeprom_mac[5] += mac_inc; ++ ++ ath5k_pdata.macaddr = athxk_eeprom_mac; ++ ltq_pci_plat_dev_init = ath5k_pci_plat_dev_init; ++ ++ dev_info(&pdev->dev, "loaded ath5k eeprom\n"); ++ ++ return 0; ++} ++ ++static struct of_device_id ath5k_eeprom_ids[] = { ++ { .compatible = "ath5k,eeprom" }, ++ { } ++}; ++ ++static struct platform_driver ath5k_eeprom_driver = { ++ .driver = { ++ .name = "ath5k,eeprom", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(ath5k_eeprom_ids), ++ }, ++}; ++ ++static int __init of_ath5k_eeprom_init(void) ++{ ++ int ret = platform_driver_probe(&ath5k_eeprom_driver, of_ath5k_eeprom_probe); ++ ++ if (ret) ++ ath5k_eep_load = 1; ++ ++ return ret; ++} ++ ++static int __init of_ath5k_eeprom_init_late(void) ++{ ++ if (!ath5k_eep_load) ++ return 0; ++ ++ return platform_driver_probe(&ath5k_eeprom_driver, of_ath5k_eeprom_probe); ++} ++late_initcall(of_ath5k_eeprom_init_late); ++subsys_initcall(of_ath5k_eeprom_init); +--- /dev/null ++++ b/arch/mips/lantiq/xway/eth_mac.c +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2012 John Crispin <blogic@openwrt.org> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/init.h> ++#include <linux/if_ether.h> ++ ++static u8 eth_mac[6]; ++static int eth_mac_set; ++ ++const u8* ltq_get_eth_mac(void) ++{ ++ return eth_mac; ++} ++ ++static int __init setup_ethaddr(char *str) ++{ ++ eth_mac_set = mac_pton(str, eth_mac); ++ return !eth_mac_set; ++} ++early_param("ethaddr", setup_ethaddr); +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -757,7 +757,11 @@ ltq_etop_init(struct net_device *dev) + if (err) + goto err_hw; + +- memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr)); ++ memcpy(&mac.sa_data, ltq_get_eth_mac(), ETH_ALEN); ++ ++ if (priv->mac && !is_valid_ether_addr(mac.sa_data)) ++ memcpy(&mac.sa_data, priv->mac, ETH_ALEN); ++ + if (!is_valid_ether_addr(mac.sa_data)) { + pr_warn("etop: invalid MAC, using random\n"); + eth_random_addr(mac.sa_data); diff --git a/target/linux/lantiq/patches-5.15/0042-arch-mips-increase-io_space_limit.patch b/target/linux/lantiq/patches-5.15/0042-arch-mips-increase-io_space_limit.patch new file mode 100644 index 0000000000..c81222af57 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0042-arch-mips-increase-io_space_limit.patch @@ -0,0 +1,24 @@ +From 9807eb80a1b3bad7a4a89aa6566497bb1cadd6ef Mon Sep 17 00:00:00 2001 +From: John Crispin <john@phrozen.org> +Date: Fri, 3 Jun 2016 13:12:20 +0200 +Subject: [PATCH] arch: mips: increase io_space_limit + +this value comes from x86 and breaks some pci devices + +Signed-off-by: John Crispin <john@phrozen.org> +--- + arch/mips/include/asm/mach-lantiq/spaces.h | 8 ++++++++ + 1 file changed, 8 insertions(+) + create mode 100644 arch/mips/include/asm/mach-lantiq/spaces.h + +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/spaces.h +@@ -0,0 +1,8 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __ASM_MACH_LANTIQ_SPACES_H_ ++#define __ASM_MACH_LANTIQ_SPACES_H_ ++ ++#define IO_SPACE_LIMIT 0xffffffff ++ ++#include <asm/mach-generic/spaces.h> ++#endif diff --git a/target/linux/lantiq/patches-5.15/0050-USB-DWC2-make-the-lantiq-settings-match-vendor-drive.patch b/target/linux/lantiq/patches-5.15/0050-USB-DWC2-make-the-lantiq-settings-match-vendor-drive.patch new file mode 100644 index 0000000000..333a2e6377 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0050-USB-DWC2-make-the-lantiq-settings-match-vendor-drive.patch @@ -0,0 +1,78 @@ +From de2cad82c4d0872066f83ce59462603852b47f03 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Fri, 6 Jan 2017 17:55:24 +0100 +Subject: [PATCH 2/2] usb: dwc2: add support for other Lantiq SoCs + +The size of the internal RAM of the DesignWare USB controller changed +between the different Lantiq SoCs. We have the following sizes: + +Amazon + Danube: 8 KByte +Amazon SE + arx100: 2 KByte +xrx200 + xrx300: 2.5 KByte + +For Danube SoC we do not provide the params and let the driver decide +to use sane defaults, for the Amazon SE and arx100 we use small fifos +and for the xrx200 and xrx300 SCs a little bit bigger periodic fifo. +The auto detection of max_transfer_size and max_packet_count should +work, so remove it. + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + drivers/usb/dwc2/platform.c | 46 ++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 39 insertions(+), 7 deletions(-) + +--- a/drivers/usb/dwc2/params.c ++++ b/drivers/usb/dwc2/params.c +@@ -92,7 +92,14 @@ static void dwc2_set_rk_params(struct dw + p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + } + +-static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg) ++static void dwc2_set_ltq_danube_params(struct dwc2_hsotg *hsotg) ++{ ++ struct dwc2_core_params *p = &hsotg->params; ++ ++ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; ++} ++ ++static void dwc2_set_ltq_ase_params(struct dwc2_hsotg *hsotg) + { + struct dwc2_core_params *p = &hsotg->params; + +@@ -100,12 +107,20 @@ static void dwc2_set_ltq_params(struct d + p->host_rx_fifo_size = 288; + p->host_nperio_tx_fifo_size = 128; + p->host_perio_tx_fifo_size = 96; +- p->max_transfer_size = 65535; +- p->max_packet_count = 511; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << + GAHBCFG_HBSTLEN_SHIFT; + } + ++static void dwc2_set_ltq_xrx200_params(struct dwc2_hsotg *hsotg) ++{ ++ struct dwc2_core_params *p = &hsotg->params; ++ ++ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; ++ p->host_rx_fifo_size = 288; ++ p->host_nperio_tx_fifo_size = 128; ++ p->host_perio_tx_fifo_size = 136; ++} ++ + static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg) + { + struct dwc2_core_params *p = &hsotg->params; +@@ -196,8 +211,11 @@ const struct of_device_id dwc2_of_match_ + { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, + { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, + { .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params }, +- { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, +- { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, ++ { .compatible = "lantiq,danube-usb", .data = &dwc2_set_ltq_danube_params }, ++ { .compatible = "lantiq,ase-usb", .data = &dwc2_set_ltq_ase_params }, ++ { .compatible = "lantiq,arx100-usb", .data = &dwc2_set_ltq_ase_params }, ++ { .compatible = "lantiq,xrx200-usb", .data = &dwc2_set_ltq_xrx200_params }, ++ { .compatible = "lantiq,xrx300-usb", .data = &dwc2_set_ltq_xrx200_params }, + { .compatible = "snps,dwc2" }, + { .compatible = "samsung,s3c6400-hsotg", + .data = dwc2_set_s3c6400_params }, diff --git a/target/linux/lantiq/patches-5.15/0051-MIPS-lantiq-improve-USB-initialization.patch b/target/linux/lantiq/patches-5.15/0051-MIPS-lantiq-improve-USB-initialization.patch new file mode 100644 index 0000000000..9d62892b56 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0051-MIPS-lantiq-improve-USB-initialization.patch @@ -0,0 +1,49 @@ +From 14909c4e4e836925668e74fc6e0e85ba0283cbf9 Mon Sep 17 00:00:00 2001 +From: Hauke Mehrtens <hauke@hauke-m.de> +Date: Fri, 6 Jan 2017 17:40:12 +0100 +Subject: [PATCH 2/2] MIPS: lantiq: improve USB initialization + +This adds code to initialize the USB controller and PHY also on Danube, +Amazon SE and AR10. This code is based on the Vendor driver from +different UGW versions and compared to the hardware documentation. + +Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +--- + arch/mips/lantiq/xway/sysctrl.c | 20 +++++++ + 2 files changed, 110 insertions(+), 30 deletions(-) + + +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -248,6 +248,25 @@ static void pmu_disable(struct clk *clk) + pr_warn("deactivating PMU module failed!"); + } + ++static void usb_set_clock(void) ++{ ++ unsigned int val = ltq_cgu_r32(ifccr); ++ ++ if (of_machine_is_compatible("lantiq,ar10") || ++ of_machine_is_compatible("lantiq,grx390")) { ++ val &= ~0x03; /* XTAL divided by 3 */ ++ } else if (of_machine_is_compatible("lantiq,ar9") || ++ of_machine_is_compatible("lantiq,vr9")) { ++ /* TODO: this depends on the XTAL frequency */ ++ val |= 0x03; /* XTAL divided by 3 */ ++ } else if (of_machine_is_compatible("lantiq,ase")) { ++ val |= 0x20; /* from XTAL */ ++ } else if (of_machine_is_compatible("lantiq,danube")) { ++ val |= 0x30; /* 12 MHz, generated from 36 MHz */ ++ } ++ ltq_cgu_w32(val, ifccr); ++} ++ + /* the pci enable helper */ + static int pci_enable(struct clk *clk) + { +@@ -585,4 +604,5 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("1e116000.mei", "dfe", 1, 0, PMU_DFE); + clkdev_add_pmu("1e100400.serial", NULL, 1, 0, PMU_ASC0); + } ++ usb_set_clock(); + } diff --git a/target/linux/lantiq/patches-5.15/0101-find_active_root.patch b/target/linux/lantiq/patches-5.15/0101-find_active_root.patch new file mode 100644 index 0000000000..925ac9dbba --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0101-find_active_root.patch @@ -0,0 +1,93 @@ +--- a/drivers/mtd/parsers/ofpart_core.c ++++ b/drivers/mtd/parsers/ofpart_core.c +@@ -38,6 +38,38 @@ static bool node_has_compatible(struct d + return of_get_property(pp, "compatible", NULL); + } + ++static uint8_t * brnboot_get_selected_root_part(struct mtd_info *master, ++ loff_t offset) ++{ ++ static uint8_t root_id; ++ int err, len; ++ ++ err = mtd_read(master, offset, 0x01, &len, &root_id); ++ ++ if (mtd_is_bitflip(err) || !err) ++ return &root_id; ++ ++ return NULL; ++} ++ ++static void brnboot_set_active_root_part(struct mtd_partition *pparts, ++ struct device_node **part_nodes, ++ int nr_parts, ++ uint8_t *root_id) ++{ ++ int i; ++ ++ for (i = 0; i < nr_parts; i++) { ++ int part_root_id; ++ ++ if (!of_property_read_u32(part_nodes[i], "brnboot,root-id", &part_root_id) ++ && part_root_id == *root_id) { ++ pparts[i].name = "firmware"; ++ break; ++ } ++ } ++} ++ + static int parse_fixed_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +@@ -51,6 +83,8 @@ static int parse_fixed_partitions(struct + struct device_node *pp; + int nr_parts, i, ret = 0; + bool dedicated = true; ++ uint8_t *proot_id = NULL; ++ struct device_node **part_nodes; + + /* Pull of_node from the master device node */ + mtd_node = mtd_get_of_node(master); +@@ -95,7 +129,9 @@ static int parse_fixed_partitions(struct + return 0; + + parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); +- if (!parts) ++ part_nodes = kcalloc(nr_parts, sizeof(*part_nodes), GFP_KERNEL); ++ ++ if (!parts || !part_nodes) + return -ENOMEM; + + i = 0; +@@ -147,6 +183,11 @@ static int parse_fixed_partitions(struct + if (of_property_read_bool(pp, "slc-mode")) + parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; + ++ if (!proot_id && of_device_is_compatible(pp, "brnboot,root-selector")) ++ proot_id = brnboot_get_selected_root_part(master, parts[i].offset); ++ ++ part_nodes[i] = pp; ++ + i++; + } + +@@ -156,6 +197,11 @@ static int parse_fixed_partitions(struct + if (quirks && quirks->post_parse) + quirks->post_parse(master, parts, nr_parts); + ++ if (proot_id) ++ brnboot_set_active_root_part(parts, part_nodes, nr_parts, proot_id); ++ ++ kfree(part_nodes); ++ + *pparts = parts; + return nr_parts; + +@@ -166,6 +212,7 @@ ofpart_fail: + ofpart_none: + of_node_put(pp); + kfree(parts); ++ kfree(part_nodes); + return ret; + } + diff --git a/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch b/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch new file mode 100644 index 0000000000..10633199e6 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0151-lantiq-ifxmips_pcie-use-of.patch @@ -0,0 +1,387 @@ +--- a/arch/mips/pci/ifxmips_pcie.c ++++ b/arch/mips/pci/ifxmips_pcie.c +@@ -16,8 +16,15 @@ + #include <asm/paccess.h> + #include <linux/pci.h> + #include <linux/pci_regs.h> ++#include <linux/phy/phy.h> ++#include <linux/regmap.h> ++#include <linux/reset.h> ++#include <linux/mfd/syscon.h> + #include <linux/module.h> + ++#include <linux/of_gpio.h> ++#include <linux/of_platform.h> ++ + #include "ifxmips_pcie.h" + #include "ifxmips_pcie_reg.h" + +@@ -40,6 +47,10 @@ + static DEFINE_SPINLOCK(ifx_pcie_lock); + + u32 g_pcie_debug_flag = PCIE_MSG_ANY & (~PCIE_MSG_CFG); ++static int pcie_reset_gpio; ++static struct phy *ltq_pcie_phy; ++static struct reset_control *ltq_pcie_reset; ++static struct regmap *ltq_rcu_regmap; + + static ifx_pcie_irq_t pcie_irqs[IFX_PCIE_CORE_NR] = { + { +@@ -82,6 +93,22 @@ void ifx_pcie_debug(const char *fmt, ... + printk("%s", buf); + } + ++static inline void pcie_ep_gpio_rst_init(int pcie_port) ++{ ++ gpio_direction_output(pcie_reset_gpio, 1); ++ gpio_set_value(pcie_reset_gpio, 1); ++} ++ ++static inline void pcie_device_rst_assert(int pcie_port) ++{ ++ gpio_set_value(pcie_reset_gpio, 0); ++} ++ ++static inline void pcie_device_rst_deassert(int pcie_port) ++{ ++ mdelay(100); ++ gpio_direction_output(pcie_reset_gpio, 1); ++} + + static inline int pcie_ltssm_enable(int pcie_port) + { +@@ -988,10 +1015,22 @@ int ifx_pcie_bios_plat_dev_init(struct + static int + pcie_rc_initialize(int pcie_port) + { +- int i; ++ int i, ret; + #define IFX_PCIE_PHY_LOOP_CNT 5 + +- pcie_rcu_endian_setup(pcie_port); ++ regmap_update_bits(ltq_rcu_regmap, 0x4c, IFX_RCU_AHB_BE_PCIE_M, ++ IFX_RCU_AHB_BE_PCIE_M); ++ ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ regmap_update_bits(ltq_rcu_regmap, 0x4c, IFX_RCU_AHB_BE_PCIE_S, ++ IFX_RCU_AHB_BE_PCIE_S); ++#else ++ regmap_update_bits(ltq_rcu_regmap, 0x4c, IFX_RCU_AHB_BE_PCIE_S, ++ 0x0); ++#endif ++ ++ regmap_update_bits(ltq_rcu_regmap, 0x4c, IFX_RCU_AHB_BE_XBAR_M, ++ 0x0); + + pcie_ep_gpio_rst_init(pcie_port); + +@@ -1000,26 +1039,21 @@ pcie_rc_initialize(int pcie_port) + * reset PCIe PHY will solve this issue + */ + for (i = 0; i < IFX_PCIE_PHY_LOOP_CNT; i++) { +- /* Disable PCIe PHY Analog part for sanity check */ +- pcie_phy_pmu_disable(pcie_port); +- +- pcie_phy_rst_assert(pcie_port); +- pcie_phy_rst_deassert(pcie_port); +- +- /* Make sure PHY PLL is stable */ +- udelay(20); +- +- /* PCIe Core reset enabled, low active, sw programmed */ +- pcie_core_rst_assert(pcie_port); ++ ret = phy_init(ltq_pcie_phy); ++ if (ret) ++ continue; + + /* Put PCIe EP in reset status */ + pcie_device_rst_assert(pcie_port); + +- /* PCI PHY & Core reset disabled, high active, sw programmed */ +- pcie_core_rst_deassert(pcie_port); ++ udelay(1); ++ reset_control_deassert(ltq_pcie_reset); + +- /* Already in a quiet state, program PLL, enable PHY, check ready bit */ +- pcie_phy_clock_mode_setup(pcie_port); ++ ret = phy_power_on(ltq_pcie_phy); ++ if (ret) { ++ phy_exit(ltq_pcie_phy); ++ continue; ++ } + + /* Enable PCIe PHY and Clock */ + pcie_core_pmu_setup(pcie_port); +@@ -1035,6 +1069,10 @@ pcie_rc_initialize(int pcie_port) + /* Once link is up, break out */ + if (pcie_app_loigc_setup(pcie_port) == 0) + break; ++ ++ phy_power_off(ltq_pcie_phy); ++ reset_control_assert(ltq_pcie_reset); ++ phy_exit(ltq_pcie_phy); + } + if (i >= IFX_PCIE_PHY_LOOP_CNT) { + printk(KERN_ERR "%s link up failed!!!!!\n", __func__); +@@ -1045,17 +1083,67 @@ pcie_rc_initialize(int pcie_port) + return 0; + } + +-static int __init ifx_pcie_bios_init(void) ++static int ifx_pcie_bios_probe(struct platform_device *pdev) + { ++ struct device_node *node = pdev->dev.of_node; + void __iomem *io_map_base; + int pcie_port; + int startup_port; ++ struct device_node *np; ++ struct pci_bus *bus; ++ ++ /* ++ * In case a PCI device is physical present, the Lantiq PCI driver need ++ * to be loaded prior to the Lantiq PCIe driver. Otherwise none of them ++ * will work. ++ * ++ * In case the lantiq PCI driver is enabled in the device tree, check if ++ * a PCI bus (hopefully the one of the Lantiq PCI driver one) is already ++ * registered. ++ * ++ * It will fail if there is another PCI controller, this controller is ++ * registered before the Lantiq PCIe driver is probe and the lantiq PCI ++ */ ++ np = of_find_compatible_node(NULL, NULL, "lantiq,pci-xway"); ++ ++ if (of_device_is_available(np)) { ++ bus = pci_find_next_bus(bus); ++ ++ if (!bus) ++ return -EPROBE_DEFER; ++ } + + /* Enable AHB Master/ Slave */ + pcie_ahb_pmu_setup(); + + startup_port = IFX_PCIE_PORT0; +- ++ ++ ltq_pcie_phy = devm_phy_get(&pdev->dev, "pcie"); ++ if (IS_ERR(ltq_pcie_phy)) { ++ dev_err(&pdev->dev, "failed to get the PCIe PHY\n"); ++ return PTR_ERR(ltq_pcie_phy); ++ } ++ ++ ltq_pcie_reset = devm_reset_control_get_shared(&pdev->dev, NULL); ++ if (IS_ERR(ltq_pcie_reset)) { ++ dev_err(&pdev->dev, "failed to get the PCIe reset line\n"); ++ return PTR_ERR(ltq_pcie_reset); ++ } ++ ++ ltq_rcu_regmap = syscon_regmap_lookup_by_phandle(node, "lantiq,rcu"); ++ if (IS_ERR(ltq_rcu_regmap)) ++ return PTR_ERR(ltq_rcu_regmap); ++ ++ pcie_reset_gpio = of_get_named_gpio(node, "gpio-reset", 0); ++ if (gpio_is_valid(pcie_reset_gpio)) { ++ int ret = devm_gpio_request(&pdev->dev, pcie_reset_gpio, "pcie-reset"); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to request gpio %d\n", pcie_reset_gpio); ++ return ret; ++ } ++ gpio_direction_output(pcie_reset_gpio, 1); ++ } ++ + for (pcie_port = startup_port; pcie_port < IFX_PCIE_CORE_NR; pcie_port++){ + if (pcie_rc_initialize(pcie_port) == 0) { + IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s: ifx_pcie_cfg_base 0x%p\n", +@@ -1067,6 +1155,7 @@ static int __init ifx_pcie_bios_init(voi + return -ENOMEM; + } + ifx_pcie_controller[pcie_port].pcic.io_map_base = (unsigned long)io_map_base; ++ pci_load_of_ranges(&ifx_pcie_controller[pcie_port].pcic, node); + + register_pci_controller(&ifx_pcie_controller[pcie_port].pcic); + /* XXX, clear error status */ +@@ -1083,6 +1172,30 @@ static int __init ifx_pcie_bios_init(voi + + return 0; + } ++ ++static const struct of_device_id ifxmips_pcie_match[] = { ++ { .compatible = "lantiq,pcie-xrx200" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ifxmips_pcie_match); ++ ++static struct platform_driver ltq_pci_driver = { ++ .probe = ifx_pcie_bios_probe, ++ .driver = { ++ .name = "pcie-xrx200", ++ .owner = THIS_MODULE, ++ .of_match_table = ifxmips_pcie_match, ++ }, ++}; ++ ++int __init ifx_pcie_bios_init(void) ++{ ++ int ret = platform_driver_register(<q_pci_driver); ++ if (ret) ++ pr_info("pcie-xrx200: Error registering platform driver!"); ++ return ret; ++} ++ + arch_initcall(ifx_pcie_bios_init); + + MODULE_LICENSE("GPL"); +--- a/arch/mips/pci/ifxmips_pcie_vr9.h ++++ b/arch/mips/pci/ifxmips_pcie_vr9.h +@@ -22,8 +22,6 @@ + #include <linux/gpio.h> + #include <lantiq_soc.h> + +-#define IFX_PCIE_GPIO_RESET 494 +- + #define IFX_REG_R32 ltq_r32 + #define IFX_REG_W32 ltq_w32 + #define CONFIG_IFX_PCIE_HW_SWAP +@@ -53,21 +51,6 @@ + #define OUT ((volatile u32*)(IFX_GPIO + 0x0070)) + + +-static inline void pcie_ep_gpio_rst_init(int pcie_port) +-{ +- +- gpio_request(IFX_PCIE_GPIO_RESET, "pcie-reset"); +- gpio_direction_output(IFX_PCIE_GPIO_RESET, 1); +- gpio_set_value(IFX_PCIE_GPIO_RESET, 1); +- +-/* ifx_gpio_pin_reserve(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +- ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +- ifx_gpio_dir_out_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +- ifx_gpio_altsel0_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +- ifx_gpio_altsel1_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +- ifx_gpio_open_drain_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id);*/ +-} +- + static inline void pcie_ahb_pmu_setup(void) + { + /* Enable AHB bus master/slave */ +@@ -79,24 +62,6 @@ static inline void pcie_ahb_pmu_setup(vo + //AHBS_PMU_SETUP(IFX_PMU_ENABLE); + } + +-static inline void pcie_rcu_endian_setup(int pcie_port) +-{ +- u32 reg; +- +- reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); +-#ifdef CONFIG_IFX_PCIE_HW_SWAP +- reg |= IFX_RCU_AHB_BE_PCIE_M; +- reg |= IFX_RCU_AHB_BE_PCIE_S; +- reg &= ~IFX_RCU_AHB_BE_XBAR_M; +-#else +- reg |= IFX_RCU_AHB_BE_PCIE_M; +- reg &= ~IFX_RCU_AHB_BE_PCIE_S; +- reg &= ~IFX_RCU_AHB_BE_XBAR_M; +-#endif /* CONFIG_IFX_PCIE_HW_SWAP */ +- IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); +- IFX_PCIE_PRINT(PCIE_MSG_REG, "%s IFX_RCU_AHB_ENDIAN: 0x%08x\n", __func__, IFX_REG_R32(IFX_RCU_AHB_ENDIAN)); +-} +- + static inline void pcie_phy_pmu_enable(int pcie_port) + { + struct clk *clk; +@@ -115,17 +80,6 @@ static inline void pcie_phy_pmu_disable( + // PCIE_PHY_PMU_SETUP(IFX_PMU_DISABLE); + } + +-static inline void pcie_pdi_big_endian(int pcie_port) +-{ +- u32 reg; +- +- /* SRAM2PDI endianness control. */ +- reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); +- /* Config AHB->PCIe and PDI endianness */ +- reg |= IFX_RCU_AHB_BE_PCIE_PDI; +- IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); +-} +- + static inline void pcie_pdi_pmu_enable(int pcie_port) + { + /* Enable PDI to access PCIe PHY register */ +@@ -135,65 +89,6 @@ static inline void pcie_pdi_pmu_enable(i + //PDI_PMU_SETUP(IFX_PMU_ENABLE); + } + +-static inline void pcie_core_rst_assert(int pcie_port) +-{ +- u32 reg; +- +- reg = IFX_REG_R32(IFX_RCU_RST_REQ); +- +- /* Reset PCIe PHY & Core, bit 22, bit 26 may be affected if write it directly */ +- reg |= 0x00400000; +- IFX_REG_W32(reg, IFX_RCU_RST_REQ); +-} +- +-static inline void pcie_core_rst_deassert(int pcie_port) +-{ +- u32 reg; +- +- /* Make sure one micro-second delay */ +- udelay(1); +- +- /* Reset PCIe PHY & Core, bit 22 */ +- reg = IFX_REG_R32(IFX_RCU_RST_REQ); +- reg &= ~0x00400000; +- IFX_REG_W32(reg, IFX_RCU_RST_REQ); +-} +- +-static inline void pcie_phy_rst_assert(int pcie_port) +-{ +- u32 reg; +- +- reg = IFX_REG_R32(IFX_RCU_RST_REQ); +- reg |= 0x00001000; /* Bit 12 */ +- IFX_REG_W32(reg, IFX_RCU_RST_REQ); +-} +- +-static inline void pcie_phy_rst_deassert(int pcie_port) +-{ +- u32 reg; +- +- /* Make sure one micro-second delay */ +- udelay(1); +- +- reg = IFX_REG_R32(IFX_RCU_RST_REQ); +- reg &= ~0x00001000; /* Bit 12 */ +- IFX_REG_W32(reg, IFX_RCU_RST_REQ); +-} +- +-static inline void pcie_device_rst_assert(int pcie_port) +-{ +- gpio_set_value(IFX_PCIE_GPIO_RESET, 0); +-// ifx_gpio_output_clear(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +-} +- +-static inline void pcie_device_rst_deassert(int pcie_port) +-{ +- mdelay(100); +- gpio_direction_output(IFX_PCIE_GPIO_RESET, 1); +-// gpio_set_value(IFX_PCIE_GPIO_RESET, 1); +- //ifx_gpio_output_set(IFX_PCIE_GPIO_RESET, ifx_pcie_gpio_module_id); +-} +- + static inline void pcie_core_pmu_setup(int pcie_port) + { + struct clk *clk; +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -43,7 +43,7 @@ obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o + obj-$(CONFIG_SOC_MT7620) += pci-mt7620.o + obj-$(CONFIG_SOC_RT288X) += pci-rt2880.o + obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o +-obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie_phy.o ifxmips_pcie.o fixup-lantiq-pcie.o ++obj-$(CONFIG_PCIE_LANTIQ) += ifxmips_pcie.o fixup-lantiq-pcie.o + obj-$(CONFIG_PCIE_LANTIQ_MSI) += pcie-lantiq-msi.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o diff --git a/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch b/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch new file mode 100644 index 0000000000..71e9886d2e --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0152-lantiq-VPE.patch @@ -0,0 +1,180 @@ +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -2434,6 +2434,12 @@ config MIPS_VPE_LOADER + Includes a loader for loading an elf relocatable object + onto another VPE and running it. + ++config IFX_VPE_EXT ++ bool "IFX APRP Extensions" ++ depends on MIPS_VPE_LOADER ++ help ++ IFX included extensions in APRP ++ + config MIPS_VPE_LOADER_CMP + bool + default "y" +--- a/arch/mips/include/asm/vpe.h ++++ b/arch/mips/include/asm/vpe.h +@@ -127,4 +127,13 @@ void cleanup_tc(struct tc *tc); + + int __init vpe_module_init(void); + void __exit vpe_module_exit(void); ++ ++/* For the explanation of the APIs please refer the section "MT APRP Kernel ++ * Programming" in AR9 SW Architecture Specification ++ */ ++int32_t vpe1_sw_start(void *sw_start_addr, uint32_t tcmask, uint32_t flags); ++int32_t vpe1_sw_stop(uint32_t flags); ++uint32_t vpe1_get_load_addr(uint32_t flags); ++uint32_t vpe1_get_max_mem(uint32_t flags); ++ + #endif /* _ASM_VPE_H */ +--- a/arch/mips/kernel/vpe-mt.c ++++ b/arch/mips/kernel/vpe-mt.c +@@ -29,6 +29,7 @@ int vpe_run(struct vpe *v) + struct vpe_notifications *notifier; + unsigned int vpeflags; + struct tc *t; ++ unsigned long physical_memsize = 0L; + + /* check we are the Master VPE */ + local_irq_save(flags); +@@ -417,6 +418,8 @@ int __init vpe_module_init(void) + } + + v->ntcs = hw_tcs - aprp_cpu_index(); ++ write_tc_c0_tcbind((read_tc_c0_tcbind() & ++ ~TCBIND_CURVPE) | 1); + + /* add the tc to the list of this vpe's tc's. */ + list_add(&t->tc, &v->tc); +@@ -519,3 +522,47 @@ void __exit vpe_module_exit(void) + release_vpe(v); + } + } ++ ++#ifdef CONFIG_IFX_VPE_EXT ++int32_t vpe1_sw_start(void *sw_start_addr, uint32_t tcmask, uint32_t flags) ++{ ++ enum vpe_state state; ++ struct vpe *v = get_vpe(tclimit); ++ struct vpe_notifications *not; ++ ++ if (tcmask || flags) { ++ pr_warn("Currently tcmask and flags should be 0. Other values are not supported\n"); ++ return -1; ++ } ++ ++ state = xchg(&v->state, VPE_STATE_INUSE); ++ if (state != VPE_STATE_UNUSED) { ++ vpe_stop(v); ++ ++ list_for_each_entry(not, &v->notify, list) { ++ not->stop(tclimit); ++ } ++ } ++ ++ v->__start = (unsigned long)sw_start_addr; ++ ++ if (!vpe_run(v)) { ++ pr_debug("VPE loader: VPE1 running successfully\n"); ++ return 0; ++ } ++ return -1; ++} ++EXPORT_SYMBOL(vpe1_sw_start); ++ ++int32_t vpe1_sw_stop(uint32_t flags) ++{ ++ struct vpe *v = get_vpe(tclimit); ++ ++ if (!vpe_free(v)) { ++ pr_debug("RP Stopped\n"); ++ return 0; ++ } else ++ return -1; ++} ++EXPORT_SYMBOL(vpe1_sw_stop); ++#endif +--- a/arch/mips/kernel/vpe.c ++++ b/arch/mips/kernel/vpe.c +@@ -49,6 +49,41 @@ struct vpe_control vpecontrol = { + .tc_list = LIST_HEAD_INIT(vpecontrol.tc_list) + }; + ++#ifdef CONFIG_IFX_VPE_EXT ++unsigned int vpe1_load_addr; ++ ++static int __init load_address(char *str) ++{ ++ get_option(&str, &vpe1_load_addr); ++ return 1; ++} ++__setup("vpe1_load_addr=", load_address); ++ ++static unsigned int vpe1_mem; ++static int __init vpe1mem(char *str) ++{ ++ vpe1_mem = memparse(str, &str); ++ return 1; ++} ++__setup("vpe1_mem=", vpe1mem); ++ ++uint32_t vpe1_get_load_addr(uint32_t flags) ++{ ++ return vpe1_load_addr; ++} ++EXPORT_SYMBOL(vpe1_get_load_addr); ++ ++uint32_t vpe1_get_max_mem(uint32_t flags) ++{ ++ if (!vpe1_mem) ++ return P_SIZE; ++ else ++ return vpe1_mem; ++} ++EXPORT_SYMBOL(vpe1_get_max_mem); ++ ++#endif ++ + /* get the vpe associated with this minor */ + struct vpe *get_vpe(int minor) + { +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -34,10 +34,14 @@ unsigned long physical_memsize = 0L; + */ + static struct ltq_soc_info soc_info; + ++/* for Multithreading (APRP), vpe.c will use it */ ++unsigned long cp0_memsize; ++ + const char *get_system_type(void) + { + return soc_info.sys_type; + } ++EXPORT_SYMBOL(ltq_soc_type); + + int ltq_soc_type(void) + { +--- a/arch/mips/include/asm/mipsmtregs.h ++++ b/arch/mips/include/asm/mipsmtregs.h +@@ -32,6 +32,9 @@ + #define read_c0_vpeconf1() __read_32bit_c0_register($1, 3) + #define write_c0_vpeconf1(val) __write_32bit_c0_register($1, 3, val) + ++#define read_c0_vpeopt() __read_32bit_c0_register($1, 7) ++#define write_c0_vpeopt(val) __write_32bit_c0_register($1, 7, val) ++ + #define read_c0_tcstatus() __read_32bit_c0_register($2, 1) + #define write_c0_tcstatus(val) __write_32bit_c0_register($2, 1, val) + +@@ -378,6 +381,8 @@ do { \ + #define write_vpe_c0_vpeconf0(val) mttc0(1, 2, val) + #define read_vpe_c0_vpeconf1() mftc0(1, 3) + #define write_vpe_c0_vpeconf1(val) mttc0(1, 3, val) ++#define read_vpe_c0_vpeopt() mftc0(1, 7) ++#define write_vpe_c0_vpeopt(val) mttc0(1, 7, val) + #define read_vpe_c0_count() mftc0(9, 0) + #define write_vpe_c0_count(val) mttc0(9, 0, val) + #define read_vpe_c0_status() mftc0(12, 0) diff --git a/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch b/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch new file mode 100644 index 0000000000..d6556d115d --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0154-lantiq-pci-bar11mask-fix.patch @@ -0,0 +1,22 @@ +--- a/arch/mips/pci/pci-lantiq.c ++++ b/arch/mips/pci/pci-lantiq.c +@@ -59,6 +59,8 @@ + #define ltq_pci_cfg_w32(x, y) ltq_w32((x), ltq_pci_mapped_cfg + (y)) + #define ltq_pci_cfg_r32(x) ltq_r32(ltq_pci_mapped_cfg + (x)) + ++extern u32 max_low_pfn; ++ + __iomem void *ltq_pci_mapped_cfg; + static __iomem void *ltq_pci_membase; + +@@ -84,8 +86,8 @@ static inline u32 ltq_calc_bar11mask(voi + u32 mem, bar11mask; + + /* BAR11MASK value depends on available memory on system. */ +- mem = get_num_physpages() * PAGE_SIZE; +- bar11mask = (0x0ffffff0 & ~((1 << (fls(mem) - 1)) - 1)) | 8; ++ mem = max_low_pfn << PAGE_SHIFT; ++ bar11mask = ((-roundup_pow_of_two(mem)) & 0x0F000000) | 8; + + return bar11mask; + } diff --git a/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch b/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch new file mode 100644 index 0000000000..898c2d4821 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0155-lantiq-VPE-nosmp.patch @@ -0,0 +1,14 @@ +--- a/arch/mips/kernel/vpe-mt.c ++++ b/arch/mips/kernel/vpe-mt.c +@@ -132,7 +132,10 @@ int vpe_run(struct vpe *v) + * kernels need to turn it on, even if that wasn't the pre-dvpe() state. + */ + #ifdef CONFIG_SMP +- evpe(vpeflags); ++ if (!setup_max_cpus) /* nosmp is set */ ++ evpe(EVPE_ENABLE); ++ else ++ evpe(vpeflags); + #else + evpe(EVPE_ENABLE); + #endif diff --git a/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch b/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch new file mode 100644 index 0000000000..796220b2bc --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0160-owrt-lantiq-multiple-flash.patch @@ -0,0 +1,220 @@ +--- a/drivers/mtd/maps/lantiq-flash.c ++++ b/drivers/mtd/maps/lantiq-flash.c +@@ -17,6 +17,7 @@ + #include <linux/mtd/cfi.h> + #include <linux/platform_device.h> + #include <linux/mtd/physmap.h> ++#include <linux/mtd/concat.h> + #include <linux/of.h> + + #include <lantiq_soc.h> +@@ -36,13 +37,16 @@ enum { + LTQ_NOR_NORMAL + }; + ++#define MAX_RESOURCES 4 ++ + struct ltq_mtd { +- struct resource *res; +- struct mtd_info *mtd; +- struct map_info *map; ++ struct mtd_info *mtd[MAX_RESOURCES]; ++ struct mtd_info *cmtd; ++ struct map_info map[MAX_RESOURCES]; + }; + + static const char ltq_map_name[] = "ltq_nor"; ++static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL }; + + static map_word + ltq_read16(struct map_info *map, unsigned long adr) +@@ -106,11 +110,43 @@ ltq_copy_to(struct map_info *map, unsign + } + + static int ++ltq_mtd_remove(struct platform_device *pdev) ++{ ++ struct ltq_mtd *ltq_mtd = platform_get_drvdata(pdev); ++ int i; ++ ++ if (ltq_mtd == NULL) ++ return 0; ++ ++ if (ltq_mtd->cmtd) { ++ mtd_device_unregister(ltq_mtd->cmtd); ++ if (ltq_mtd->cmtd != ltq_mtd->mtd[0]) ++ mtd_concat_destroy(ltq_mtd->cmtd); ++ } ++ ++ for (i = 0; i < MAX_RESOURCES; i++) { ++ if (ltq_mtd->mtd[i] != NULL) ++ map_destroy(ltq_mtd->mtd[i]); ++ } ++ ++ kfree(ltq_mtd); ++ ++ return 0; ++} ++ ++static int + ltq_mtd_probe(struct platform_device *pdev) + { + struct ltq_mtd *ltq_mtd; + struct cfi_private *cfi; +- int err; ++ int err = 0; ++ int i; ++ int devices_found = 0; ++ ++ static const char *rom_probe_types[] = { ++ "cfi_probe", "jedec_probe", NULL ++ }; ++ const char **type; + + ltq_mtd = devm_kzalloc(&pdev->dev, sizeof(struct ltq_mtd), GFP_KERNEL); + if (!ltq_mtd) +@@ -118,75 +154,89 @@ ltq_mtd_probe(struct platform_device *pd + + platform_set_drvdata(pdev, ltq_mtd); + +- ltq_mtd->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!ltq_mtd->res) { +- dev_err(&pdev->dev, "failed to get memory resource\n"); +- return -ENOENT; ++ for (i = 0; i < pdev->num_resources; i++) { ++ printk(KERN_NOTICE "lantiq nor flash device: %.8llx at %.8llx\n", ++ (unsigned long long)resource_size(&pdev->resource[i]), ++ (unsigned long long)pdev->resource[i].start); ++ ++ if (!devm_request_mem_region(&pdev->dev, ++ pdev->resource[i].start, ++ resource_size(&pdev->resource[i]), ++ dev_name(&pdev->dev))) { ++ dev_err(&pdev->dev, "Could not reserve memory region\n"); ++ return -ENOMEM; ++ } ++ ++ ltq_mtd->map[i].name = ltq_map_name; ++ ltq_mtd->map[i].bankwidth = 2; ++ ltq_mtd->map[i].read = ltq_read16; ++ ltq_mtd->map[i].write = ltq_write16; ++ ltq_mtd->map[i].copy_from = ltq_copy_from; ++ ltq_mtd->map[i].copy_to = ltq_copy_to; ++ ++ if (of_find_property(pdev->dev.of_node, "lantiq,noxip", NULL)) ++ ltq_mtd->map[i].phys = NO_XIP; ++ else ++ ltq_mtd->map[i].phys = pdev->resource[i].start; ++ ltq_mtd->map[i].size = resource_size(&pdev->resource[i]); ++ ltq_mtd->map[i].virt = devm_ioremap(&pdev->dev, pdev->resource[i].start, ++ ltq_mtd->map[i].size); ++ if (IS_ERR(ltq_mtd->map[i].virt)) ++ return PTR_ERR(ltq_mtd->map[i].virt); ++ ++ if (ltq_mtd->map[i].virt == NULL) { ++ dev_err(&pdev->dev, "Failed to ioremap flash region\n"); ++ err = PTR_ERR(ltq_mtd->map[i].virt); ++ goto err_out; ++ } ++ ++ ltq_mtd->map[i].map_priv_1 = LTQ_NOR_PROBING; ++ for (type = rom_probe_types; !ltq_mtd->mtd[i] && *type; type++) ++ ltq_mtd->mtd[i] = do_map_probe(*type, <q_mtd->map[i]); ++ ltq_mtd->map[i].map_priv_1 = LTQ_NOR_NORMAL; ++ ++ if (!ltq_mtd->mtd[i]) { ++ dev_err(&pdev->dev, "probing failed\n"); ++ return -ENXIO; ++ } else { ++ devices_found++; ++ } ++ ++ ltq_mtd->mtd[i]->owner = THIS_MODULE; ++ ltq_mtd->mtd[i]->dev.parent = &pdev->dev; ++ ++ cfi = ltq_mtd->map[i].fldrv_priv; ++ cfi->addr_unlock1 ^= 1; ++ cfi->addr_unlock2 ^= 1; + } + +- ltq_mtd->map = devm_kzalloc(&pdev->dev, sizeof(struct map_info), +- GFP_KERNEL); +- if (!ltq_mtd->map) +- return -ENOMEM; +- +- if (of_find_property(pdev->dev.of_node, "lantiq,noxip", NULL)) +- ltq_mtd->map->phys = NO_XIP; +- else +- ltq_mtd->map->phys = ltq_mtd->res->start; +- ltq_mtd->res->start; +- ltq_mtd->map->size = resource_size(ltq_mtd->res); +- ltq_mtd->map->virt = devm_ioremap_resource(&pdev->dev, ltq_mtd->res); +- if (IS_ERR(ltq_mtd->map->virt)) +- return PTR_ERR(ltq_mtd->map->virt); +- +- ltq_mtd->map->name = ltq_map_name; +- ltq_mtd->map->bankwidth = 2; +- ltq_mtd->map->read = ltq_read16; +- ltq_mtd->map->write = ltq_write16; +- ltq_mtd->map->copy_from = ltq_copy_from; +- ltq_mtd->map->copy_to = ltq_copy_to; +- +- ltq_mtd->map->map_priv_1 = LTQ_NOR_PROBING; +- ltq_mtd->mtd = do_map_probe("cfi_probe", ltq_mtd->map); +- ltq_mtd->map->map_priv_1 = LTQ_NOR_NORMAL; +- +- if (!ltq_mtd->mtd) { +- dev_err(&pdev->dev, "probing failed\n"); +- return -ENXIO; ++ if (devices_found == 1) { ++ ltq_mtd->cmtd = ltq_mtd->mtd[0]; ++ } else if (devices_found > 1) { ++ /* ++ * We detected multiple devices. Concatenate them together. ++ */ ++ ltq_mtd->cmtd = mtd_concat_create(ltq_mtd->mtd, devices_found, dev_name(&pdev->dev)); ++ if (ltq_mtd->cmtd == NULL) ++ err = -ENXIO; + } + +- ltq_mtd->mtd->dev.parent = &pdev->dev; +- mtd_set_of_node(ltq_mtd->mtd, pdev->dev.of_node); +- +- cfi = ltq_mtd->map->fldrv_priv; +- cfi->addr_unlock1 ^= 1; +- cfi->addr_unlock2 ^= 1; ++ ltq_mtd->cmtd->dev.parent = &pdev->dev; ++ mtd_set_of_node(ltq_mtd->cmtd, pdev->dev.of_node); + +- err = mtd_device_register(ltq_mtd->mtd, NULL, 0); ++ err = mtd_device_register(ltq_mtd->cmtd, NULL, 0); + if (err) { + dev_err(&pdev->dev, "failed to add partitions\n"); +- goto err_destroy; ++ goto err_out; + } + + return 0; + +-err_destroy: +- map_destroy(ltq_mtd->mtd); ++err_out: ++ ltq_mtd_remove(pdev); + return err; + } + +-static int +-ltq_mtd_remove(struct platform_device *pdev) +-{ +- struct ltq_mtd *ltq_mtd = platform_get_drvdata(pdev); +- +- if (ltq_mtd && ltq_mtd->mtd) { +- mtd_device_unregister(ltq_mtd->mtd); +- map_destroy(ltq_mtd->mtd); +- } +- return 0; +-} +- + static const struct of_device_id ltq_mtd_match[] = { + { .compatible = "lantiq,nor" }, + {}, diff --git a/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch b/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch new file mode 100644 index 0000000000..d153c521d3 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0300-MTD-cfi-cmdset-0001-disable-buffered-writes.patch @@ -0,0 +1,11 @@ +--- a/drivers/mtd/chips/cfi_cmdset_0001.c ++++ b/drivers/mtd/chips/cfi_cmdset_0001.c +@@ -39,7 +39,7 @@ + /* #define CMDSET0001_DISABLE_WRITE_SUSPEND */ + + // debugging, turns off buffer write mode if set to 1 +-#define FORCE_WORD_WRITE 0 ++#define FORCE_WORD_WRITE 1 + + /* Intel chips */ + #define I82802AB 0x00ad diff --git a/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch b/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch new file mode 100644 index 0000000000..92c4b56493 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0301-xrx200-add-gphy-clk-src-device-tree-binding.patch @@ -0,0 +1,30 @@ +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -440,6 +440,20 @@ static void clkdev_add_clkout(void) + } + } + ++static void set_phy_clock_source(struct device_node *np_cgu) ++{ ++ u32 phy_clk_src, ifcc; ++ ++ if (!np_cgu) ++ return; ++ ++ if (of_property_read_u32(np_cgu, "lantiq,phy-clk-src", &phy_clk_src)) ++ return; ++ ++ ifcc = ltq_cgu_r32(ifccr) & ~(0x1c); ++ ltq_cgu_w32(ifcc | (phy_clk_src << 2), ifccr); ++} ++ + /* bring up all register ranges that we need for basic system control */ + void __init ltq_soc_init(void) + { +@@ -605,4 +619,6 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("1e100400.serial", NULL, 1, 0, PMU_ASC0); + } + usb_set_clock(); ++ ++ set_phy_clock_source(np_cgu); + } diff --git a/target/linux/lantiq/patches-5.15/0310-v5.16-MIPS-lantiq-dma-make-the-burst-length-configurable-b.patch b/target/linux/lantiq/patches-5.15/0310-v5.16-MIPS-lantiq-dma-make-the-burst-length-configurable-b.patch new file mode 100644 index 0000000000..33c06e9d77 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0310-v5.16-MIPS-lantiq-dma-make-the-burst-length-configurable-b.patch @@ -0,0 +1,86 @@ +From 49293bbc50cb7d44223eb49e0f7cb38e7dac2361 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 14 Sep 2021 23:21:01 +0200 +Subject: [PATCH 4/5] MIPS: lantiq: dma: make the burst length configurable by + the drivers + +Make the burst length configurable by the drivers. + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Acked-by: Hauke Mehrtens <hauke@hauke-m.de> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + .../include/asm/mach-lantiq/xway/xway_dma.h | 2 +- + arch/mips/lantiq/xway/dma.c | 38 ++++++++++++++++--- + 2 files changed, 34 insertions(+), 6 deletions(-) + +--- a/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h +@@ -45,6 +45,6 @@ extern void ltq_dma_close(struct ltq_dma + extern void ltq_dma_alloc_tx(struct ltq_dma_channel *ch); + extern void ltq_dma_alloc_rx(struct ltq_dma_channel *ch); + extern void ltq_dma_free(struct ltq_dma_channel *ch); +-extern void ltq_dma_init_port(int p); ++extern void ltq_dma_init_port(int p, int tx_burst, int rx_burst); + + #endif +--- a/arch/mips/lantiq/xway/dma.c ++++ b/arch/mips/lantiq/xway/dma.c +@@ -181,7 +181,7 @@ ltq_dma_free(struct ltq_dma_channel *ch) + EXPORT_SYMBOL_GPL(ltq_dma_free); + + void +-ltq_dma_init_port(int p) ++ltq_dma_init_port(int p, int tx_burst, int rx_burst) + { + ltq_dma_w32(p, LTQ_DMA_PS); + switch (p) { +@@ -190,16 +190,44 @@ ltq_dma_init_port(int p) + * Tell the DMA engine to swap the endianness of data frames and + * drop packets if the channel arbitration fails. + */ +- ltq_dma_w32_mask(0, DMA_ETOP_ENDIANNESS | DMA_PDEN, ++ ltq_dma_w32_mask(0, (DMA_ETOP_ENDIANNESS | DMA_PDEN), + LTQ_DMA_PCTRL); + break; + +- case DMA_PORT_DEU: +- ltq_dma_w32((DMA_PCTRL_2W_BURST << DMA_TX_BURST_SHIFT) | +- (DMA_PCTRL_2W_BURST << DMA_RX_BURST_SHIFT), ++ default: ++ break; ++ } ++ ++ switch (rx_burst) { ++ case 8: ++ ltq_dma_w32_mask(0x0c, (DMA_PCTRL_8W_BURST << DMA_RX_BURST_SHIFT), + LTQ_DMA_PCTRL); + break; ++ case 4: ++ ltq_dma_w32_mask(0x0c, (DMA_PCTRL_4W_BURST << DMA_RX_BURST_SHIFT), ++ LTQ_DMA_PCTRL); ++ break; ++ case 2: ++ ltq_dma_w32_mask(0x0c, (DMA_PCTRL_2W_BURST << DMA_RX_BURST_SHIFT), ++ LTQ_DMA_PCTRL); ++ break; ++ default: ++ break; ++ } + ++ switch (tx_burst) { ++ case 8: ++ ltq_dma_w32_mask(0x30, (DMA_PCTRL_8W_BURST << DMA_TX_BURST_SHIFT), ++ LTQ_DMA_PCTRL); ++ break; ++ case 4: ++ ltq_dma_w32_mask(0x30, (DMA_PCTRL_4W_BURST << DMA_TX_BURST_SHIFT), ++ LTQ_DMA_PCTRL); ++ break; ++ case 2: ++ ltq_dma_w32_mask(0x30, (DMA_PCTRL_2W_BURST << DMA_TX_BURST_SHIFT), ++ LTQ_DMA_PCTRL); ++ break; + default: + break; + } diff --git a/target/linux/lantiq/patches-5.15/0320-MIPS-lantiq-enable-all-hardware-interrupts-on-second.patch b/target/linux/lantiq/patches-5.15/0320-MIPS-lantiq-enable-all-hardware-interrupts-on-second.patch new file mode 100644 index 0000000000..0ac9adfb96 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0320-MIPS-lantiq-enable-all-hardware-interrupts-on-second.patch @@ -0,0 +1,86 @@ +From 2025bc9c3a949b65bfd0601f727678b37962c6c5 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Fri, 4 Jun 2021 18:27:58 +0200 +Subject: [PATCH] MIPS: lantiq: enable all hardware interrupts on second VPE + +This patch is needed to handle interrupts by the second VPE on the Lantiq +ARX100, xRX200, xRX300 and xRX330 SoCs. Switching some ICU interrupts to +the second VPE results in a hang. Currently, the vsmp_init_secondary() +function is responsible for enabling these interrupts. It only enables +Malta-specific interrupts (SW0, SW1, HW4 and HW5). + +The MIPS core has 8 interrupts defined. On Lantiq SoCs, hardware +interrupts are wired to an ICU instance. Each VPE has an independent +instance of the ICU. The mapping of the ICU interrupts is shown below: +SW0(IP0) - IPI call, +SW1(IP1) - IPI resched, +HW0(IP2) - ICU 0-31, +HW1(IP3) - ICU 32-63, +HW2(IP4) - ICU 64-95, +HW3(IP5) - ICU 96-127, +HW4(IP6) - ICU 128-159, +HW5(IP7) - timer. + +This patch enables all interrupt lines on the second VPE. + +This problem affects multithreaded SoCs with a custom interrupt controller. +SOCs with 1004Kc core and newer use the MIPS GIC. At this point, I am aware +that the Realtek RTL839x and RTL930x SoCs may need a similar fix. In the +future, this may be replaced with some generic solution. + +Tested on Lantiq xRX200. + +Suggested-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +--- + arch/mips/lantiq/prom.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -37,6 +37,14 @@ static struct ltq_soc_info soc_info; + /* for Multithreading (APRP), vpe.c will use it */ + unsigned long cp0_memsize; + ++/* ++ * These structs are used to override vsmp_init_secondary() ++ */ ++#if defined(CONFIG_MIPS_MT_SMP) ++extern const struct plat_smp_ops vsmp_smp_ops; ++static struct plat_smp_ops lantiq_smp_ops; ++#endif ++ + const char *get_system_type(void) + { + return soc_info.sys_type; +@@ -100,6 +108,17 @@ void __init device_tree_init(void) + unflatten_and_copy_device_tree(); + } + ++#if defined(CONFIG_MIPS_MT_SMP) ++static void lantiq_init_secondary(void) ++{ ++ /* ++ * MIPS CPU startup function vsmp_init_secondary() will only ++ * enable some of the interrupts for the second CPU/VPE. ++ */ ++ set_c0_status(ST0_IM); ++} ++#endif ++ + void __init prom_init(void) + { + /* call the soc specific detetcion code and get it to fill soc_info */ +@@ -111,7 +130,10 @@ void __init prom_init(void) + prom_init_cmdline(); + + #if defined(CONFIG_MIPS_MT_SMP) +- if (register_vsmp_smp_ops()) +- panic("failed to register_vsmp_smp_ops()"); ++ if (cpu_has_mipsmt) { ++ lantiq_smp_ops = vsmp_smp_ops; ++ lantiq_smp_ops.init_secondary = lantiq_init_secondary; ++ register_smp_ops(&lantiq_smp_ops); ++ } + #endif + } diff --git a/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch b/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch new file mode 100644 index 0000000000..2cc541ae3c --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0701-NET-lantiq-etop-of-mido.patch @@ -0,0 +1,37 @@ +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -30,6 +30,7 @@ + #include <linux/of_net.h> + #include <linux/of_irq.h> + #include <linux/of_platform.h> ++#include <linux/of_mdio.h> + + #include <asm/checksum.h> + +@@ -553,7 +554,8 @@ static int + ltq_etop_mdio_init(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int err; ++ struct device_node *mdio_np = NULL; ++ int err, ret; + + priv->mii_bus = mdiobus_alloc(); + if (!priv->mii_bus) { +@@ -573,7 +575,15 @@ ltq_etop_mdio_init(struct net_device *de + priv->mii_bus->name = "ltq_mii"; + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + priv->pdev->name, priv->pdev->id); +- if (mdiobus_register(priv->mii_bus)) { ++ ++ mdio_np = of_get_child_by_name(priv->pdev->dev.of_node, "mdio-bus"); ++ ++ if (mdio_np) ++ ret = of_mdiobus_register(priv->mii_bus, mdio_np); ++ else ++ ret = mdiobus_register(priv->mii_bus); ++ ++ if (ret) { + err = -ENXIO; + goto err_out_free_mdiobus; + } diff --git a/target/linux/lantiq/patches-5.15/0702-v5.16-net-lantiq-add-support-for-jumbo-frames.patch b/target/linux/lantiq/patches-5.15/0702-v5.16-net-lantiq-add-support-for-jumbo-frames.patch new file mode 100644 index 0000000000..5bbf752dba --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0702-v5.16-net-lantiq-add-support-for-jumbo-frames.patch @@ -0,0 +1,145 @@ +From 998ac358019e491217e752bc6dcbb3afb2a6fa3e Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Sun, 19 Sep 2021 20:24:28 +0200 +Subject: [PATCH] net: lantiq: add support for jumbo frames + +Add support for jumbo frames. Full support for jumbo frames requires +changes in the DSA switch driver (lantiq_gswip.c). + +Tested on BT Hone Hub 5A. + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/lantiq_xrx200.c | 64 +++++++++++++++++++++++++--- + 1 file changed, 57 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -14,13 +14,15 @@ + #include <linux/clk.h> + #include <linux/delay.h> + ++#include <linux/if_vlan.h> ++ + #include <linux/of_net.h> + #include <linux/of_platform.h> + + #include <xway_dma.h> + + /* DMA */ +-#define XRX200_DMA_DATA_LEN 0x600 ++#define XRX200_DMA_DATA_LEN (SZ_64K - 1) + #define XRX200_DMA_RX 0 + #define XRX200_DMA_TX 1 + +@@ -106,7 +108,8 @@ static void xrx200_flush_dma(struct xrx2 + break; + + desc->ctl = LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | +- XRX200_DMA_DATA_LEN; ++ (ch->priv->net_dev->mtu + VLAN_ETH_HLEN + ++ ETH_FCS_LEN); + ch->dma.desc++; + ch->dma.desc %= LTQ_DESC_NUM; + } +@@ -154,19 +157,20 @@ static int xrx200_close(struct net_devic + + static int xrx200_alloc_skb(struct xrx200_chan *ch) + { ++ int len = ch->priv->net_dev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + struct sk_buff *skb = ch->skb[ch->dma.desc]; + dma_addr_t mapping; + int ret = 0; + + ch->skb[ch->dma.desc] = netdev_alloc_skb_ip_align(ch->priv->net_dev, +- XRX200_DMA_DATA_LEN); ++ len); + if (!ch->skb[ch->dma.desc]) { + ret = -ENOMEM; + goto skip; + } + + mapping = dma_map_single(ch->priv->dev, ch->skb[ch->dma.desc]->data, +- XRX200_DMA_DATA_LEN, DMA_FROM_DEVICE); ++ len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ch->priv->dev, mapping))) { + dev_kfree_skb_any(ch->skb[ch->dma.desc]); + ch->skb[ch->dma.desc] = skb; +@@ -179,8 +183,7 @@ static int xrx200_alloc_skb(struct xrx20 + wmb(); + skip: + ch->dma.desc_base[ch->dma.desc].ctl = +- LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | +- XRX200_DMA_DATA_LEN; ++ LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | len; + + return ret; + } +@@ -340,10 +343,57 @@ err_drop: + return NETDEV_TX_OK; + } + ++static int ++xrx200_change_mtu(struct net_device *net_dev, int new_mtu) ++{ ++ struct xrx200_priv *priv = netdev_priv(net_dev); ++ struct xrx200_chan *ch_rx = &priv->chan_rx; ++ int old_mtu = net_dev->mtu; ++ bool running = false; ++ struct sk_buff *skb; ++ int curr_desc; ++ int ret = 0; ++ ++ net_dev->mtu = new_mtu; ++ ++ if (new_mtu <= old_mtu) ++ return ret; ++ ++ running = netif_running(net_dev); ++ if (running) { ++ napi_disable(&ch_rx->napi); ++ ltq_dma_close(&ch_rx->dma); ++ } ++ ++ xrx200_poll_rx(&ch_rx->napi, LTQ_DESC_NUM); ++ curr_desc = ch_rx->dma.desc; ++ ++ for (ch_rx->dma.desc = 0; ch_rx->dma.desc < LTQ_DESC_NUM; ++ ch_rx->dma.desc++) { ++ skb = ch_rx->skb[ch_rx->dma.desc]; ++ ret = xrx200_alloc_skb(ch_rx); ++ if (ret) { ++ net_dev->mtu = old_mtu; ++ break; ++ } ++ dev_kfree_skb_any(skb); ++ } ++ ++ ch_rx->dma.desc = curr_desc; ++ if (running) { ++ napi_enable(&ch_rx->napi); ++ ltq_dma_open(&ch_rx->dma); ++ ltq_dma_enable_irq(&ch_rx->dma); ++ } ++ ++ return ret; ++} ++ + static const struct net_device_ops xrx200_netdev_ops = { + .ndo_open = xrx200_open, + .ndo_stop = xrx200_close, + .ndo_start_xmit = xrx200_start_xmit, ++ .ndo_change_mtu = xrx200_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + }; +@@ -454,7 +504,7 @@ static int xrx200_probe(struct platform_ + net_dev->netdev_ops = &xrx200_netdev_ops; + SET_NETDEV_DEV(net_dev, dev); + net_dev->min_mtu = ETH_ZLEN; +- net_dev->max_mtu = XRX200_DMA_DATA_LEN; ++ net_dev->max_mtu = XRX200_DMA_DATA_LEN - VLAN_ETH_HLEN - ETH_FCS_LEN; + + /* load the memory ranges */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/target/linux/lantiq/patches-5.15/0703-v5.16-net-lantiq_xrx200-increase-buffer-reservation.patch b/target/linux/lantiq/patches-5.15/0703-v5.16-net-lantiq_xrx200-increase-buffer-reservation.patch new file mode 100644 index 0000000000..77c0eb6354 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0703-v5.16-net-lantiq_xrx200-increase-buffer-reservation.patch @@ -0,0 +1,122 @@ +From 1488fc204568f707fe2a42a913788c00a95af30e Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Fri, 17 Dec 2021 01:07:40 +0100 +Subject: [PATCH] net: lantiq_xrx200: increase buffer reservation + +If the user sets a lower mtu on the CPU port than on the switch, +then DMA inserts a few more bytes into the buffer than expected. +In the worst case, it may exceed the size of the buffer. The +experiments showed that the buffer should be a multiple of the +burst length value. This patch rounds the length of the rx buffer +upwards and fixes this bug. The reservation of FCS space in the +buffer has been removed as PMAC strips the FCS. + +Fixes: 998ac358019e ("net: lantiq: add support for jumbo frames") +Reported-by: Thomas Nixon <tom@tomn.co.uk> +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/lantiq_xrx200.c | 34 ++++++++++++++++++++-------- + 1 file changed, 24 insertions(+), 10 deletions(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -70,6 +70,8 @@ struct xrx200_priv { + struct xrx200_chan chan_tx; + struct xrx200_chan chan_rx; + ++ u16 rx_buf_size; ++ + struct net_device *net_dev; + struct device *dev; + +@@ -96,6 +98,16 @@ static void xrx200_pmac_mask(struct xrx2 + xrx200_pmac_w32(priv, val, offset); + } + ++static int xrx200_max_frame_len(int mtu) ++{ ++ return VLAN_ETH_HLEN + mtu; ++} ++ ++static int xrx200_buffer_size(int mtu) ++{ ++ return round_up(xrx200_max_frame_len(mtu), 4 * XRX200_DMA_BURST_LEN); ++} ++ + /* drop all the packets from the DMA ring */ + static void xrx200_flush_dma(struct xrx200_chan *ch) + { +@@ -108,8 +120,7 @@ static void xrx200_flush_dma(struct xrx2 + break; + + desc->ctl = LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | +- (ch->priv->net_dev->mtu + VLAN_ETH_HLEN + +- ETH_FCS_LEN); ++ ch->priv->rx_buf_size; + ch->dma.desc++; + ch->dma.desc %= LTQ_DESC_NUM; + } +@@ -157,21 +168,21 @@ static int xrx200_close(struct net_devic + + static int xrx200_alloc_skb(struct xrx200_chan *ch) + { +- int len = ch->priv->net_dev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + struct sk_buff *skb = ch->skb[ch->dma.desc]; ++ struct xrx200_priv *priv = ch->priv; + dma_addr_t mapping; + int ret = 0; + +- ch->skb[ch->dma.desc] = netdev_alloc_skb_ip_align(ch->priv->net_dev, +- len); ++ ch->skb[ch->dma.desc] = netdev_alloc_skb_ip_align(priv->net_dev, ++ priv->rx_buf_size); + if (!ch->skb[ch->dma.desc]) { + ret = -ENOMEM; + goto skip; + } + +- mapping = dma_map_single(ch->priv->dev, ch->skb[ch->dma.desc]->data, +- len, DMA_FROM_DEVICE); +- if (unlikely(dma_mapping_error(ch->priv->dev, mapping))) { ++ mapping = dma_map_single(priv->dev, ch->skb[ch->dma.desc]->data, ++ priv->rx_buf_size, DMA_FROM_DEVICE); ++ if (unlikely(dma_mapping_error(priv->dev, mapping))) { + dev_kfree_skb_any(ch->skb[ch->dma.desc]); + ch->skb[ch->dma.desc] = skb; + ret = -ENOMEM; +@@ -183,7 +194,7 @@ static int xrx200_alloc_skb(struct xrx20 + wmb(); + skip: + ch->dma.desc_base[ch->dma.desc].ctl = +- LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | len; ++ LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | priv->rx_buf_size; + + return ret; + } +@@ -355,6 +366,7 @@ xrx200_change_mtu(struct net_device *net + int ret = 0; + + net_dev->mtu = new_mtu; ++ priv->rx_buf_size = xrx200_buffer_size(new_mtu); + + if (new_mtu <= old_mtu) + return ret; +@@ -374,6 +386,7 @@ xrx200_change_mtu(struct net_device *net + ret = xrx200_alloc_skb(ch_rx); + if (ret) { + net_dev->mtu = old_mtu; ++ priv->rx_buf_size = xrx200_buffer_size(old_mtu); + break; + } + dev_kfree_skb_any(skb); +@@ -504,7 +517,8 @@ static int xrx200_probe(struct platform_ + net_dev->netdev_ops = &xrx200_netdev_ops; + SET_NETDEV_DEV(net_dev, dev); + net_dev->min_mtu = ETH_ZLEN; +- net_dev->max_mtu = XRX200_DMA_DATA_LEN - VLAN_ETH_HLEN - ETH_FCS_LEN; ++ net_dev->max_mtu = XRX200_DMA_DATA_LEN - xrx200_max_frame_len(0); ++ priv->rx_buf_size = xrx200_buffer_size(ETH_DATA_LEN); + + /* load the memory ranges */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/target/linux/lantiq/patches-5.15/0704-v5.17-net-lantiq_xrx200-add-ingress-SG-DMA-support.patch b/target/linux/lantiq/patches-5.15/0704-v5.17-net-lantiq_xrx200-add-ingress-SG-DMA-support.patch new file mode 100644 index 0000000000..f2c36952fc --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0704-v5.17-net-lantiq_xrx200-add-ingress-SG-DMA-support.patch @@ -0,0 +1,104 @@ +From c3e6b2c35b34214c58c1e90d65dab5f5393608e7 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Mon, 3 Jan 2022 20:43:16 +0100 +Subject: [PATCH] net: lantiq_xrx200: add ingress SG DMA support + +This patch adds support for scatter gather DMA. DMA in PMAC splits +the packet into several buffers when the MTU on the CPU port is +less than the MTU of the switch. The first buffer starts at an +offset of NET_IP_ALIGN. In subsequent buffers, dma ignores the +offset. Thanks to this patch, the user can still connect to the +device in such a situation. For normal configurations, the patch +has no effect on performance. + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/lantiq_xrx200.c | 47 +++++++++++++++++++++++----- + 1 file changed, 40 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -26,6 +26,9 @@ + #define XRX200_DMA_RX 0 + #define XRX200_DMA_TX 1 + ++#define XRX200_DMA_PACKET_COMPLETE 0 ++#define XRX200_DMA_PACKET_IN_PROGRESS 1 ++ + /* cpu port mac */ + #define PMAC_RX_IPG 0x0024 + #define PMAC_RX_IPG_MASK 0xf +@@ -61,6 +64,9 @@ struct xrx200_chan { + struct ltq_dma_channel dma; + struct sk_buff *skb[LTQ_DESC_NUM]; + ++ struct sk_buff *skb_head; ++ struct sk_buff *skb_tail; ++ + struct xrx200_priv *priv; + }; + +@@ -204,7 +210,8 @@ static int xrx200_hw_receive(struct xrx2 + struct xrx200_priv *priv = ch->priv; + struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; + struct sk_buff *skb = ch->skb[ch->dma.desc]; +- int len = (desc->ctl & LTQ_DMA_SIZE_MASK); ++ u32 ctl = desc->ctl; ++ int len = (ctl & LTQ_DMA_SIZE_MASK); + struct net_device *net_dev = priv->net_dev; + int ret; + +@@ -220,12 +227,36 @@ static int xrx200_hw_receive(struct xrx2 + } + + skb_put(skb, len); +- skb->protocol = eth_type_trans(skb, net_dev); +- netif_receive_skb(skb); +- net_dev->stats.rx_packets++; +- net_dev->stats.rx_bytes += len; + +- return 0; ++ /* add buffers to skb via skb->frag_list */ ++ if (ctl & LTQ_DMA_SOP) { ++ ch->skb_head = skb; ++ ch->skb_tail = skb; ++ } else if (ch->skb_head) { ++ if (ch->skb_head == ch->skb_tail) ++ skb_shinfo(ch->skb_tail)->frag_list = skb; ++ else ++ ch->skb_tail->next = skb; ++ ch->skb_tail = skb; ++ skb_reserve(ch->skb_tail, -NET_IP_ALIGN); ++ ch->skb_head->len += skb->len; ++ ch->skb_head->data_len += skb->len; ++ ch->skb_head->truesize += skb->truesize; ++ } ++ ++ if (ctl & LTQ_DMA_EOP) { ++ ch->skb_head->protocol = eth_type_trans(ch->skb_head, net_dev); ++ netif_receive_skb(ch->skb_head); ++ net_dev->stats.rx_packets++; ++ net_dev->stats.rx_bytes += ch->skb_head->len; ++ ch->skb_head = NULL; ++ ch->skb_tail = NULL; ++ ret = XRX200_DMA_PACKET_COMPLETE; ++ } else { ++ ret = XRX200_DMA_PACKET_IN_PROGRESS; ++ } ++ ++ return ret; + } + + static int xrx200_poll_rx(struct napi_struct *napi, int budget) +@@ -240,7 +271,9 @@ static int xrx200_poll_rx(struct napi_st + + if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { + ret = xrx200_hw_receive(ch); +- if (ret) ++ if (ret == XRX200_DMA_PACKET_IN_PROGRESS) ++ continue; ++ if (ret != XRX200_DMA_PACKET_COMPLETE) + return ret; + rx++; + } else { diff --git a/target/linux/lantiq/patches-5.15/0705-v5.13-net-dsa-lantiq-allow-to-use-all-GPHYs-on-xRX300-and-.patch b/target/linux/lantiq/patches-5.15/0705-v5.13-net-dsa-lantiq-allow-to-use-all-GPHYs-on-xRX300-and-.patch new file mode 100644 index 0000000000..a69f682c03 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0705-v5.13-net-dsa-lantiq-allow-to-use-all-GPHYs-on-xRX300-and-.patch @@ -0,0 +1,275 @@ +From a09d042b086202735c4ed64573cdd79933020001 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Mon, 22 Mar 2021 21:37:15 +0100 +Subject: [PATCH] net: dsa: lantiq: allow to use all GPHYs on xRX300 and xRX330 + +This patch allows to use all PHYs on GRX300 and GRX330. The ARX300 +has 3 and the GRX330 has 4 integrated PHYs connected to different +ports compared to VRX200. Each integrated PHY can work as single +Gigabit Ethernet PHY (GMII) or as double Fast Ethernet PHY (MII). + +Allowed port configurations: + +xRX200: +GMAC0: RGMII, MII, REVMII or RMII port +GMAC1: RGMII, MII, REVMII or RMII port +GMAC2: GPHY0 (GMII) +GMAC3: GPHY0 (MII) +GMAC4: GPHY1 (GMII) +GMAC5: GPHY1 (MII) or RGMII port + +xRX300: +GMAC0: RGMII port +GMAC1: GPHY2 (GMII) +GMAC2: GPHY0 (GMII) +GMAC3: GPHY0 (MII) +GMAC4: GPHY1 (GMII) +GMAC5: GPHY1 (MII) or RGMII port + +xRX330: +GMAC0: RGMII, GMII or RMII port +GMAC1: GPHY2 (GMII) +GMAC2: GPHY0 (GMII) +GMAC3: GPHY0 (MII) or GPHY3 (GMII) +GMAC4: GPHY1 (GMII) +GMAC5: GPHY1 (MII), RGMII or RMII port + +Tested on D-Link DWR966 (xRX330) with OpenWRT. + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Acked-by: Hauke Mehrtens <hauke@hauke-m.de> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/lantiq_gswip.c | 142 ++++++++++++++++++++++++++------- + 1 file changed, 113 insertions(+), 29 deletions(-) + +--- a/drivers/net/dsa/lantiq_gswip.c ++++ b/drivers/net/dsa/lantiq_gswip.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0 + /* +- * Lantiq / Intel GSWIP switch driver for VRX200 SoCs ++ * Lantiq / Intel GSWIP switch driver for VRX200, xRX300 and xRX330 SoCs + * + * Copyright (C) 2010 Lantiq Deutschland + * Copyright (C) 2012 John Crispin <john@phrozen.org> +@@ -104,6 +104,7 @@ + #define GSWIP_MII_CFG_MODE_RMIIP 0x2 + #define GSWIP_MII_CFG_MODE_RMIIM 0x3 + #define GSWIP_MII_CFG_MODE_RGMII 0x4 ++#define GSWIP_MII_CFG_MODE_GMII 0x9 + #define GSWIP_MII_CFG_MODE_MASK 0xf + #define GSWIP_MII_CFG_RATE_M2P5 0x00 + #define GSWIP_MII_CFG_RATE_M25 0x10 +@@ -241,6 +242,7 @@ + struct gswip_hw_info { + int max_ports; + int cpu_port; ++ const struct dsa_switch_ops *ops; + }; + + struct xway_gphy_match_data { +@@ -1438,12 +1440,42 @@ static int gswip_port_fdb_dump(struct ds + return 0; + } + +-static void gswip_phylink_validate(struct dsa_switch *ds, int port, +- unsigned long *supported, +- struct phylink_link_state *state) ++static void gswip_phylink_set_capab(unsigned long *supported, ++ struct phylink_link_state *state) + { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + ++ /* Allow all the expected bits */ ++ phylink_set(mask, Autoneg); ++ phylink_set_port_modes(mask); ++ phylink_set(mask, Pause); ++ phylink_set(mask, Asym_Pause); ++ ++ /* With the exclusion of MII, Reverse MII and Reduced MII, we ++ * support Gigabit, including Half duplex ++ */ ++ if (state->interface != PHY_INTERFACE_MODE_MII && ++ state->interface != PHY_INTERFACE_MODE_REVMII && ++ state->interface != PHY_INTERFACE_MODE_RMII) { ++ phylink_set(mask, 1000baseT_Full); ++ phylink_set(mask, 1000baseT_Half); ++ } ++ ++ phylink_set(mask, 10baseT_Half); ++ phylink_set(mask, 10baseT_Full); ++ phylink_set(mask, 100baseT_Half); ++ phylink_set(mask, 100baseT_Full); ++ ++ bitmap_and(supported, supported, mask, ++ __ETHTOOL_LINK_MODE_MASK_NBITS); ++ bitmap_and(state->advertising, state->advertising, mask, ++ __ETHTOOL_LINK_MODE_MASK_NBITS); ++} ++ ++static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ + switch (port) { + case 0: + case 1: +@@ -1470,38 +1502,54 @@ static void gswip_phylink_validate(struc + return; + } + +- /* Allow all the expected bits */ +- phylink_set(mask, Autoneg); +- phylink_set_port_modes(mask); +- phylink_set(mask, Pause); +- phylink_set(mask, Asym_Pause); ++ gswip_phylink_set_capab(supported, state); + +- /* With the exclusion of MII, Reverse MII and Reduced MII, we +- * support Gigabit, including Half duplex +- */ +- if (state->interface != PHY_INTERFACE_MODE_MII && +- state->interface != PHY_INTERFACE_MODE_REVMII && +- state->interface != PHY_INTERFACE_MODE_RMII) { +- phylink_set(mask, 1000baseT_Full); +- phylink_set(mask, 1000baseT_Half); ++ return; ++ ++unsupported: ++ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", ++ phy_modes(state->interface), port); ++} ++ ++static void gswip_xrx300_phylink_validate(struct dsa_switch *ds, int port, ++ unsigned long *supported, ++ struct phylink_link_state *state) ++{ ++ switch (port) { ++ case 0: ++ if (!phy_interface_mode_is_rgmii(state->interface) && ++ state->interface != PHY_INTERFACE_MODE_GMII && ++ state->interface != PHY_INTERFACE_MODE_RMII) ++ goto unsupported; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ if (state->interface != PHY_INTERFACE_MODE_INTERNAL) ++ goto unsupported; ++ break; ++ case 5: ++ if (!phy_interface_mode_is_rgmii(state->interface) && ++ state->interface != PHY_INTERFACE_MODE_INTERNAL && ++ state->interface != PHY_INTERFACE_MODE_RMII) ++ goto unsupported; ++ break; ++ default: ++ bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); ++ dev_err(ds->dev, "Unsupported port: %i\n", port); ++ return; + } + +- phylink_set(mask, 10baseT_Half); +- phylink_set(mask, 10baseT_Full); +- phylink_set(mask, 100baseT_Half); +- phylink_set(mask, 100baseT_Full); ++ gswip_phylink_set_capab(supported, state); + +- bitmap_and(supported, supported, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); +- bitmap_and(state->advertising, state->advertising, mask, +- __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + + unsupported: + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + dev_err(ds->dev, "Unsupported interface '%s' for port %d\n", + phy_modes(state->interface), port); +- return; + } + + static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link) +@@ -1636,6 +1684,9 @@ static void gswip_phylink_mac_config(str + case PHY_INTERFACE_MODE_RGMII_TXID: + miicfg |= GSWIP_MII_CFG_MODE_RGMII; + break; ++ case PHY_INTERFACE_MODE_GMII: ++ miicfg |= GSWIP_MII_CFG_MODE_GMII; ++ break; + default: + dev_err(ds->dev, + "Unsupported interface: %d\n", state->interface); +@@ -1762,7 +1813,7 @@ static int gswip_get_sset_count(struct d + return ARRAY_SIZE(gswip_rmon_cnt); + } + +-static const struct dsa_switch_ops gswip_switch_ops = { ++static const struct dsa_switch_ops gswip_xrx200_switch_ops = { + .get_tag_protocol = gswip_get_tag_protocol, + .setup = gswip_setup, + .port_enable = gswip_port_enable, +@@ -1778,7 +1829,31 @@ static const struct dsa_switch_ops gswip + .port_fdb_add = gswip_port_fdb_add, + .port_fdb_del = gswip_port_fdb_del, + .port_fdb_dump = gswip_port_fdb_dump, +- .phylink_validate = gswip_phylink_validate, ++ .phylink_validate = gswip_xrx200_phylink_validate, ++ .phylink_mac_config = gswip_phylink_mac_config, ++ .phylink_mac_link_down = gswip_phylink_mac_link_down, ++ .phylink_mac_link_up = gswip_phylink_mac_link_up, ++ .get_strings = gswip_get_strings, ++ .get_ethtool_stats = gswip_get_ethtool_stats, ++ .get_sset_count = gswip_get_sset_count, ++}; ++ ++static const struct dsa_switch_ops gswip_xrx300_switch_ops = { ++ .get_tag_protocol = gswip_get_tag_protocol, ++ .setup = gswip_setup, ++ .port_enable = gswip_port_enable, ++ .port_disable = gswip_port_disable, ++ .port_bridge_join = gswip_port_bridge_join, ++ .port_bridge_leave = gswip_port_bridge_leave, ++ .port_fast_age = gswip_port_fast_age, ++ .port_vlan_filtering = gswip_port_vlan_filtering, ++ .port_vlan_add = gswip_port_vlan_add, ++ .port_vlan_del = gswip_port_vlan_del, ++ .port_stp_state_set = gswip_port_stp_state_set, ++ .port_fdb_add = gswip_port_fdb_add, ++ .port_fdb_del = gswip_port_fdb_del, ++ .port_fdb_dump = gswip_port_fdb_dump, ++ .phylink_validate = gswip_xrx300_phylink_validate, + .phylink_mac_config = gswip_phylink_mac_config, + .phylink_mac_link_down = gswip_phylink_mac_link_down, + .phylink_mac_link_up = gswip_phylink_mac_link_up, +@@ -2042,7 +2117,7 @@ static int gswip_probe(struct platform_d + priv->ds->dev = dev; + priv->ds->num_ports = priv->hw_info->max_ports; + priv->ds->priv = priv; +- priv->ds->ops = &gswip_switch_ops; ++ priv->ds->ops = priv->hw_info->ops; + priv->dev = dev; + version = gswip_switch_r(priv, GSWIP_VERSION); + +@@ -2126,10 +2201,19 @@ static int gswip_remove(struct platform_ + static const struct gswip_hw_info gswip_xrx200 = { + .max_ports = 7, + .cpu_port = 6, ++ .ops = &gswip_xrx200_switch_ops, ++}; ++ ++static const struct gswip_hw_info gswip_xrx300 = { ++ .max_ports = 7, ++ .cpu_port = 6, ++ .ops = &gswip_xrx300_switch_ops, + }; + + static const struct of_device_id gswip_of_match[] = { + { .compatible = "lantiq,xrx200-gswip", .data = &gswip_xrx200 }, ++ { .compatible = "lantiq,xrx300-gswip", .data = &gswip_xrx300 }, ++ { .compatible = "lantiq,xrx330-gswip", .data = &gswip_xrx300 }, + {}, + }; + MODULE_DEVICE_TABLE(of, gswip_of_match); diff --git a/target/linux/lantiq/patches-5.15/0706-v5.18-net-lantiq-enable-jumbo-frames-on-GSWIP.patch b/target/linux/lantiq/patches-5.15/0706-v5.18-net-lantiq-enable-jumbo-frames-on-GSWIP.patch new file mode 100644 index 0000000000..49a19bebee --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0706-v5.18-net-lantiq-enable-jumbo-frames-on-GSWIP.patch @@ -0,0 +1,127 @@ +From c40bb4fedcd6b8b6a714da5dd466eb88ed2652d1 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Wed, 9 Mar 2022 00:04:57 +0100 +Subject: net: dsa: lantiq_gswip: enable jumbo frames on GSWIP + +This enables non-standard MTUs on a per-port basis, with the overall +frame size set based on the CPU port. + +When the MTU is not changed, this should have no effect. + +Long packets crash the switch with MTUs of greater than 2526, so the +maximum is limited for now. Medium packets are sometimes dropped (e.g. +TCP over 2477, UDP over 2516-2519, ICMP over 2526), Hence an MTU value +of 2400 seems safe. + +Signed-off-by: Thomas Nixon <tom@tomn.co.uk> +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Link: https://lore.kernel.org/r/20220308230457.1599237-1-olek2@wp.pl +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/dsa/lantiq_gswip.c | 53 ++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 49 insertions(+), 4 deletions(-) + +--- a/drivers/net/dsa/lantiq_gswip.c ++++ b/drivers/net/dsa/lantiq_gswip.c +@@ -213,6 +213,7 @@ + #define GSWIP_MAC_CTRL_0_GMII_MII 0x0001 + #define GSWIP_MAC_CTRL_0_GMII_RGMII 0x0002 + #define GSWIP_MAC_CTRL_2p(p) (0x905 + ((p) * 0xC)) ++#define GSWIP_MAC_CTRL_2_LCHKL BIT(2) /* Frame Length Check Long Enable */ + #define GSWIP_MAC_CTRL_2_MLEN BIT(3) /* Maximum Untagged Frame Lnegth */ + + /* Ethernet Switch Fetch DMA Port Control Register */ +@@ -239,6 +240,15 @@ + + #define XRX200_GPHY_FW_ALIGN (16 * 1024) + ++/* Maximum packet size supported by the switch. In theory this should be 10240, ++ * but long packets currently cause lock-ups with an MTU of over 2526. Medium ++ * packets are sometimes dropped (e.g. TCP over 2477, UDP over 2516-2519, ICMP ++ * over 2526), hence an MTU value of 2400 seems safe. This issue only affects ++ * packet reception. This is probably caused by the PPA engine, which is on the ++ * RX part of the device. Packet transmission works properly up to 10240. ++ */ ++#define GSWIP_MAX_PACKET_LENGTH 2400 ++ + struct gswip_hw_info { + int max_ports; + int cpu_port; +@@ -858,10 +868,6 @@ static int gswip_setup(struct dsa_switch + gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_INGRESS, + GSWIP_PCE_PCTRL_0p(cpu_port)); + +- gswip_switch_mask(priv, 0, GSWIP_MAC_CTRL_2_MLEN, +- GSWIP_MAC_CTRL_2p(cpu_port)); +- gswip_switch_w(priv, VLAN_ETH_FRAME_LEN + 8 + ETH_FCS_LEN, +- GSWIP_MAC_FLEN); + gswip_switch_mask(priv, 0, GSWIP_BM_QUEUE_GCTRL_GL_MOD, + GSWIP_BM_QUEUE_GCTRL); + +@@ -878,6 +884,8 @@ static int gswip_setup(struct dsa_switch + return err; + } + ++ ds->mtu_enforcement_ingress = true; ++ + gswip_port_enable(ds, cpu_port, NULL); + return 0; + } +@@ -1472,6 +1480,39 @@ static void gswip_phylink_set_capab(unsi + __ETHTOOL_LINK_MODE_MASK_NBITS); + } + ++static int gswip_port_max_mtu(struct dsa_switch *ds, int port) ++{ ++ /* Includes 8 bytes for special header. */ ++ return GSWIP_MAX_PACKET_LENGTH - VLAN_ETH_HLEN - ETH_FCS_LEN; ++} ++ ++static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) ++{ ++ struct gswip_priv *priv = ds->priv; ++ int cpu_port = priv->hw_info->cpu_port; ++ ++ /* CPU port always has maximum mtu of user ports, so use it to set ++ * switch frame size, including 8 byte special header. ++ */ ++ if (port == cpu_port) { ++ new_mtu += 8; ++ gswip_switch_w(priv, VLAN_ETH_HLEN + new_mtu + ETH_FCS_LEN, ++ GSWIP_MAC_FLEN); ++ } ++ ++ /* Enable MLEN for ports with non-standard MTUs, including the special ++ * header on the CPU port added above. ++ */ ++ if (new_mtu != ETH_DATA_LEN) ++ gswip_switch_mask(priv, 0, GSWIP_MAC_CTRL_2_MLEN, ++ GSWIP_MAC_CTRL_2p(port)); ++ else ++ gswip_switch_mask(priv, GSWIP_MAC_CTRL_2_MLEN, 0, ++ GSWIP_MAC_CTRL_2p(port)); ++ ++ return 0; ++} ++ + static void gswip_xrx200_phylink_validate(struct dsa_switch *ds, int port, + unsigned long *supported, + struct phylink_link_state *state) +@@ -1829,6 +1870,8 @@ static const struct dsa_switch_ops gswip + .port_fdb_add = gswip_port_fdb_add, + .port_fdb_del = gswip_port_fdb_del, + .port_fdb_dump = gswip_port_fdb_dump, ++ .port_change_mtu = gswip_port_change_mtu, ++ .port_max_mtu = gswip_port_max_mtu, + .phylink_validate = gswip_xrx200_phylink_validate, + .phylink_mac_config = gswip_phylink_mac_config, + .phylink_mac_link_down = gswip_phylink_mac_link_down, +@@ -1853,6 +1896,8 @@ static const struct dsa_switch_ops gswip + .port_fdb_add = gswip_port_fdb_add, + .port_fdb_del = gswip_port_fdb_del, + .port_fdb_dump = gswip_port_fdb_dump, ++ .port_change_mtu = gswip_port_change_mtu, ++ .port_max_mtu = gswip_port_max_mtu, + .phylink_validate = gswip_xrx300_phylink_validate, + .phylink_mac_config = gswip_phylink_mac_config, + .phylink_mac_link_down = gswip_phylink_mac_link_down, diff --git a/target/linux/lantiq/patches-5.15/0710-v5.16-net-lantiq-configure-the-burst-length-in-ethernet-dr.patch b/target/linux/lantiq/patches-5.15/0710-v5.16-net-lantiq-configure-the-burst-length-in-ethernet-dr.patch new file mode 100644 index 0000000000..4fb7d0767b --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0710-v5.16-net-lantiq-configure-the-burst-length-in-ethernet-dr.patch @@ -0,0 +1,126 @@ +From 14d4e308e0aa0b78dc7a059716861a4380de3535 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 14 Sep 2021 23:21:02 +0200 +Subject: [PATCH 5/5] net: lantiq: configure the burst length in ethernet + drivers + +Configure the burst length in Ethernet drivers. This improves +Ethernet performance by 58%. According to the vendor BSP, +8W burst length is supported by ar9 and newer SoCs. + +The NAT benchmark results on xRX200 (Down/Up): +* 2W: 330 Mb/s +* 4W: 432 Mb/s 372 Mb/s +* 8W: 520 Mb/s 389 Mb/s + +Tested on xRX200 and xRX330. + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/lantiq_etop.c | 21 ++++++++++++++++++--- + drivers/net/ethernet/lantiq_xrx200.c | 21 ++++++++++++++++++--- + 2 files changed, 36 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -148,6 +148,9 @@ struct ltq_etop_priv { + struct ltq_etop_chan txch; + struct ltq_etop_chan rxch; + ++ int tx_burst_len; ++ int rx_burst_len; ++ + int tx_irq; + int rx_irq; + +@@ -399,7 +402,7 @@ ltq_etop_dma_init(struct net_device *dev + int rx = priv->rx_irq - LTQ_DMA_ETOP; + int err; + +- ltq_dma_init_port(DMA_PORT_ETOP); ++ ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, rx_burst_len); + + priv->txch.dma.nr = tx; + priv->txch.dma.dev = &priv->pdev->dev; +@@ -676,8 +679,8 @@ ltq_etop_tx(struct sk_buff *skb, struct + return NETDEV_TX_BUSY; + } + +- /* dma needs to start on a 16 byte aligned address */ +- byte_offset = CPHYSADDR(skb->data) % 16; ++ /* dma needs to start on a burst length value aligned address */ ++ byte_offset = CPHYSADDR(skb->data) % (priv->tx_burst_len * 4); + priv->txch.skb[priv->txch.dma.desc] = skb; + + netif_trans_update(dev); +@@ -925,6 +928,18 @@ static int ltq_etop_probe(struct platfor + spin_lock_init(&priv->lock); + SET_NETDEV_DEV(dev, &pdev->dev); + ++ err = device_property_read_u32(&pdev->dev, "lantiq,tx-burst-length", &priv->tx_burst_len); ++ if (err < 0) { ++ dev_err(&pdev->dev, "unable to read tx-burst-length property\n"); ++ return err; ++ } ++ ++ err = device_property_read_u32(&pdev->dev, "lantiq,rx-burst-length", &priv->rx_burst_len); ++ if (err < 0) { ++ dev_err(&pdev->dev, "unable to read rx-burst-length property\n"); ++ return err; ++ } ++ + netif_napi_add(dev, &priv->txch.napi, ltq_etop_poll_tx, 8); + netif_napi_add(dev, &priv->rxch.napi, ltq_etop_poll_rx, 32); + priv->txch.netdev = dev; +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -81,6 +81,9 @@ struct xrx200_priv { + struct net_device *net_dev; + struct device *dev; + ++ int tx_burst_len; ++ int rx_burst_len; ++ + __iomem void *pmac_reg; + }; + +@@ -363,8 +366,8 @@ static netdev_tx_t xrx200_start_xmit(str + if (unlikely(dma_mapping_error(priv->dev, mapping))) + goto err_drop; + +- /* dma needs to start on a 16 byte aligned address */ +- byte_offset = mapping % 16; ++ /* dma needs to start on a burst length value aligned address */ ++ byte_offset = mapping % (priv->tx_burst_len * 4); + + desc->addr = mapping - byte_offset; + /* Make sure the address is written before we give it to HW */ +@@ -465,7 +468,7 @@ static int xrx200_dma_init(struct xrx200 + int ret = 0; + int i; + +- ltq_dma_init_port(DMA_PORT_ETOP); ++ ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, rx_burst_len); + + ch_rx->dma.nr = XRX200_DMA_RX; + ch_rx->dma.dev = priv->dev; +@@ -584,6 +587,18 @@ static int xrx200_probe(struct platform_ + if (err) + eth_hw_addr_random(net_dev); + ++ err = device_property_read_u32(dev, "lantiq,tx-burst-length", &priv->tx_burst_len); ++ if (err < 0) { ++ dev_err(dev, "unable to read tx-burst-length property\n"); ++ return err; ++ } ++ ++ err = device_property_read_u32(dev, "lantiq,rx-burst-length", &priv->rx_burst_len); ++ if (err < 0) { ++ dev_err(dev, "unable to read rx-burst-length property\n"); ++ return err; ++ } ++ + /* bring up the dma engine and IP core */ + err = xrx200_dma_init(priv); + if (err) diff --git a/target/linux/lantiq/patches-5.15/0711-v5.16-net-lantiq_xrx200-Hardcode-the-burst-length-value.patch b/target/linux/lantiq/patches-5.15/0711-v5.16-net-lantiq_xrx200-Hardcode-the-burst-length-value.patch new file mode 100644 index 0000000000..7448af8c26 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0711-v5.16-net-lantiq_xrx200-Hardcode-the-burst-length-value.patch @@ -0,0 +1,73 @@ +From 7e553c44f09a8f536090904c6db5b8c9dbafa03b Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 26 Oct 2021 22:59:01 +0200 +Subject: [PATCH] net: lantiq_xrx200: Hardcode the burst length value + +All SoCs with this IP core support 8 burst length. Hauke +suggested to hardcode this value and simplify the driver. + +Link: https://lkml.org/lkml/2021/9/14/1533 +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/lantiq_xrx200.c | 21 ++++----------------- + 1 file changed, 4 insertions(+), 17 deletions(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -25,6 +25,7 @@ + #define XRX200_DMA_DATA_LEN (SZ_64K - 1) + #define XRX200_DMA_RX 0 + #define XRX200_DMA_TX 1 ++#define XRX200_DMA_BURST_LEN 8 + + #define XRX200_DMA_PACKET_COMPLETE 0 + #define XRX200_DMA_PACKET_IN_PROGRESS 1 +@@ -81,9 +82,6 @@ struct xrx200_priv { + struct net_device *net_dev; + struct device *dev; + +- int tx_burst_len; +- int rx_burst_len; +- + __iomem void *pmac_reg; + }; + +@@ -367,7 +365,7 @@ static netdev_tx_t xrx200_start_xmit(str + goto err_drop; + + /* dma needs to start on a burst length value aligned address */ +- byte_offset = mapping % (priv->tx_burst_len * 4); ++ byte_offset = mapping % (XRX200_DMA_BURST_LEN * 4); + + desc->addr = mapping - byte_offset; + /* Make sure the address is written before we give it to HW */ +@@ -468,7 +466,8 @@ static int xrx200_dma_init(struct xrx200 + int ret = 0; + int i; + +- ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, rx_burst_len); ++ ltq_dma_init_port(DMA_PORT_ETOP, XRX200_DMA_BURST_LEN, ++ XRX200_DMA_BURST_LEN); + + ch_rx->dma.nr = XRX200_DMA_RX; + ch_rx->dma.dev = priv->dev; +@@ -587,18 +586,6 @@ static int xrx200_probe(struct platform_ + if (err) + eth_hw_addr_random(net_dev); + +- err = device_property_read_u32(dev, "lantiq,tx-burst-length", &priv->tx_burst_len); +- if (err < 0) { +- dev_err(dev, "unable to read tx-burst-length property\n"); +- return err; +- } +- +- err = device_property_read_u32(dev, "lantiq,rx-burst-length", &priv->rx_burst_len); +- if (err < 0) { +- dev_err(dev, "unable to read rx-burst-length property\n"); +- return err; +- } +- + /* bring up the dma engine and IP core */ + err = xrx200_dma_init(priv); + if (err) diff --git a/target/linux/lantiq/patches-5.15/0712-v5.16-net-ethernet-lantiq_etop-Fix-compilation-error.patch b/target/linux/lantiq/patches-5.15/0712-v5.16-net-ethernet-lantiq_etop-Fix-compilation-error.patch new file mode 100644 index 0000000000..06f4bc2eee --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0712-v5.16-net-ethernet-lantiq_etop-Fix-compilation-error.patch @@ -0,0 +1,26 @@ +From 68eabc348148ae051631e8dab13c3b1a85c82896 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 9 Nov 2021 23:23:54 +0100 +Subject: [PATCH] net: ethernet: lantiq_etop: Fix compilation error + +This fixes the error detected when compiling the driver. + +Fixes: 14d4e308e0aa ("net: lantiq: configure the burst length in ethernet drivers") +Reported-by: kernel test robot <lkp@intel.com> +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/lantiq_etop.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -402,7 +402,7 @@ ltq_etop_dma_init(struct net_device *dev + int rx = priv->rx_irq - LTQ_DMA_ETOP; + int err; + +- ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, rx_burst_len); ++ ltq_dma_init_port(DMA_PORT_ETOP, priv->tx_burst_len, priv->rx_burst_len); + + priv->txch.dma.nr = tx; + priv->txch.dma.dev = &priv->pdev->dev; diff --git a/target/linux/lantiq/patches-5.15/0713-v5.17-MIPS-lantiq-dma-increase-descritor-count.patch b/target/linux/lantiq/patches-5.15/0713-v5.17-MIPS-lantiq-dma-increase-descritor-count.patch new file mode 100644 index 0000000000..37ed1d4f31 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0713-v5.17-MIPS-lantiq-dma-increase-descritor-count.patch @@ -0,0 +1,28 @@ +From 5112e9234bbb89f8dd15c983206bd9107b8436d5 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 4 Jan 2022 16:11:42 +0100 +Subject: [PATCH 713/715] MIPS: lantiq: dma: increase descritor count + +NAT Performance results on BT Home Hub 5A (kernel 5.10.89, mtu 1500): + + Down Up +Before 539 Mbps 599 Mbps +After 545 Mbps 625 Mbps + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + arch/mips/include/asm/mach-lantiq/xway/xway_dma.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h +@@ -8,7 +8,7 @@ + #define LTQ_DMA_H__ + + #define LTQ_DESC_SIZE 0x08 /* each descriptor is 64bit */ +-#define LTQ_DESC_NUM 0x40 /* 64 descriptors / channel */ ++#define LTQ_DESC_NUM 0xC0 /* 192 descriptors / channel */ + + #define LTQ_DMA_OWN BIT(31) /* owner bit */ + #define LTQ_DMA_C BIT(30) /* complete bit */ diff --git a/target/linux/lantiq/patches-5.15/0714-v5.17-net-lantiq_xrx200-increase-napi-poll-weigth.patch b/target/linux/lantiq/patches-5.15/0714-v5.17-net-lantiq_xrx200-increase-napi-poll-weigth.patch new file mode 100644 index 0000000000..1fa49f406e --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0714-v5.17-net-lantiq_xrx200-increase-napi-poll-weigth.patch @@ -0,0 +1,32 @@ +From 768818d772d5d4ddc0c7eb2e62848929270ab7a3 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 4 Jan 2022 16:11:43 +0100 +Subject: [PATCH 714/715] net: lantiq_xrx200: increase napi poll weigth + +NAT Performance results on BT Home Hub 5A (kernel 5.10.89, mtu 1500): + + Down Up +Before 545 Mbps 625 Mbps +After 577 Mbps 648 Mbps + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/lantiq_xrx200.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -606,8 +606,10 @@ static int xrx200_probe(struct platform_ + PMAC_HD_CTL); + + /* setup NAPI */ +- netif_napi_add(net_dev, &priv->chan_rx.napi, xrx200_poll_rx, 32); +- netif_tx_napi_add(net_dev, &priv->chan_tx.napi, xrx200_tx_housekeeping, 32); ++ netif_napi_add(net_dev, &priv->chan_rx.napi, xrx200_poll_rx, ++ NAPI_POLL_WEIGHT); ++ netif_tx_napi_add(net_dev, &priv->chan_tx.napi, xrx200_tx_housekeeping, ++ NAPI_POLL_WEIGHT); + + platform_set_drvdata(pdev, priv); + diff --git a/target/linux/lantiq/patches-5.15/0715-v5.17-net-lantiq_xrx200-convert-to-build_skb.patch b/target/linux/lantiq/patches-5.15/0715-v5.17-net-lantiq_xrx200-convert-to-build_skb.patch new file mode 100644 index 0000000000..b2b014832c --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0715-v5.17-net-lantiq_xrx200-convert-to-build_skb.patch @@ -0,0 +1,206 @@ +From e015593573b3e3f74bd8a63c05fa92902194a354 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Tue, 4 Jan 2022 16:11:44 +0100 +Subject: [PATCH 715/715] net: lantiq_xrx200: convert to build_skb + +We can increase the efficiency of rx path by using buffers to receive +packets then build SKBs around them just before passing into the network +stack. In contrast, preallocating SKBs too early reduces CPU cache +efficiency. + +NAT Performance results on BT Home Hub 5A (kernel 5.10.89, mtu 1500): + + Down Up +Before 577 Mbps 648 Mbps +After 624 Mbps 695 Mbps + +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/lantiq_xrx200.c | 56 ++++++++++++++++++---------- + 1 file changed, 36 insertions(+), 20 deletions(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -63,7 +63,11 @@ struct xrx200_chan { + + struct napi_struct napi; + struct ltq_dma_channel dma; +- struct sk_buff *skb[LTQ_DESC_NUM]; ++ ++ union { ++ struct sk_buff *skb[LTQ_DESC_NUM]; ++ void *rx_buff[LTQ_DESC_NUM]; ++ }; + + struct sk_buff *skb_head; + struct sk_buff *skb_tail; +@@ -78,6 +82,7 @@ struct xrx200_priv { + struct xrx200_chan chan_rx; + + u16 rx_buf_size; ++ u16 rx_skb_size; + + struct net_device *net_dev; + struct device *dev; +@@ -115,6 +120,12 @@ static int xrx200_buffer_size(int mtu) + return round_up(xrx200_max_frame_len(mtu), 4 * XRX200_DMA_BURST_LEN); + } + ++static int xrx200_skb_size(u16 buf_size) ++{ ++ return SKB_DATA_ALIGN(buf_size + NET_SKB_PAD + NET_IP_ALIGN) + ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++} ++ + /* drop all the packets from the DMA ring */ + static void xrx200_flush_dma(struct xrx200_chan *ch) + { +@@ -173,30 +184,29 @@ static int xrx200_close(struct net_devic + return 0; + } + +-static int xrx200_alloc_skb(struct xrx200_chan *ch) ++static int xrx200_alloc_buf(struct xrx200_chan *ch, void *(*alloc)(unsigned int size)) + { +- struct sk_buff *skb = ch->skb[ch->dma.desc]; ++ void *buf = ch->rx_buff[ch->dma.desc]; + struct xrx200_priv *priv = ch->priv; + dma_addr_t mapping; + int ret = 0; + +- ch->skb[ch->dma.desc] = netdev_alloc_skb_ip_align(priv->net_dev, +- priv->rx_buf_size); +- if (!ch->skb[ch->dma.desc]) { ++ ch->rx_buff[ch->dma.desc] = alloc(priv->rx_skb_size); ++ if (!ch->rx_buff[ch->dma.desc]) { + ret = -ENOMEM; + goto skip; + } + +- mapping = dma_map_single(priv->dev, ch->skb[ch->dma.desc]->data, ++ mapping = dma_map_single(priv->dev, ch->rx_buff[ch->dma.desc], + priv->rx_buf_size, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, mapping))) { +- dev_kfree_skb_any(ch->skb[ch->dma.desc]); +- ch->skb[ch->dma.desc] = skb; ++ skb_free_frag(ch->rx_buff[ch->dma.desc]); ++ ch->rx_buff[ch->dma.desc] = buf; + ret = -ENOMEM; + goto skip; + } + +- ch->dma.desc_base[ch->dma.desc].addr = mapping; ++ ch->dma.desc_base[ch->dma.desc].addr = mapping + NET_SKB_PAD + NET_IP_ALIGN; + /* Make sure the address is written before we give it to HW */ + wmb(); + skip: +@@ -210,13 +220,14 @@ static int xrx200_hw_receive(struct xrx2 + { + struct xrx200_priv *priv = ch->priv; + struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; +- struct sk_buff *skb = ch->skb[ch->dma.desc]; ++ void *buf = ch->rx_buff[ch->dma.desc]; + u32 ctl = desc->ctl; + int len = (ctl & LTQ_DMA_SIZE_MASK); + struct net_device *net_dev = priv->net_dev; ++ struct sk_buff *skb; + int ret; + +- ret = xrx200_alloc_skb(ch); ++ ret = xrx200_alloc_buf(ch, napi_alloc_frag); + + ch->dma.desc++; + ch->dma.desc %= LTQ_DESC_NUM; +@@ -227,19 +238,21 @@ static int xrx200_hw_receive(struct xrx2 + return ret; + } + ++ skb = build_skb(buf, priv->rx_skb_size); ++ skb_reserve(skb, NET_SKB_PAD); + skb_put(skb, len); + + /* add buffers to skb via skb->frag_list */ + if (ctl & LTQ_DMA_SOP) { + ch->skb_head = skb; + ch->skb_tail = skb; ++ skb_reserve(skb, NET_IP_ALIGN); + } else if (ch->skb_head) { + if (ch->skb_head == ch->skb_tail) + skb_shinfo(ch->skb_tail)->frag_list = skb; + else + ch->skb_tail->next = skb; + ch->skb_tail = skb; +- skb_reserve(ch->skb_tail, -NET_IP_ALIGN); + ch->skb_head->len += skb->len; + ch->skb_head->data_len += skb->len; + ch->skb_head->truesize += skb->truesize; +@@ -395,12 +408,13 @@ xrx200_change_mtu(struct net_device *net + struct xrx200_chan *ch_rx = &priv->chan_rx; + int old_mtu = net_dev->mtu; + bool running = false; +- struct sk_buff *skb; ++ void *buff; + int curr_desc; + int ret = 0; + + net_dev->mtu = new_mtu; + priv->rx_buf_size = xrx200_buffer_size(new_mtu); ++ priv->rx_skb_size = xrx200_skb_size(priv->rx_buf_size); + + if (new_mtu <= old_mtu) + return ret; +@@ -416,14 +430,15 @@ xrx200_change_mtu(struct net_device *net + + for (ch_rx->dma.desc = 0; ch_rx->dma.desc < LTQ_DESC_NUM; + ch_rx->dma.desc++) { +- skb = ch_rx->skb[ch_rx->dma.desc]; +- ret = xrx200_alloc_skb(ch_rx); ++ buff = ch_rx->rx_buff[ch_rx->dma.desc]; ++ ret = xrx200_alloc_buf(ch_rx, netdev_alloc_frag); + if (ret) { + net_dev->mtu = old_mtu; + priv->rx_buf_size = xrx200_buffer_size(old_mtu); ++ priv->rx_skb_size = xrx200_skb_size(priv->rx_buf_size); + break; + } +- dev_kfree_skb_any(skb); ++ skb_free_frag(buff); + } + + ch_rx->dma.desc = curr_desc; +@@ -476,7 +491,7 @@ static int xrx200_dma_init(struct xrx200 + ltq_dma_alloc_rx(&ch_rx->dma); + for (ch_rx->dma.desc = 0; ch_rx->dma.desc < LTQ_DESC_NUM; + ch_rx->dma.desc++) { +- ret = xrx200_alloc_skb(ch_rx); ++ ret = xrx200_alloc_buf(ch_rx, netdev_alloc_frag); + if (ret) + goto rx_free; + } +@@ -511,7 +526,7 @@ rx_ring_free: + /* free the allocated RX ring */ + for (i = 0; i < LTQ_DESC_NUM; i++) { + if (priv->chan_rx.skb[i]) +- dev_kfree_skb_any(priv->chan_rx.skb[i]); ++ skb_free_frag(priv->chan_rx.rx_buff[i]); + } + + rx_free: +@@ -528,7 +543,7 @@ static void xrx200_hw_cleanup(struct xrx + + /* free the allocated RX ring */ + for (i = 0; i < LTQ_DESC_NUM; i++) +- dev_kfree_skb_any(priv->chan_rx.skb[i]); ++ skb_free_frag(priv->chan_rx.rx_buff[i]); + } + + static int xrx200_probe(struct platform_device *pdev) +@@ -554,6 +569,7 @@ static int xrx200_probe(struct platform_ + net_dev->min_mtu = ETH_ZLEN; + net_dev->max_mtu = XRX200_DMA_DATA_LEN - xrx200_max_frame_len(0); + priv->rx_buf_size = xrx200_buffer_size(ETH_DATA_LEN); ++ priv->rx_skb_size = xrx200_skb_size(priv->rx_buf_size); + + /* load the memory ranges */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/target/linux/lantiq/patches-5.15/0716-v5.17-net-lantiq_xrx200-fix-use-after-free-bug.patch b/target/linux/lantiq/patches-5.15/0716-v5.17-net-lantiq_xrx200-fix-use-after-free-bug.patch new file mode 100644 index 0000000000..090b7e3111 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0716-v5.17-net-lantiq_xrx200-fix-use-after-free-bug.patch @@ -0,0 +1,30 @@ +From dd830aed23c6e07cd8e2a163742bf3d63c9add08 Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Sat, 5 Mar 2022 12:20:39 +0100 +Subject: net: lantiq_xrx200: fix use after free bug + +The skb->len field is read after the packet is sent to the network +stack. In the meantime, skb can be freed. This patch fixes this bug. + +Fixes: c3e6b2c35b34 ("net: lantiq_xrx200: add ingress SG DMA support") +Reported-by: Eric Dumazet <eric.dumazet@gmail.com> +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Acked-by: Hauke Mehrtens <hauke@hauke-m.de> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/ethernet/lantiq_xrx200.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -260,9 +260,9 @@ static int xrx200_hw_receive(struct xrx2 + + if (ctl & LTQ_DMA_EOP) { + ch->skb_head->protocol = eth_type_trans(ch->skb_head, net_dev); +- netif_receive_skb(ch->skb_head); + net_dev->stats.rx_packets++; + net_dev->stats.rx_bytes += ch->skb_head->len; ++ netif_receive_skb(ch->skb_head); + ch->skb_head = NULL; + ch->skb_tail = NULL; + ret = XRX200_DMA_PACKET_COMPLETE; diff --git a/target/linux/lantiq/patches-5.15/0717-v6.0-net-lantiq_xrx200-confirm-skb-is-allocated-before-us.patch b/target/linux/lantiq/patches-5.15/0717-v6.0-net-lantiq_xrx200-confirm-skb-is-allocated-before-us.patch new file mode 100644 index 0000000000..9eaec58033 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0717-v6.0-net-lantiq_xrx200-confirm-skb-is-allocated-before-us.patch @@ -0,0 +1,33 @@ +From c8b043702dc0894c07721c5b019096cebc8c798f Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Wed, 24 Aug 2022 23:54:06 +0200 +Subject: [PATCH] net: lantiq_xrx200: confirm skb is allocated before using + +xrx200_hw_receive() assumes build_skb() always works and goes straight +to skb_reserve(). However, build_skb() can fail under memory pressure. + +Add a check in case build_skb() failed to allocate and return NULL. + +Fixes: e015593573b3 ("net: lantiq_xrx200: convert to build_skb") +Reported-by: Eric Dumazet <edumazet@google.com> +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/lantiq_xrx200.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -239,6 +239,12 @@ static int xrx200_hw_receive(struct xrx2 + } + + skb = build_skb(buf, priv->rx_skb_size); ++ if (!skb) { ++ skb_free_frag(buf); ++ net_dev->stats.rx_dropped++; ++ return -ENOMEM; ++ } ++ + skb_reserve(skb, NET_SKB_PAD); + skb_put(skb, len); + diff --git a/target/linux/lantiq/patches-5.15/0718-v6.0-net-lantiq_xrx200-fix-lock-under-memory-pressure.patch b/target/linux/lantiq/patches-5.15/0718-v6.0-net-lantiq_xrx200-fix-lock-under-memory-pressure.patch new file mode 100644 index 0000000000..929ae57ace --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0718-v6.0-net-lantiq_xrx200-fix-lock-under-memory-pressure.patch @@ -0,0 +1,33 @@ +From c4b6e9341f930e4dd089231c0414758f5f1f9dbd Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Wed, 24 Aug 2022 23:54:07 +0200 +Subject: [PATCH] net: lantiq_xrx200: fix lock under memory pressure + +When the xrx200_hw_receive() function returns -ENOMEM, the NAPI poll +function immediately returns an error. +This is incorrect for two reasons: +* the function terminates without enabling interrupts or scheduling NAPI, +* the error code (-ENOMEM) is returned instead of the number of received +packets. + +After the first memory allocation failure occurs, packet reception is +locked due to disabled interrupts from DMA.. + +Fixes: fe1a56420cf2 ("net: lantiq: Add Lantiq / Intel VRX200 Ethernet driver") +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/lantiq_xrx200.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -294,7 +294,7 @@ static int xrx200_poll_rx(struct napi_st + if (ret == XRX200_DMA_PACKET_IN_PROGRESS) + continue; + if (ret != XRX200_DMA_PACKET_COMPLETE) +- return ret; ++ break; + rx++; + } else { + break; diff --git a/target/linux/lantiq/patches-5.15/0719-v6.0-net-lantiq_xrx200-restore-buffer-if-memory-allocatio.patch b/target/linux/lantiq/patches-5.15/0719-v6.0-net-lantiq_xrx200-restore-buffer-if-memory-allocatio.patch new file mode 100644 index 0000000000..182da58ed9 --- /dev/null +++ b/target/linux/lantiq/patches-5.15/0719-v6.0-net-lantiq_xrx200-restore-buffer-if-memory-allocatio.patch @@ -0,0 +1,27 @@ +From c9c3b1775f80fa21f5bff874027d2ccb10f5d90c Mon Sep 17 00:00:00 2001 +From: Aleksander Jan Bajkowski <olek2@wp.pl> +Date: Wed, 24 Aug 2022 23:54:08 +0200 +Subject: [PATCH] net: lantiq_xrx200: restore buffer if memory allocation + failed + +In a situation where memory allocation fails, an invalid buffer address +is stored. When this descriptor is used again, the system panics in the +build_skb() function when accessing memory. + +Fixes: 7ea6cd16f159 ("lantiq: net: fix duplicated skb in rx descriptor ring") +Signed-off-by: Aleksander Jan Bajkowski <olek2@wp.pl> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + drivers/net/ethernet/lantiq_xrx200.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/ethernet/lantiq_xrx200.c ++++ b/drivers/net/ethernet/lantiq_xrx200.c +@@ -193,6 +193,7 @@ static int xrx200_alloc_buf(struct xrx20 + + ch->rx_buff[ch->dma.desc] = alloc(priv->rx_skb_size); + if (!ch->rx_buff[ch->dma.desc]) { ++ ch->rx_buff[ch->dma.desc] = buf; + ret = -ENOMEM; + goto skip; + } |