From: Daniel Golle Date: Mon, 2 Mar 2020 10:26:00 +0000 (+0100) Subject: oxnas: add 5.4 as testing kernel version X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=5ea1b1ecd14793c547cb141d149e43aa954e99fb;p=openwrt%2Fstaging%2Fstintel.git oxnas: add 5.4 as testing kernel version Add 5.4 kernel version as a new testing kernel option. Run-tested on Shuttle KD20, seems to work just as well as kernel 4.14. Signed-off-by: Daniel Golle --- diff --git a/target/linux/oxnas/Makefile b/target/linux/oxnas/Makefile index 52d57f336a..2f9b9f4546 100644 --- a/target/linux/oxnas/Makefile +++ b/target/linux/oxnas/Makefile @@ -10,6 +10,7 @@ DEVICE_TYPE:=nas MAINTAINER:=Daniel Golle KERNEL_PATCHVER:=4.14 +KERNEL_TESTING_PATCHVER:=5.4 include $(INCLUDE_DIR)/target.mk diff --git a/target/linux/oxnas/config-5.4 b/target/linux/oxnas/config-5.4 new file mode 100644 index 0000000000..85e41d9c85 --- /dev/null +++ b/target/linux/oxnas/config-5.4 @@ -0,0 +1,367 @@ +CONFIG_64BIT_TIME=y +CONFIG_ALIGNMENT_TRAP=y +CONFIG_ARCH_32BIT_OFF_T=y +CONFIG_ARCH_CLOCKSOURCE_DATA=y +CONFIG_ARCH_HAS_BINFMT_FLAT=y +CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y +CONFIG_ARCH_HAS_DMA_WRITE_COMBINE=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_ARCH_HAS_FORTIFY_SOURCE=y +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_ARCH_HAS_KCOV=y +CONFIG_ARCH_HAS_KEEPINITRD=y +CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y +CONFIG_ARCH_HAS_PHYS_TO_DMA=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAS_SETUP_DMA_OPS=y +CONFIG_ARCH_HAS_SET_MEMORY=y +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y +CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_KEEP_MEMBLOCK=y +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_ARCH_MULTIPLATFORM=y +CONFIG_ARCH_MULTI_CPU_AUTO=y +# CONFIG_ARCH_MULTI_V4 is not set +# CONFIG_ARCH_MULTI_V4T is not set +CONFIG_ARCH_MULTI_V4_V5=y +CONFIG_ARCH_MULTI_V5=y +CONFIG_ARCH_NR_GPIO=0 +CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y +CONFIG_ARCH_OXNAS=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_ARCH_WANT_LIBATA_LEDS=y +CONFIG_ARM=y +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_ARM_PMU=y +# CONFIG_ARM_SMMU is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_UNWIND=y +CONFIG_ATAGS=y +CONFIG_AUTO_ZRELADDR=y +CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y +CONFIG_BLK_CMDLINE_PARSER=y +CONFIG_BLK_DEBUG_FS=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=65536 +CONFIG_BLK_PM=y +CONFIG_BLK_SCSI_REQUEST=y +# CONFIG_BPF_SYSCALL is not set +CONFIG_CC_HAS_KASAN_GENERIC=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMA=y +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=7 +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_SIZE_MBYTES=64 +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +CONFIG_CMDLINE_PARTITION=y +CONFIG_COMMON_CLK=y +CONFIG_COMMON_CLK_OXNAS=y +CONFIG_COMPAT_32BIT_TIME=y +CONFIG_CONTIG_ALLOC=y +CONFIG_COREDUMP=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_ARM926T=y +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_PM=y +# CONFIG_CPU_IDLE_GOV_TEO is not set +CONFIG_CPU_THUMB_CAPABLE=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_USE_DOMAINS=y +CONFIG_CRASH_CORE=y +CONFIG_CRC16=y +# CONFIG_CRC32_SARWATE is not set +CONFIG_CRC32_SLICEBY8=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_CRYPTO_RNG2=y +CONFIG_DEBUG_ALIGN_RODATA=y +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_DEBUG_USER is not set +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_DMA_CMA=y +CONFIG_DMA_DECLARE_COHERENT=y +CONFIG_DMA_REMAP=y +CONFIG_DNOTIFY=y +CONFIG_DTC=y +# CONFIG_DWMAC_DWC_QOS_ETH is not set +CONFIG_DWMAC_GENERIC=y +CONFIG_DWMAC_OXNAS=y +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +CONFIG_ELF_CORE=y +CONFIG_FAT_FS=y +CONFIG_FIXED_PHY=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_FREEZER=y +CONFIG_FW_LOADER_PAGED_BUF=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CPU_AUTOPROBE=y +CONFIG_GENERIC_EARLY_IOREMAP=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_GENERIC_IRQ_MULTI_HANDLER=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_PHY=y +CONFIG_GENERIC_PINCONF=y +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GPIOLIB=y +CONFIG_GPIOLIB_IRQCHIP=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_HANDLE_DOMAIN_IRQ=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAVE_ARCH_AUDITSYSCALL=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_COPY_THREAD_TLS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y +CONFIG_HAVE_EBPF_JIT=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_LD_DEAD_CODE_DATA_ELIMINATION=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_PCI=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_RSEQ=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_UID16=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HID=y +CONFIG_HID_GENERIC=y +CONFIG_HWMON=y +CONFIG_HZ_FIXED=0 +CONFIG_ICPLUS_PHY=y +CONFIG_INET_DIAG=y +# CONFIG_INET_DIAG_DESTROY is not set +# CONFIG_INET_RAW_DIAG is not set +CONFIG_INET_TCP_DIAG=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INPUT=y +# CONFIG_IOMMU_DEBUGFS is not set +# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_IRQCHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +# CONFIG_ISDN is not set +# CONFIG_JFFS2_FS is not set +CONFIG_KALLSYMS=y +CONFIG_KASAN_STACK=1 +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_XZ is not set +CONFIG_KEXEC=y +CONFIG_KEXEC_CORE=y +# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_NETDEV is not set +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LIBFDT=y +CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCK_DEBUGGING_SUPPORT=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_MACH_OX810SE is not set +CONFIG_MDIO_BUS=y +CONFIG_MDIO_DEVICE=y +CONFIG_MEMFD_CREATE=y +CONFIG_MEMORY_ISOLATION=y +CONFIG_MFD_SYSCON=y +CONFIG_MIGRATION=y +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_KUSER_HELPERS=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NLS=y +CONFIG_NO_HZ=y +CONFIG_NO_HZ_COMMON=y +CONFIG_NO_HZ_IDLE=y +# CONFIG_NVMEM_REBOOT_MODE is not set +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_KOBJ=y +CONFIG_OF_MDIO=y +CONFIG_OF_NET=y +CONFIG_OF_RESERVED_MEM=y +CONFIG_OLD_SIGACTION=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OXNAS_RPS_TIMER=y +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PAGE_POOL=y +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_PHYLIB=y +CONFIG_PHYLINK=y +CONFIG_PHY_OXNAS=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_OXNAS=y +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PLUGIN_HOSTCC="g++" +CONFIG_PM=y +CONFIG_PM_CLK=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_POWER_RESET=y +CONFIG_POWER_RESET_OXNAS=y +CONFIG_PPS=y +CONFIG_PTP_1588_CLOCK=y +CONFIG_RAS=y +CONFIG_RATIONAL=y +CONFIG_RCU_TRACE=y +CONFIG_RD_BZIP2=y +CONFIG_RD_GZIP=y +CONFIG_RD_LZ4=y +CONFIG_RD_LZMA=y +CONFIG_RD_LZO=y +CONFIG_RD_XZ=y +CONFIG_REALTEK_PHY=y +CONFIG_REFCOUNT_FULL=y +CONFIG_REGMAP=y +CONFIG_REGMAP_MMIO=y +CONFIG_RESET_CONTROLLER=y +CONFIG_RESET_OXNAS=y +CONFIG_SCHED_DEBUG=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_FSL=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_MCTRL_GPIO=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIO=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_SERPORT=y +CONFIG_SIMPLE_PM_BUS=y +CONFIG_SLUB_DEBUG=y +CONFIG_SOCK_DIAG=y +CONFIG_SPARSE_IRQ=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_SRCU=y +CONFIG_STACKTRACE=y +# CONFIG_STAGING is not set +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +# CONFIG_STMMAC_SELFTESTS is not set +# CONFIG_STRIP_ASM_SYMS is not set +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_SWPHY=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_TIMER_OF=y +CONFIG_TIMER_PROBE=y +CONFIG_TINY_SRCU=y +CONFIG_TRACE_CLOCK=y +CONFIG_UBSAN_ALIGNMENT=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +CONFIG_UNIX_SCM=y +CONFIG_UNWINDER_ARM=y +# CONFIG_UNWINDER_FRAME_POINTER is not set +CONFIG_USB_SUPPORT=y +# CONFIG_USERIO is not set +CONFIG_USE_OF=y +CONFIG_VERSATILE_FPGA_IRQ=y +CONFIG_VERSATILE_FPGA_IRQ_NR=4 +CONFIG_VFAT_FS=y +# CONFIG_VFP is not set +# CONFIG_VLAN_8021Q is not set +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_WATCHDOG is not set +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_BCJ=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_X86=y +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZLIB_INFLATE=y diff --git a/target/linux/oxnas/files-4.14/drivers/pci/host/pcie-oxnas.c b/target/linux/oxnas/files-4.14/drivers/pci/host/pcie-oxnas.c new file mode 100644 index 0000000000..1dd13f9364 --- /dev/null +++ b/target/linux/oxnas/files-4.14/drivers/pci/host/pcie-oxnas.c @@ -0,0 +1,672 @@ +/* + * PCIe driver for PLX NAS782X SoCs + * + * Author: Ma Haijun + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 + +static inline void oxnas_register_clear_mask(void __iomem *p, unsigned mask) +{ + u32 val = readl_relaxed(p); + + val &= ~mask; + writel_relaxed(val, p); +} + +static inline void oxnas_register_set_mask(void __iomem *p, unsigned mask) +{ + u32 val = readl_relaxed(p); + + val |= mask; + writel_relaxed(val, p); +} + +static inline void oxnas_register_value_mask(void __iomem *p, + unsigned mask, unsigned new_value) +{ + /* TODO sanity check mask & new_value = new_value */ + u32 val = readl_relaxed(p); + + val &= ~mask; + val |= new_value; + writel_relaxed(val, p); +} + +#define VERSION_ID_MAGIC 0x082510b5 +#define LINK_UP_TIMEOUT_SECONDS 1 +#define NUM_CONTROLLERS 1 + +enum { + PCIE_DEVICE_TYPE_MASK = 0x0F, + PCIE_DEVICE_TYPE_ENDPOINT = 0, + PCIE_DEVICE_TYPE_LEGACY_ENDPOINT = 1, + PCIE_DEVICE_TYPE_ROOT = 4, + + PCIE_LTSSM = BIT(4), + PCIE_READY_ENTR_L23 = BIT(9), + PCIE_LINK_UP = BIT(11), + PCIE_OBTRANS = BIT(12), +}; + +/* core config registers */ +enum { + PCI_CONFIG_VERSION_DEVICEID = 0, + PCI_CONFIG_COMMAND_STATUS = 4, +}; + +/* inbound config registers */ +enum { + IB_ADDR_XLATE_ENABLE = 0xFC, + + /* bits */ + ENABLE_IN_ADDR_TRANS = BIT(0), +}; + +/* outbound config registers, offset relative to PCIE_POM0_MEM_ADDR */ +enum { + PCIE_POM0_MEM_ADDR = 0, + PCIE_POM1_MEM_ADDR = 4, + PCIE_IN0_MEM_ADDR = 8, + PCIE_IN1_MEM_ADDR = 12, + PCIE_IN_IO_ADDR = 16, + PCIE_IN_CFG0_ADDR = 20, + PCIE_IN_CFG1_ADDR = 24, + PCIE_IN_MSG_ADDR = 28, + PCIE_IN0_MEM_LIMIT = 32, + PCIE_IN1_MEM_LIMIT = 36, + PCIE_IN_IO_LIMIT = 40, + PCIE_IN_CFG0_LIMIT = 44, + PCIE_IN_CFG1_LIMIT = 48, + PCIE_IN_MSG_LIMIT = 52, + PCIE_AHB_SLAVE_CTRL = 56, + + PCIE_SLAVE_BE_SHIFT = 22, +}; + +#define PCIE_SLAVE_BE(val) ((val) << PCIE_SLAVE_BE_SHIFT) +#define PCIE_SLAVE_BE_MASK PCIE_SLAVE_BE(0xF) + +struct oxnas_pcie_shared { + /* seems all access are serialized, no lock required */ + int refcount; +}; + +/* Structure representing one PCIe interfaces */ +struct oxnas_pcie { + void __iomem *cfgbase; + void __iomem *base; + void __iomem *inbound; + struct regmap *sys_ctrl; + unsigned int outbound_offset; + unsigned int pcie_ctrl_offset; + struct phy *phy; + int haslink; + struct platform_device *pdev; + struct resource io; + struct resource cfg; + struct resource pre_mem; /* prefetchable */ + struct resource non_mem; /* non-prefetchable */ + struct resource busn; /* max available bus numbers */ + int card_reset; /* gpio pin, optional */ + unsigned hcsl_en; /* hcsl pci enable bit */ + struct clk *clk; + struct clk *busclk; /* for pcie bus, actually the PLLB */ + void *private_data[1]; + spinlock_t lock; +}; + +static struct oxnas_pcie_shared pcie_shared = { + .refcount = 0, +}; + +static inline struct oxnas_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + + +static inline void set_out_lanes(struct oxnas_pcie *pcie, unsigned lanes) +{ + regmap_update_bits(pcie->sys_ctrl, pcie->outbound_offset + PCIE_AHB_SLAVE_CTRL, + PCIE_SLAVE_BE_MASK, PCIE_SLAVE_BE(lanes)); + wmb(); +} + +static int oxnas_pcie_link_up(struct oxnas_pcie *pcie) +{ + unsigned long end; + unsigned int val; + + /* Poll for PCIE link up */ + end = jiffies + (LINK_UP_TIMEOUT_SECONDS * HZ); + while (!time_after(jiffies, end)) { + regmap_read(pcie->sys_ctrl, pcie->pcie_ctrl_offset, &val); + if (val & PCIE_LINK_UP) + return 1; + } + return 0; +} + +static void oxnas_pcie_setup_hw(struct oxnas_pcie *pcie) +{ + /* We won't have any inbound address translation. This allows PCI + * devices to access anywhere in the AHB address map. Might be regarded + * as a bit dangerous, but let's get things working before we worry + * about that + */ + oxnas_register_clear_mask(pcie->inbound + IB_ADDR_XLATE_ENABLE, + ENABLE_IN_ADDR_TRANS); + wmb(); + + /* + * Program outbound translation windows + * + * Outbound window is what is referred to as "PCI client" region in HRM + * + * Could use the larger alternative address space to get >>64M regions + * for graphics cards etc., but will not bother at this point. + * + * IP bug means that AMBA window size must be a power of 2 + * + * Set mem0 window for first 16MB of outbound window non-prefetchable + * Set mem1 window for second 16MB of outbound window prefetchable + * Set io window for next 16MB of outbound window + * Set cfg0 for final 1MB of outbound window + * + * Ignore mem1, cfg1 and msg windows for now as no obvious use cases for + * 820 that would need them + * + * Probably ideally want no offset between mem0 window start as seen by + * ARM and as seen on PCI bus and get Linux to assign memory regions to + * PCI devices using the same "PCI client" region start address as seen + * by ARM + */ + + /* Set PCIeA mem0 region to be 1st 16MB of the 64MB PCIeA window */ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN0_MEM_ADDR, pcie->non_mem.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN0_MEM_LIMIT, pcie->non_mem.end); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_POM0_MEM_ADDR, pcie->non_mem.start); + + /* Set PCIeA mem1 region to be 2nd 16MB of the 64MB PCIeA window */ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN1_MEM_ADDR, pcie->pre_mem.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN1_MEM_LIMIT, pcie->pre_mem.end); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_POM1_MEM_ADDR, pcie->pre_mem.start); + + /* Set PCIeA io to be third 16M region of the 64MB PCIeA window*/ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_IO_ADDR, pcie->io.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_IO_LIMIT, pcie->io.end); + + + /* Set PCIeA cgf0 to be last 16M region of the 64MB PCIeA window*/ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_CFG0_ADDR, pcie->cfg.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_CFG0_LIMIT, pcie->cfg.end); + wmb(); + + /* Enable outbound address translation */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, PCIE_OBTRANS, PCIE_OBTRANS); + wmb(); + + /* + * Program PCIe command register for core to: + * enable memory space + * enable bus master + * enable io + */ + writel_relaxed(7, pcie->base + PCI_CONFIG_COMMAND_STATUS); + /* which is which */ + wmb(); +} + +static unsigned oxnas_pcie_cfg_to_offset( + struct pci_sys_data *sys, + unsigned char bus_number, + unsigned int devfn, + int where) +{ + unsigned int function = PCI_FUNC(devfn); + unsigned int slot = PCI_SLOT(devfn); + unsigned char bus_number_offset; + + bus_number_offset = bus_number - sys->busnr; + + /* + * We'll assume for now that the offset, function, slot, bus encoding + * should map onto linear, contiguous addresses in PCIe config space, + * albeit that the majority will be unused as only slot 0 is valid for + * any PCIe bus and most devices have only function 0 + * + * Could be that PCIe in fact works by not encoding the slot number into + * the config space address as it's known that only slot 0 is valid. + * We'll have to experiment if/when we get a PCIe switch connected to + * the PCIe host + */ + return (bus_number_offset << 20) | (slot << 15) | (function << 12) | + (where & ~3); +} + +/* PCI configuration space write function */ +static int oxnas_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + unsigned long flags; + struct oxnas_pcie *pcie = sys_to_pcie(bus->sysdata); + unsigned offset; + u32 value; + u32 lanes; + + /* Only a single device per bus for PCIe point-to-point links */ + if (PCI_SLOT(devfn) > 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (!pcie->haslink) + return PCIBIOS_DEVICE_NOT_FOUND; + + offset = oxnas_pcie_cfg_to_offset(bus->sysdata, bus->number, devfn, + where); + + value = val << (8 * (where & 3)); + lanes = (0xf >> (4-size)) << (where & 3); + /* it race with mem and io write, but the possibility is low, normally + * all config writes happens at driver initialize stage, wont interleave + * with others. + * and many pcie cards use dword (4bytes) access mem/io access only, + * so not bother to copy that ugly work-around now. */ + spin_lock_irqsave(&pcie->lock, flags); + set_out_lanes(pcie, lanes); + writel_relaxed(value, pcie->cfgbase + offset); + set_out_lanes(pcie, 0xf); + spin_unlock_irqrestore(&pcie->lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +/* PCI configuration space read function */ +static int oxnas_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct oxnas_pcie *pcie = sys_to_pcie(bus->sysdata); + unsigned offset; + u32 value; + u32 left_bytes, right_bytes; + + /* Only a single device per bus for PCIe point-to-point links */ + if (PCI_SLOT(devfn) > 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (!pcie->haslink) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + offset = oxnas_pcie_cfg_to_offset(bus->sysdata, bus->number, devfn, + where); + value = readl_relaxed(pcie->cfgbase + offset); + left_bytes = where & 3; + right_bytes = 4 - left_bytes - size; + value <<= right_bytes * 8; + value >>= (left_bytes + right_bytes) * 8; + *val = value; + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops oxnas_pcie_ops = { + .read = oxnas_pcie_rd_conf, + .write = oxnas_pcie_wr_conf, +}; + +static int oxnas_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct oxnas_pcie *pcie = sys_to_pcie(sys); + + pci_add_resource_offset(&sys->resources, &pcie->non_mem, sys->mem_offset); + pci_add_resource_offset(&sys->resources, &pcie->pre_mem, sys->mem_offset); + pci_add_resource_offset(&sys->resources, &pcie->io, sys->io_offset); + pci_add_resource(&sys->resources, &pcie->busn); + if (sys->busnr == 0) { /* default one */ + sys->busnr = pcie->busn.start; + } + /* do not use devm_ioremap_resource, it does not like cfg resource */ + pcie->cfgbase = devm_ioremap(&pcie->pdev->dev, pcie->cfg.start, + resource_size(&pcie->cfg)); + if (!pcie->cfgbase) + return -ENOMEM; + + oxnas_pcie_setup_hw(pcie); + + return 1; +} + +static void oxnas_pcie_enable(struct device *dev, struct oxnas_pcie *pcie) +{ + struct hw_pci hw; + int i; + + memset(&hw, 0, sizeof(hw)); + for (i = 0; i < NUM_CONTROLLERS; i++) + pcie->private_data[i] = pcie; + + hw.nr_controllers = NUM_CONTROLLERS; +/* I think use stack pointer is a bad idea though it is valid in this case */ + hw.private_data = pcie->private_data; + hw.setup = oxnas_pcie_setup; + hw.map_irq = of_irq_parse_and_map_pci; + hw.ops = &oxnas_pcie_ops; + + /* pass dev to maintain of tree, interrupt mapping rely on this */ + pci_common_init_dev(dev, &hw); +} + +static int oxnas_pcie_shared_init(struct platform_device *pdev, struct oxnas_pcie *pcie) +{ + if (++pcie_shared.refcount == 1) { + phy_init(pcie->phy); + phy_power_on(pcie->phy); + return 0; + } else { + return 0; + } +} + +#if 0 +/* maybe we will call it when enter low power state */ +static void oxnas_pcie_shared_deinit(struct platform_device *pdev) +{ + if (--pcie_shared.refcount == 0) { + /* no cleanup needed */; + } +} +#endif + +static int +oxnas_pcie_map_registers(struct platform_device *pdev, + struct device_node *np, + struct oxnas_pcie *pcie) +{ + struct resource regs; + int ret = 0; + u32 outbound_ctrl_offset; + u32 pcie_ctrl_offset; + + ret = of_address_to_resource(np, 0, ®s); + if (ret) { + dev_err(&pdev->dev, "failed to parse base register space\n"); + return -EINVAL; + } + + pcie->base = devm_ioremap_resource(&pdev->dev, ®s); + if (!pcie->base) { + dev_err(&pdev->dev, "failed to map base register space\n"); + return -ENOMEM; + } + + ret = of_address_to_resource(np, 1, ®s); + if (ret) { + dev_err(&pdev->dev, "failed to parse inbound register space\n"); + return -EINVAL; + } + + pcie->inbound = devm_ioremap_resource(&pdev->dev, ®s); + if (!pcie->inbound) { + dev_err(&pdev->dev, "failed to map inbound register space\n"); + return -ENOMEM; + } + + pcie->phy = devm_of_phy_get(&pdev->dev, np, NULL); + if (IS_ERR(pcie->phy)) { + if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) { + dev_err(&pdev->dev, "failed to probe phy\n"); + return PTR_ERR(pcie->phy); + } + dev_warn(&pdev->dev, "phy not attached\n"); + pcie->phy = NULL; + } + + if (of_property_read_u32(np, "plxtech,pcie-outbound-offset", + &outbound_ctrl_offset)) { + dev_err(&pdev->dev, "failed to parse outbound register offset\n"); + return -EINVAL; + } + pcie->outbound_offset = outbound_ctrl_offset; + + if (of_property_read_u32(np, "plxtech,pcie-ctrl-offset", + &pcie_ctrl_offset)) { + dev_err(&pdev->dev, "failed to parse pcie-ctrl register offset\n"); + return -EINVAL; + } + pcie->pcie_ctrl_offset = pcie_ctrl_offset; + + return 0; +} + +static int oxnas_pcie_init_res(struct platform_device *pdev, + struct oxnas_pcie *pcie, + struct device_node *np) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + int ret; + + if (of_pci_range_parser_init(&parser, np)) + return -EINVAL; + + /* Get the I/O and memory ranges from DT */ + for_each_of_pci_range(&parser, &range) { + + unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; + if (restype == IORESOURCE_IO) { + of_pci_range_to_resource(&range, np, &pcie->io); + pcie->io.name = "I/O"; + } + if (restype == IORESOURCE_MEM) { + if (range.flags & IORESOURCE_PREFETCH) { + of_pci_range_to_resource(&range, np, &pcie->pre_mem); + pcie->pre_mem.name = "PRE MEM"; + } else { + of_pci_range_to_resource(&range, np, &pcie->non_mem); + pcie->non_mem.name = "NON MEM"; + } + + } + if (restype == 0) + of_pci_range_to_resource(&range, np, &pcie->cfg); + } + + /* Get the bus range */ + ret = of_pci_parse_bus_range(np, &pcie->busn); + + if (ret) { + dev_err(&pdev->dev, "failed to parse bus-range property: %d\n", + ret); + return ret; + } + + pcie->card_reset = of_get_gpio(np, 0); + if (pcie->card_reset < 0) + dev_info(&pdev->dev, "card reset gpio pin not exists\n"); + + if (of_property_read_u32(np, "plxtech,pcie-hcsl-bit", &pcie->hcsl_en)) + return -EINVAL; + + pcie->clk = of_clk_get_by_name(np, "pcie"); + if (IS_ERR(pcie->clk)) { + return PTR_ERR(pcie->clk); + } + + pcie->busclk = of_clk_get_by_name(np, "busclk"); + if (IS_ERR(pcie->busclk)) { + clk_put(pcie->clk); + return PTR_ERR(pcie->busclk); + } + + return 0; +} + +static void oxnas_pcie_init_hw(struct platform_device *pdev, + struct oxnas_pcie *pcie) +{ + u32 version_id; + int ret; + + clk_prepare_enable(pcie->busclk); + + /* reset PCIe cards use hard-wired gpio pin */ + if (pcie->card_reset >= 0 && + !gpio_direction_output(pcie->card_reset, 0)) { + wmb(); + mdelay(10); + /* must tri-state the pin to pull it up */ + gpio_direction_input(pcie->card_reset); + wmb(); + mdelay(100); + } + + /* ToDo: use phy power-on port... */ + regmap_update_bits(pcie->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, + BIT(pcie->hcsl_en), BIT(pcie->hcsl_en)); + + /* core */ + ret = device_reset(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "core reset failed %d\n", ret); + return; + } + + /* Start PCIe core clocks */ + clk_prepare_enable(pcie->clk); + + version_id = readl_relaxed(pcie->base + PCI_CONFIG_VERSION_DEVICEID); + dev_info(&pdev->dev, "PCIe version/deviceID 0x%x\n", version_id); + + if (version_id != VERSION_ID_MAGIC) { + dev_info(&pdev->dev, "PCIe controller not found\n"); + pcie->haslink = 0; + return; + } + + /* allow entry to L23 state */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, + PCIE_READY_ENTR_L23, PCIE_READY_ENTR_L23); + + /* Set PCIe core into RootCore mode */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, + PCIE_DEVICE_TYPE_MASK, PCIE_DEVICE_TYPE_ROOT); + wmb(); + + /* Bring up the PCI core */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, + PCIE_LTSSM, PCIE_LTSSM); + wmb(); +} + +static int oxnas_pcie_probe(struct platform_device *pdev) +{ + struct oxnas_pcie *pcie; + struct device_node *np = pdev->dev.of_node; + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(struct oxnas_pcie), + GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + pcie->haslink = 1; + spin_lock_init(&pcie->lock); + + pcie->sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); + if (IS_ERR(pcie->sys_ctrl)) + return PTR_ERR(pcie->sys_ctrl); + + ret = oxnas_pcie_init_res(pdev, pcie, np); + if (ret) + return ret; + if (pcie->card_reset >= 0) { + ret = gpio_request_one(pcie->card_reset, GPIOF_DIR_IN, + dev_name(&pdev->dev)); + if (ret) { + dev_err(&pdev->dev, "cannot request gpio pin %d\n", + pcie->card_reset); + return ret; + } + } + + ret = oxnas_pcie_map_registers(pdev, np, pcie); + if (ret) { + dev_err(&pdev->dev, "cannot map registers\n"); + goto err_free_gpio; + } + + ret = oxnas_pcie_shared_init(pdev, pcie); + if (ret) + goto err_free_gpio; + + /* if hw not found, haslink cleared */ + oxnas_pcie_init_hw(pdev, pcie); + + if (pcie->haslink && oxnas_pcie_link_up(pcie)) { + pcie->haslink = 1; + dev_info(&pdev->dev, "link up\n"); + } else { + pcie->haslink = 0; + dev_info(&pdev->dev, "link down\n"); + } + /* should we register our controller even when pcie->haslink is 0 ? */ + /* register the controller with framework */ + oxnas_pcie_enable(&pdev->dev, pcie); + + return 0; + +err_free_gpio: + if (pcie->card_reset) + gpio_free(pcie->card_reset); + + return ret; +} + +static const struct of_device_id oxnas_pcie_of_match_table[] = { + { .compatible = "plxtech,nas782x-pcie", }, + {}, +}; + +static struct platform_driver oxnas_pcie_driver = { + .driver = { + .name = "oxnas-pcie", + .suppress_bind_attrs = true, + .of_match_table = oxnas_pcie_of_match_table, + }, + .probe = oxnas_pcie_probe, +}; + +builtin_platform_driver(oxnas_pcie_driver); diff --git a/target/linux/oxnas/files-4.14/phy/phy-oxnas-pcie.c b/target/linux/oxnas/files-4.14/phy/phy-oxnas-pcie.c new file mode 100644 index 0000000000..676a5f9332 --- /dev/null +++ b/target/linux/oxnas/files-4.14/phy/phy-oxnas-pcie.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Daniel Golle + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADDR_VAL(val) ((val) & 0xFFFF) +#define DATA_VAL(val) ((val) & 0xFFFF) + +#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 + +enum { + HCSL_BIAS_ON = BIT(0), + HCSL_PCIE_EN = BIT(1), + HCSL_PCIEA_EN = BIT(2), + HCSL_PCIEB_EN = BIT(3), +}; + +enum { + /* pcie phy reg offset */ + PHY_ADDR = 0, + PHY_DATA = 4, + /* phy data reg bits */ + READ_EN = BIT(16), + WRITE_EN = BIT(17), + CAP_DATA = BIT(18), +}; + +struct oxnas_pcie_phy { + struct device *dev; + void __iomem *membase; + const struct phy_ops *ops; + struct regmap *sys_ctrl; +}; + +static int oxnas_pcie_phy_init(struct phy *phy) +{ + struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); + struct reset_control *rstc; + int ret; + + /* generate clocks from HCSL buffers, shared parts */ + regmap_write(pciephy->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, HCSL_BIAS_ON|HCSL_PCIE_EN); + + /* Ensure PCIe PHY is properly reset */ + rstc = reset_control_get(pciephy->dev, "phy"); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + } else { + ret = reset_control_reset(rstc); + reset_control_put(rstc); + } + + if (ret) { + dev_err(pciephy->dev, "phy reset failed %d\n", ret); + return ret; + } + + return 0; +} + +static int oxnas_pcie_phy_power_on(struct phy *phy) +{ + struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); + + /* Enable PCIe Pre-Emphasis: What these value means? */ + writel(ADDR_VAL(0x0014), pciephy->membase + PHY_ADDR); + writel(DATA_VAL(0xce10) | CAP_DATA, pciephy->membase + PHY_DATA); + writel(DATA_VAL(0xce10) | WRITE_EN, pciephy->membase + PHY_DATA); + + writel(ADDR_VAL(0x2004), pciephy->membase + PHY_ADDR); + writel(DATA_VAL(0x82c7) | CAP_DATA, pciephy->membase + PHY_DATA); + writel(DATA_VAL(0x82c7) | WRITE_EN, pciephy->membase + PHY_DATA); + + return 0; +} + +static const struct phy_ops ops = { + .init = oxnas_pcie_phy_init, + .power_on = oxnas_pcie_phy_power_on, + .owner = THIS_MODULE, +}; + +static int oxnas_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct oxnas_pcie_phy *pciephy; + struct regmap *sys_ctrl; + void __iomem *membase; + + membase = of_iomap(np, 0); + if (IS_ERR(membase)) + return PTR_ERR(membase); + + sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); + if (IS_ERR(sys_ctrl)) { + dev_err(dev, "Cannot find OX820 SYSCRTL\n"); + return PTR_ERR(sys_ctrl); + } + + pciephy = devm_kzalloc(dev, sizeof(*pciephy), GFP_KERNEL); + if (!pciephy) + return -ENOMEM; + + pciephy->sys_ctrl = sys_ctrl; + pciephy->membase = membase; + pciephy->dev = dev; + pciephy->ops = &ops; + + generic_phy = devm_phy_create(dev, dev->of_node, pciephy->ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, pciephy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id oxnas_pcie_phy_id_table[] = { + { .compatible = "oxsemi,ox820-pcie-phy" }, + { }, +}; + +static struct platform_driver oxnas_pcie_phy_driver = { + .probe = oxnas_pcie_phy_probe, + .driver = { + .name = "ox820-pcie-phy", + .of_match_table = oxnas_pcie_phy_id_table, + }, +}; + +builtin_platform_driver(oxnas_pcie_phy_driver); diff --git a/target/linux/oxnas/files-5.4/drivers/pci/controller/pcie-oxnas.c b/target/linux/oxnas/files-5.4/drivers/pci/controller/pcie-oxnas.c new file mode 100644 index 0000000000..f2145e31b5 --- /dev/null +++ b/target/linux/oxnas/files-5.4/drivers/pci/controller/pcie-oxnas.c @@ -0,0 +1,674 @@ +/* + * PCIe driver for PLX NAS782X SoCs + * + * Author: Ma Haijun + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" + +#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 + +static inline void oxnas_register_clear_mask(void __iomem *p, unsigned mask) +{ + u32 val = readl_relaxed(p); + + val &= ~mask; + writel_relaxed(val, p); +} + +static inline void oxnas_register_set_mask(void __iomem *p, unsigned mask) +{ + u32 val = readl_relaxed(p); + + val |= mask; + writel_relaxed(val, p); +} + +static inline void oxnas_register_value_mask(void __iomem *p, + unsigned mask, unsigned new_value) +{ + /* TODO sanity check mask & new_value = new_value */ + u32 val = readl_relaxed(p); + + val &= ~mask; + val |= new_value; + writel_relaxed(val, p); +} + +#define VERSION_ID_MAGIC 0x082510b5 +#define LINK_UP_TIMEOUT_SECONDS 1 +#define NUM_CONTROLLERS 1 + +enum { + PCIE_DEVICE_TYPE_MASK = 0x0F, + PCIE_DEVICE_TYPE_ENDPOINT = 0, + PCIE_DEVICE_TYPE_LEGACY_ENDPOINT = 1, + PCIE_DEVICE_TYPE_ROOT = 4, + + PCIE_LTSSM = BIT(4), + PCIE_READY_ENTR_L23 = BIT(9), + PCIE_LINK_UP = BIT(11), + PCIE_OBTRANS = BIT(12), +}; + +/* core config registers */ +enum { + PCI_CONFIG_VERSION_DEVICEID = 0, + PCI_CONFIG_COMMAND_STATUS = 4, +}; + +/* inbound config registers */ +enum { + IB_ADDR_XLATE_ENABLE = 0xFC, + + /* bits */ + ENABLE_IN_ADDR_TRANS = BIT(0), +}; + +/* outbound config registers, offset relative to PCIE_POM0_MEM_ADDR */ +enum { + PCIE_POM0_MEM_ADDR = 0, + PCIE_POM1_MEM_ADDR = 4, + PCIE_IN0_MEM_ADDR = 8, + PCIE_IN1_MEM_ADDR = 12, + PCIE_IN_IO_ADDR = 16, + PCIE_IN_CFG0_ADDR = 20, + PCIE_IN_CFG1_ADDR = 24, + PCIE_IN_MSG_ADDR = 28, + PCIE_IN0_MEM_LIMIT = 32, + PCIE_IN1_MEM_LIMIT = 36, + PCIE_IN_IO_LIMIT = 40, + PCIE_IN_CFG0_LIMIT = 44, + PCIE_IN_CFG1_LIMIT = 48, + PCIE_IN_MSG_LIMIT = 52, + PCIE_AHB_SLAVE_CTRL = 56, + + PCIE_SLAVE_BE_SHIFT = 22, +}; + +#define PCIE_SLAVE_BE(val) ((val) << PCIE_SLAVE_BE_SHIFT) +#define PCIE_SLAVE_BE_MASK PCIE_SLAVE_BE(0xF) + +struct oxnas_pcie_shared { + /* seems all access are serialized, no lock required */ + int refcount; +}; + +/* Structure representing one PCIe interfaces */ +struct oxnas_pcie { + void __iomem *cfgbase; + void __iomem *base; + void __iomem *inbound; + struct regmap *sys_ctrl; + unsigned int outbound_offset; + unsigned int pcie_ctrl_offset; + struct phy *phy; + int haslink; + struct platform_device *pdev; + struct resource io; + struct resource cfg; + struct resource pre_mem; /* prefetchable */ + struct resource non_mem; /* non-prefetchable */ + struct resource busn; /* max available bus numbers */ + int card_reset; /* gpio pin, optional */ + unsigned hcsl_en; /* hcsl pci enable bit */ + struct clk *clk; + struct clk *busclk; /* for pcie bus, actually the PLLB */ + void *private_data[1]; + spinlock_t lock; +}; + +static struct oxnas_pcie_shared pcie_shared = { + .refcount = 0, +}; + +static inline struct oxnas_pcie *sys_to_pcie(struct pci_sys_data *sys) +{ + return sys->private_data; +} + + +static inline void set_out_lanes(struct oxnas_pcie *pcie, unsigned lanes) +{ + regmap_update_bits(pcie->sys_ctrl, pcie->outbound_offset + PCIE_AHB_SLAVE_CTRL, + PCIE_SLAVE_BE_MASK, PCIE_SLAVE_BE(lanes)); + wmb(); +} + +static int oxnas_pcie_link_up(struct oxnas_pcie *pcie) +{ + unsigned long end; + unsigned int val; + + /* Poll for PCIE link up */ + end = jiffies + (LINK_UP_TIMEOUT_SECONDS * HZ); + while (!time_after(jiffies, end)) { + regmap_read(pcie->sys_ctrl, pcie->pcie_ctrl_offset, &val); + if (val & PCIE_LINK_UP) + return 1; + } + return 0; +} + +static void oxnas_pcie_setup_hw(struct oxnas_pcie *pcie) +{ + /* We won't have any inbound address translation. This allows PCI + * devices to access anywhere in the AHB address map. Might be regarded + * as a bit dangerous, but let's get things working before we worry + * about that + */ + oxnas_register_clear_mask(pcie->inbound + IB_ADDR_XLATE_ENABLE, + ENABLE_IN_ADDR_TRANS); + wmb(); + + /* + * Program outbound translation windows + * + * Outbound window is what is referred to as "PCI client" region in HRM + * + * Could use the larger alternative address space to get >>64M regions + * for graphics cards etc., but will not bother at this point. + * + * IP bug means that AMBA window size must be a power of 2 + * + * Set mem0 window for first 16MB of outbound window non-prefetchable + * Set mem1 window for second 16MB of outbound window prefetchable + * Set io window for next 16MB of outbound window + * Set cfg0 for final 1MB of outbound window + * + * Ignore mem1, cfg1 and msg windows for now as no obvious use cases for + * 820 that would need them + * + * Probably ideally want no offset between mem0 window start as seen by + * ARM and as seen on PCI bus and get Linux to assign memory regions to + * PCI devices using the same "PCI client" region start address as seen + * by ARM + */ + + /* Set PCIeA mem0 region to be 1st 16MB of the 64MB PCIeA window */ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN0_MEM_ADDR, pcie->non_mem.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN0_MEM_LIMIT, pcie->non_mem.end); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_POM0_MEM_ADDR, pcie->non_mem.start); + + /* Set PCIeA mem1 region to be 2nd 16MB of the 64MB PCIeA window */ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN1_MEM_ADDR, pcie->pre_mem.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN1_MEM_LIMIT, pcie->pre_mem.end); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_POM1_MEM_ADDR, pcie->pre_mem.start); + + /* Set PCIeA io to be third 16M region of the 64MB PCIeA window*/ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_IO_ADDR, pcie->io.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_IO_LIMIT, pcie->io.end); + + + /* Set PCIeA cgf0 to be last 16M region of the 64MB PCIeA window*/ + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_CFG0_ADDR, pcie->cfg.start); + regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_CFG0_LIMIT, pcie->cfg.end); + wmb(); + + /* Enable outbound address translation */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, PCIE_OBTRANS, PCIE_OBTRANS); + wmb(); + + /* + * Program PCIe command register for core to: + * enable memory space + * enable bus master + * enable io + */ + writel_relaxed(7, pcie->base + PCI_CONFIG_COMMAND_STATUS); + /* which is which */ + wmb(); +} + +static unsigned oxnas_pcie_cfg_to_offset( + struct pci_sys_data *sys, + unsigned char bus_number, + unsigned int devfn, + int where) +{ + unsigned int function = PCI_FUNC(devfn); + unsigned int slot = PCI_SLOT(devfn); + unsigned char bus_number_offset; + + bus_number_offset = bus_number - sys->busnr; + + /* + * We'll assume for now that the offset, function, slot, bus encoding + * should map onto linear, contiguous addresses in PCIe config space, + * albeit that the majority will be unused as only slot 0 is valid for + * any PCIe bus and most devices have only function 0 + * + * Could be that PCIe in fact works by not encoding the slot number into + * the config space address as it's known that only slot 0 is valid. + * We'll have to experiment if/when we get a PCIe switch connected to + * the PCIe host + */ + return (bus_number_offset << 20) | (slot << 15) | (function << 12) | + (where & ~3); +} + +/* PCI configuration space write function */ +static int oxnas_pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + unsigned long flags; + struct oxnas_pcie *pcie = sys_to_pcie(bus->sysdata); + unsigned offset; + u32 value; + u32 lanes; + + /* Only a single device per bus for PCIe point-to-point links */ + if (PCI_SLOT(devfn) > 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (!pcie->haslink) + return PCIBIOS_DEVICE_NOT_FOUND; + + offset = oxnas_pcie_cfg_to_offset(bus->sysdata, bus->number, devfn, + where); + + value = val << (8 * (where & 3)); + lanes = (0xf >> (4-size)) << (where & 3); + /* it race with mem and io write, but the possibility is low, normally + * all config writes happens at driver initialize stage, wont interleave + * with others. + * and many pcie cards use dword (4bytes) access mem/io access only, + * so not bother to copy that ugly work-around now. */ + spin_lock_irqsave(&pcie->lock, flags); + set_out_lanes(pcie, lanes); + writel_relaxed(value, pcie->cfgbase + offset); + set_out_lanes(pcie, 0xf); + spin_unlock_irqrestore(&pcie->lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + +/* PCI configuration space read function */ +static int oxnas_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 *val) +{ + struct oxnas_pcie *pcie = sys_to_pcie(bus->sysdata); + unsigned offset; + u32 value; + u32 left_bytes, right_bytes; + + /* Only a single device per bus for PCIe point-to-point links */ + if (PCI_SLOT(devfn) > 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (!pcie->haslink) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + offset = oxnas_pcie_cfg_to_offset(bus->sysdata, bus->number, devfn, + where); + value = readl_relaxed(pcie->cfgbase + offset); + left_bytes = where & 3; + right_bytes = 4 - left_bytes - size; + value <<= right_bytes * 8; + value >>= (left_bytes + right_bytes) * 8; + *val = value; + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops oxnas_pcie_ops = { + .read = oxnas_pcie_rd_conf, + .write = oxnas_pcie_wr_conf, +}; + +static int oxnas_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct oxnas_pcie *pcie = sys_to_pcie(sys); + + pci_add_resource_offset(&sys->resources, &pcie->non_mem, sys->mem_offset); + pci_add_resource_offset(&sys->resources, &pcie->pre_mem, sys->mem_offset); + pci_add_resource_offset(&sys->resources, &pcie->io, sys->io_offset); + pci_add_resource(&sys->resources, &pcie->busn); + if (sys->busnr == 0) { /* default one */ + sys->busnr = pcie->busn.start; + } + /* do not use devm_ioremap_resource, it does not like cfg resource */ + pcie->cfgbase = devm_ioremap(&pcie->pdev->dev, pcie->cfg.start, + resource_size(&pcie->cfg)); + if (!pcie->cfgbase) + return -ENOMEM; + + oxnas_pcie_setup_hw(pcie); + + return 1; +} + +static void oxnas_pcie_enable(struct device *dev, struct oxnas_pcie *pcie) +{ + struct hw_pci hw; + int i; + + memset(&hw, 0, sizeof(hw)); + for (i = 0; i < NUM_CONTROLLERS; i++) + pcie->private_data[i] = pcie; + + hw.nr_controllers = NUM_CONTROLLERS; +/* I think use stack pointer is a bad idea though it is valid in this case */ + hw.private_data = pcie->private_data; + hw.setup = oxnas_pcie_setup; + hw.map_irq = of_irq_parse_and_map_pci; + hw.ops = &oxnas_pcie_ops; + + /* pass dev to maintain of tree, interrupt mapping rely on this */ + pci_common_init_dev(dev, &hw); +} + +static int oxnas_pcie_shared_init(struct platform_device *pdev, struct oxnas_pcie *pcie) +{ + if (++pcie_shared.refcount == 1) { + phy_init(pcie->phy); + phy_power_on(pcie->phy); + return 0; + } else { + return 0; + } +} + +#if 0 +/* maybe we will call it when enter low power state */ +static void oxnas_pcie_shared_deinit(struct platform_device *pdev) +{ + if (--pcie_shared.refcount == 0) { + /* no cleanup needed */; + } +} +#endif + +static int +oxnas_pcie_map_registers(struct platform_device *pdev, + struct device_node *np, + struct oxnas_pcie *pcie) +{ + struct resource regs; + int ret = 0; + u32 outbound_ctrl_offset; + u32 pcie_ctrl_offset; + + ret = of_address_to_resource(np, 0, ®s); + if (ret) { + dev_err(&pdev->dev, "failed to parse base register space\n"); + return -EINVAL; + } + + pcie->base = devm_ioremap_resource(&pdev->dev, ®s); + if (!pcie->base) { + dev_err(&pdev->dev, "failed to map base register space\n"); + return -ENOMEM; + } + + ret = of_address_to_resource(np, 1, ®s); + if (ret) { + dev_err(&pdev->dev, "failed to parse inbound register space\n"); + return -EINVAL; + } + + pcie->inbound = devm_ioremap_resource(&pdev->dev, ®s); + if (!pcie->inbound) { + dev_err(&pdev->dev, "failed to map inbound register space\n"); + return -ENOMEM; + } + + pcie->phy = devm_of_phy_get(&pdev->dev, np, NULL); + if (IS_ERR(pcie->phy)) { + if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) { + dev_err(&pdev->dev, "failed to probe phy\n"); + return PTR_ERR(pcie->phy); + } + dev_warn(&pdev->dev, "phy not attached\n"); + pcie->phy = NULL; + } + + if (of_property_read_u32(np, "plxtech,pcie-outbound-offset", + &outbound_ctrl_offset)) { + dev_err(&pdev->dev, "failed to parse outbound register offset\n"); + return -EINVAL; + } + pcie->outbound_offset = outbound_ctrl_offset; + + if (of_property_read_u32(np, "plxtech,pcie-ctrl-offset", + &pcie_ctrl_offset)) { + dev_err(&pdev->dev, "failed to parse pcie-ctrl register offset\n"); + return -EINVAL; + } + pcie->pcie_ctrl_offset = pcie_ctrl_offset; + + return 0; +} + +static int oxnas_pcie_init_res(struct platform_device *pdev, + struct oxnas_pcie *pcie, + struct device_node *np) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + int ret; + + if (of_pci_range_parser_init(&parser, np)) + return -EINVAL; + + /* Get the I/O and memory ranges from DT */ + for_each_of_pci_range(&parser, &range) { + + unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; + if (restype == IORESOURCE_IO) { + of_pci_range_to_resource(&range, np, &pcie->io); + pcie->io.name = "I/O"; + } + if (restype == IORESOURCE_MEM) { + if (range.flags & IORESOURCE_PREFETCH) { + of_pci_range_to_resource(&range, np, &pcie->pre_mem); + pcie->pre_mem.name = "PRE MEM"; + } else { + of_pci_range_to_resource(&range, np, &pcie->non_mem); + pcie->non_mem.name = "NON MEM"; + } + + } + if (restype == 0) + of_pci_range_to_resource(&range, np, &pcie->cfg); + } + + /* Get the bus range */ + ret = of_pci_parse_bus_range(np, &pcie->busn); + + if (ret) { + dev_err(&pdev->dev, "failed to parse bus-range property: %d\n", + ret); + return ret; + } + + pcie->card_reset = of_get_gpio(np, 0); + if (pcie->card_reset < 0) + dev_info(&pdev->dev, "card reset gpio pin not exists\n"); + + if (of_property_read_u32(np, "plxtech,pcie-hcsl-bit", &pcie->hcsl_en)) + return -EINVAL; + + pcie->clk = of_clk_get_by_name(np, "pcie"); + if (IS_ERR(pcie->clk)) { + return PTR_ERR(pcie->clk); + } + + pcie->busclk = of_clk_get_by_name(np, "busclk"); + if (IS_ERR(pcie->busclk)) { + clk_put(pcie->clk); + return PTR_ERR(pcie->busclk); + } + + return 0; +} + +static void oxnas_pcie_init_hw(struct platform_device *pdev, + struct oxnas_pcie *pcie) +{ + u32 version_id; + int ret; + + clk_prepare_enable(pcie->busclk); + + /* reset PCIe cards use hard-wired gpio pin */ + if (pcie->card_reset >= 0 && + !gpio_direction_output(pcie->card_reset, 0)) { + wmb(); + mdelay(10); + /* must tri-state the pin to pull it up */ + gpio_direction_input(pcie->card_reset); + wmb(); + mdelay(100); + } + + /* ToDo: use phy power-on port... */ + regmap_update_bits(pcie->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, + BIT(pcie->hcsl_en), BIT(pcie->hcsl_en)); + + /* core */ + ret = device_reset(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "core reset failed %d\n", ret); + return; + } + + /* Start PCIe core clocks */ + clk_prepare_enable(pcie->clk); + + version_id = readl_relaxed(pcie->base + PCI_CONFIG_VERSION_DEVICEID); + dev_info(&pdev->dev, "PCIe version/deviceID 0x%x\n", version_id); + + if (version_id != VERSION_ID_MAGIC) { + dev_info(&pdev->dev, "PCIe controller not found\n"); + pcie->haslink = 0; + return; + } + + /* allow entry to L23 state */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, + PCIE_READY_ENTR_L23, PCIE_READY_ENTR_L23); + + /* Set PCIe core into RootCore mode */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, + PCIE_DEVICE_TYPE_MASK, PCIE_DEVICE_TYPE_ROOT); + wmb(); + + /* Bring up the PCI core */ + regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, + PCIE_LTSSM, PCIE_LTSSM); + wmb(); +} + +static int oxnas_pcie_probe(struct platform_device *pdev) +{ + struct oxnas_pcie *pcie; + struct device_node *np = pdev->dev.of_node; + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(struct oxnas_pcie), + GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + pcie->haslink = 1; + spin_lock_init(&pcie->lock); + + pcie->sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); + if (IS_ERR(pcie->sys_ctrl)) + return PTR_ERR(pcie->sys_ctrl); + + ret = oxnas_pcie_init_res(pdev, pcie, np); + if (ret) + return ret; + if (pcie->card_reset >= 0) { + ret = gpio_request_one(pcie->card_reset, GPIOF_DIR_IN, + dev_name(&pdev->dev)); + if (ret) { + dev_err(&pdev->dev, "cannot request gpio pin %d\n", + pcie->card_reset); + return ret; + } + } + + ret = oxnas_pcie_map_registers(pdev, np, pcie); + if (ret) { + dev_err(&pdev->dev, "cannot map registers\n"); + goto err_free_gpio; + } + + ret = oxnas_pcie_shared_init(pdev, pcie); + if (ret) + goto err_free_gpio; + + /* if hw not found, haslink cleared */ + oxnas_pcie_init_hw(pdev, pcie); + + if (pcie->haslink && oxnas_pcie_link_up(pcie)) { + pcie->haslink = 1; + dev_info(&pdev->dev, "link up\n"); + } else { + pcie->haslink = 0; + dev_info(&pdev->dev, "link down\n"); + } + /* should we register our controller even when pcie->haslink is 0 ? */ + /* register the controller with framework */ + oxnas_pcie_enable(&pdev->dev, pcie); + + return 0; + +err_free_gpio: + if (pcie->card_reset) + gpio_free(pcie->card_reset); + + return ret; +} + +static const struct of_device_id oxnas_pcie_of_match_table[] = { + { .compatible = "plxtech,nas782x-pcie", }, + {}, +}; + +static struct platform_driver oxnas_pcie_driver = { + .driver = { + .name = "oxnas-pcie", + .suppress_bind_attrs = true, + .of_match_table = oxnas_pcie_of_match_table, + }, + .probe = oxnas_pcie_probe, +}; + +builtin_platform_driver(oxnas_pcie_driver); diff --git a/target/linux/oxnas/files-5.4/drivers/phy/phy-oxnas-pcie.c b/target/linux/oxnas/files-5.4/drivers/phy/phy-oxnas-pcie.c new file mode 100644 index 0000000000..6ded8b2cf6 --- /dev/null +++ b/target/linux/oxnas/files-5.4/drivers/phy/phy-oxnas-pcie.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Daniel Golle + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADDR_VAL(val) ((val) & 0xFFFF) +#define DATA_VAL(val) ((val) & 0xFFFF) + +#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 + +enum { + HCSL_BIAS_ON = BIT(0), + HCSL_PCIE_EN = BIT(1), + HCSL_PCIEA_EN = BIT(2), + HCSL_PCIEB_EN = BIT(3), +}; + +enum { + /* pcie phy reg offset */ + PHY_ADDR = 0, + PHY_DATA = 4, + /* phy data reg bits */ + READ_EN = BIT(16), + WRITE_EN = BIT(17), + CAP_DATA = BIT(18), +}; + +struct oxnas_pcie_phy { + struct device *dev; + void __iomem *membase; + const struct phy_ops *ops; + struct regmap *sys_ctrl; + struct reset_control *rstc; +}; + +static int oxnas_pcie_phy_init(struct phy *phy) +{ + struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); + int ret; + + /* generate clocks from HCSL buffers, shared parts */ + regmap_write(pciephy->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, HCSL_BIAS_ON|HCSL_PCIE_EN); + + /* Ensure PCIe PHY is properly reset */ + ret = reset_control_reset(pciephy->rstc); + + if (ret) { + dev_err(pciephy->dev, "phy reset failed %d\n", ret); + return ret; + } + + return 0; +} + +static int oxnas_pcie_phy_power_on(struct phy *phy) +{ + struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); + + /* Enable PCIe Pre-Emphasis: What these value means? */ + writel(ADDR_VAL(0x0014), pciephy->membase + PHY_ADDR); + writel(DATA_VAL(0xce10) | CAP_DATA, pciephy->membase + PHY_DATA); + writel(DATA_VAL(0xce10) | WRITE_EN, pciephy->membase + PHY_DATA); + + writel(ADDR_VAL(0x2004), pciephy->membase + PHY_ADDR); + writel(DATA_VAL(0x82c7) | CAP_DATA, pciephy->membase + PHY_DATA); + writel(DATA_VAL(0x82c7) | WRITE_EN, pciephy->membase + PHY_DATA); + + return 0; +} + +static const struct phy_ops ops = { + .init = oxnas_pcie_phy_init, + .power_on = oxnas_pcie_phy_power_on, + .owner = THIS_MODULE, +}; + +static int oxnas_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct oxnas_pcie_phy *pciephy; + struct regmap *sys_ctrl; + struct reset_control *rstc; + void __iomem *membase; + + membase = of_iomap(np, 0); + if (IS_ERR(membase)) + return PTR_ERR(membase); + + sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); + if (IS_ERR(sys_ctrl)) + return PTR_ERR(sys_ctrl); + + rstc = devm_reset_control_get_shared(dev, "phy"); + if (IS_ERR(rstc)) + return PTR_ERR(rstc); + + pciephy = devm_kzalloc(dev, sizeof(*pciephy), GFP_KERNEL); + if (!pciephy) + return -ENOMEM; + + pciephy->sys_ctrl = sys_ctrl; + pciephy->rstc = rstc; + pciephy->membase = membase; + pciephy->dev = dev; + pciephy->ops = &ops; + + generic_phy = devm_phy_create(dev, dev->of_node, pciephy->ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, pciephy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id oxnas_pcie_phy_id_table[] = { + { .compatible = "oxsemi,ox820-pcie-phy" }, + { }, +}; + +static struct platform_driver oxnas_pcie_phy_driver = { + .probe = oxnas_pcie_phy_probe, + .driver = { + .name = "ox820-pcie-phy", + .of_match_table = oxnas_pcie_phy_id_table, + }, +}; + +builtin_platform_driver(oxnas_pcie_phy_driver); diff --git a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-shuttle-kd20.dts b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-shuttle-kd20.dts index a8a2ec3bc7..61ea2b57a7 100644 --- a/target/linux/oxnas/files/arch/arm/boot/dts/ox820-shuttle-kd20.dts +++ b/target/linux/oxnas/files/arch/arm/boot/dts/ox820-shuttle-kd20.dts @@ -2,6 +2,7 @@ #include "ox820.dtsi" +#include #include / { @@ -31,7 +32,8 @@ i2c-gpio { compatible = "i2c-gpio"; - gpios = <&gpio1 9 0 &gpio1 10 0>; + sda-gpios = <&gpio1 9 (GPIO_ACTIVE_HIGH|GPIO_LINE_OPEN_DRAIN)>; + sck-gpios = <&gpio1 10 (GPIO_ACTIVE_HIGH|GPIO_LINE_OPEN_DRAIN)>; i2c-gpio,delay-us = <10>; #address-cells = <1>; #size-cells = <0>; @@ -49,22 +51,22 @@ power { label = "power"; - gpios = <&gpio0 10 1>; + gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; linux,code = ; }; reset { label = "reset"; - gpios = <&gpio0 11 1>; + gpios = <&gpio0 11 GPIO_ACTIVE_LOW>; linux,code = ; }; eject1 { label = "eject1"; - gpios = <&gpio0 5 1>; + gpios = <&gpio0 5 GPIO_ACTIVE_LOW>; linux,code = ; }; eject2 { label = "eject2"; - gpios = <&gpio0 6 1>; + gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; linux,code = <162>; }; }; @@ -73,33 +75,33 @@ compatible = "gpio-leds"; led_status: status { label = "kd20:blue:status"; - gpios = <&gpio1 16 0>; + gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>; }; led_warn: status2 { label = "kd20:red:status"; - gpios = <&gpio1 17 0>; + gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; }; hdd1blue { label = "kd20:blue:hdd1"; - gpios = <&gpio0 27 0>; + gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>; linux,default-trigger = "ata1"; }; hdd1red { label = "kd20:red:hdd1"; - gpios = <&gpio1 4 0>; + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; }; hdd2blue { label = "kd20:blue:hdd2"; - gpios = <&gpio1 6 0>; + gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; linux,default-trigger = "ata2"; }; hdd2red { label = "kd20:red:hdd2"; - gpios = <&gpio1 7 0>; + gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; }; usb { label = "kd20:blue:usb"; - gpios = <&gpio1 8 0>; + gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; trigger-sources = <&ehci_port1>, <&ehci_port2>; linux,default-trigger = "usbport"; }; @@ -107,19 +109,19 @@ beeper: beeper { compatible = "gpio-beeper"; - gpios = <&gpio1 11 0>; + gpios = <&gpio1 11 GPIO_ACTIVE_HIGH>; }; gpio-fan { compatible = "gpio-fan"; - gpios = <&gpio0 2 1>; + gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; gpio-fan,speed-map = <0 0 3000 1>; }; gpio-poweroff { compatible = "gpio-poweroff"; - gpios = <&gpio0 9 0>; + gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; }; }; diff --git a/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c b/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c deleted file mode 100644 index 1dd13f9364..0000000000 --- a/target/linux/oxnas/files/drivers/pci/host/pcie-oxnas.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * PCIe driver for PLX NAS782X SoCs - * - * Author: Ma Haijun - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 - -static inline void oxnas_register_clear_mask(void __iomem *p, unsigned mask) -{ - u32 val = readl_relaxed(p); - - val &= ~mask; - writel_relaxed(val, p); -} - -static inline void oxnas_register_set_mask(void __iomem *p, unsigned mask) -{ - u32 val = readl_relaxed(p); - - val |= mask; - writel_relaxed(val, p); -} - -static inline void oxnas_register_value_mask(void __iomem *p, - unsigned mask, unsigned new_value) -{ - /* TODO sanity check mask & new_value = new_value */ - u32 val = readl_relaxed(p); - - val &= ~mask; - val |= new_value; - writel_relaxed(val, p); -} - -#define VERSION_ID_MAGIC 0x082510b5 -#define LINK_UP_TIMEOUT_SECONDS 1 -#define NUM_CONTROLLERS 1 - -enum { - PCIE_DEVICE_TYPE_MASK = 0x0F, - PCIE_DEVICE_TYPE_ENDPOINT = 0, - PCIE_DEVICE_TYPE_LEGACY_ENDPOINT = 1, - PCIE_DEVICE_TYPE_ROOT = 4, - - PCIE_LTSSM = BIT(4), - PCIE_READY_ENTR_L23 = BIT(9), - PCIE_LINK_UP = BIT(11), - PCIE_OBTRANS = BIT(12), -}; - -/* core config registers */ -enum { - PCI_CONFIG_VERSION_DEVICEID = 0, - PCI_CONFIG_COMMAND_STATUS = 4, -}; - -/* inbound config registers */ -enum { - IB_ADDR_XLATE_ENABLE = 0xFC, - - /* bits */ - ENABLE_IN_ADDR_TRANS = BIT(0), -}; - -/* outbound config registers, offset relative to PCIE_POM0_MEM_ADDR */ -enum { - PCIE_POM0_MEM_ADDR = 0, - PCIE_POM1_MEM_ADDR = 4, - PCIE_IN0_MEM_ADDR = 8, - PCIE_IN1_MEM_ADDR = 12, - PCIE_IN_IO_ADDR = 16, - PCIE_IN_CFG0_ADDR = 20, - PCIE_IN_CFG1_ADDR = 24, - PCIE_IN_MSG_ADDR = 28, - PCIE_IN0_MEM_LIMIT = 32, - PCIE_IN1_MEM_LIMIT = 36, - PCIE_IN_IO_LIMIT = 40, - PCIE_IN_CFG0_LIMIT = 44, - PCIE_IN_CFG1_LIMIT = 48, - PCIE_IN_MSG_LIMIT = 52, - PCIE_AHB_SLAVE_CTRL = 56, - - PCIE_SLAVE_BE_SHIFT = 22, -}; - -#define PCIE_SLAVE_BE(val) ((val) << PCIE_SLAVE_BE_SHIFT) -#define PCIE_SLAVE_BE_MASK PCIE_SLAVE_BE(0xF) - -struct oxnas_pcie_shared { - /* seems all access are serialized, no lock required */ - int refcount; -}; - -/* Structure representing one PCIe interfaces */ -struct oxnas_pcie { - void __iomem *cfgbase; - void __iomem *base; - void __iomem *inbound; - struct regmap *sys_ctrl; - unsigned int outbound_offset; - unsigned int pcie_ctrl_offset; - struct phy *phy; - int haslink; - struct platform_device *pdev; - struct resource io; - struct resource cfg; - struct resource pre_mem; /* prefetchable */ - struct resource non_mem; /* non-prefetchable */ - struct resource busn; /* max available bus numbers */ - int card_reset; /* gpio pin, optional */ - unsigned hcsl_en; /* hcsl pci enable bit */ - struct clk *clk; - struct clk *busclk; /* for pcie bus, actually the PLLB */ - void *private_data[1]; - spinlock_t lock; -}; - -static struct oxnas_pcie_shared pcie_shared = { - .refcount = 0, -}; - -static inline struct oxnas_pcie *sys_to_pcie(struct pci_sys_data *sys) -{ - return sys->private_data; -} - - -static inline void set_out_lanes(struct oxnas_pcie *pcie, unsigned lanes) -{ - regmap_update_bits(pcie->sys_ctrl, pcie->outbound_offset + PCIE_AHB_SLAVE_CTRL, - PCIE_SLAVE_BE_MASK, PCIE_SLAVE_BE(lanes)); - wmb(); -} - -static int oxnas_pcie_link_up(struct oxnas_pcie *pcie) -{ - unsigned long end; - unsigned int val; - - /* Poll for PCIE link up */ - end = jiffies + (LINK_UP_TIMEOUT_SECONDS * HZ); - while (!time_after(jiffies, end)) { - regmap_read(pcie->sys_ctrl, pcie->pcie_ctrl_offset, &val); - if (val & PCIE_LINK_UP) - return 1; - } - return 0; -} - -static void oxnas_pcie_setup_hw(struct oxnas_pcie *pcie) -{ - /* We won't have any inbound address translation. This allows PCI - * devices to access anywhere in the AHB address map. Might be regarded - * as a bit dangerous, but let's get things working before we worry - * about that - */ - oxnas_register_clear_mask(pcie->inbound + IB_ADDR_XLATE_ENABLE, - ENABLE_IN_ADDR_TRANS); - wmb(); - - /* - * Program outbound translation windows - * - * Outbound window is what is referred to as "PCI client" region in HRM - * - * Could use the larger alternative address space to get >>64M regions - * for graphics cards etc., but will not bother at this point. - * - * IP bug means that AMBA window size must be a power of 2 - * - * Set mem0 window for first 16MB of outbound window non-prefetchable - * Set mem1 window for second 16MB of outbound window prefetchable - * Set io window for next 16MB of outbound window - * Set cfg0 for final 1MB of outbound window - * - * Ignore mem1, cfg1 and msg windows for now as no obvious use cases for - * 820 that would need them - * - * Probably ideally want no offset between mem0 window start as seen by - * ARM and as seen on PCI bus and get Linux to assign memory regions to - * PCI devices using the same "PCI client" region start address as seen - * by ARM - */ - - /* Set PCIeA mem0 region to be 1st 16MB of the 64MB PCIeA window */ - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN0_MEM_ADDR, pcie->non_mem.start); - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN0_MEM_LIMIT, pcie->non_mem.end); - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_POM0_MEM_ADDR, pcie->non_mem.start); - - /* Set PCIeA mem1 region to be 2nd 16MB of the 64MB PCIeA window */ - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN1_MEM_ADDR, pcie->pre_mem.start); - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN1_MEM_LIMIT, pcie->pre_mem.end); - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_POM1_MEM_ADDR, pcie->pre_mem.start); - - /* Set PCIeA io to be third 16M region of the 64MB PCIeA window*/ - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_IO_ADDR, pcie->io.start); - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_IO_LIMIT, pcie->io.end); - - - /* Set PCIeA cgf0 to be last 16M region of the 64MB PCIeA window*/ - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_CFG0_ADDR, pcie->cfg.start); - regmap_write(pcie->sys_ctrl, pcie->outbound_offset + PCIE_IN_CFG0_LIMIT, pcie->cfg.end); - wmb(); - - /* Enable outbound address translation */ - regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, PCIE_OBTRANS, PCIE_OBTRANS); - wmb(); - - /* - * Program PCIe command register for core to: - * enable memory space - * enable bus master - * enable io - */ - writel_relaxed(7, pcie->base + PCI_CONFIG_COMMAND_STATUS); - /* which is which */ - wmb(); -} - -static unsigned oxnas_pcie_cfg_to_offset( - struct pci_sys_data *sys, - unsigned char bus_number, - unsigned int devfn, - int where) -{ - unsigned int function = PCI_FUNC(devfn); - unsigned int slot = PCI_SLOT(devfn); - unsigned char bus_number_offset; - - bus_number_offset = bus_number - sys->busnr; - - /* - * We'll assume for now that the offset, function, slot, bus encoding - * should map onto linear, contiguous addresses in PCIe config space, - * albeit that the majority will be unused as only slot 0 is valid for - * any PCIe bus and most devices have only function 0 - * - * Could be that PCIe in fact works by not encoding the slot number into - * the config space address as it's known that only slot 0 is valid. - * We'll have to experiment if/when we get a PCIe switch connected to - * the PCIe host - */ - return (bus_number_offset << 20) | (slot << 15) | (function << 12) | - (where & ~3); -} - -/* PCI configuration space write function */ -static int oxnas_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) -{ - unsigned long flags; - struct oxnas_pcie *pcie = sys_to_pcie(bus->sysdata); - unsigned offset; - u32 value; - u32 lanes; - - /* Only a single device per bus for PCIe point-to-point links */ - if (PCI_SLOT(devfn) > 0) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (!pcie->haslink) - return PCIBIOS_DEVICE_NOT_FOUND; - - offset = oxnas_pcie_cfg_to_offset(bus->sysdata, bus->number, devfn, - where); - - value = val << (8 * (where & 3)); - lanes = (0xf >> (4-size)) << (where & 3); - /* it race with mem and io write, but the possibility is low, normally - * all config writes happens at driver initialize stage, wont interleave - * with others. - * and many pcie cards use dword (4bytes) access mem/io access only, - * so not bother to copy that ugly work-around now. */ - spin_lock_irqsave(&pcie->lock, flags); - set_out_lanes(pcie, lanes); - writel_relaxed(value, pcie->cfgbase + offset); - set_out_lanes(pcie, 0xf); - spin_unlock_irqrestore(&pcie->lock, flags); - - return PCIBIOS_SUCCESSFUL; -} - -/* PCI configuration space read function */ -static int oxnas_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) -{ - struct oxnas_pcie *pcie = sys_to_pcie(bus->sysdata); - unsigned offset; - u32 value; - u32 left_bytes, right_bytes; - - /* Only a single device per bus for PCIe point-to-point links */ - if (PCI_SLOT(devfn) > 0) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - if (!pcie->haslink) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - offset = oxnas_pcie_cfg_to_offset(bus->sysdata, bus->number, devfn, - where); - value = readl_relaxed(pcie->cfgbase + offset); - left_bytes = where & 3; - right_bytes = 4 - left_bytes - size; - value <<= right_bytes * 8; - value >>= (left_bytes + right_bytes) * 8; - *val = value; - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops oxnas_pcie_ops = { - .read = oxnas_pcie_rd_conf, - .write = oxnas_pcie_wr_conf, -}; - -static int oxnas_pcie_setup(int nr, struct pci_sys_data *sys) -{ - struct oxnas_pcie *pcie = sys_to_pcie(sys); - - pci_add_resource_offset(&sys->resources, &pcie->non_mem, sys->mem_offset); - pci_add_resource_offset(&sys->resources, &pcie->pre_mem, sys->mem_offset); - pci_add_resource_offset(&sys->resources, &pcie->io, sys->io_offset); - pci_add_resource(&sys->resources, &pcie->busn); - if (sys->busnr == 0) { /* default one */ - sys->busnr = pcie->busn.start; - } - /* do not use devm_ioremap_resource, it does not like cfg resource */ - pcie->cfgbase = devm_ioremap(&pcie->pdev->dev, pcie->cfg.start, - resource_size(&pcie->cfg)); - if (!pcie->cfgbase) - return -ENOMEM; - - oxnas_pcie_setup_hw(pcie); - - return 1; -} - -static void oxnas_pcie_enable(struct device *dev, struct oxnas_pcie *pcie) -{ - struct hw_pci hw; - int i; - - memset(&hw, 0, sizeof(hw)); - for (i = 0; i < NUM_CONTROLLERS; i++) - pcie->private_data[i] = pcie; - - hw.nr_controllers = NUM_CONTROLLERS; -/* I think use stack pointer is a bad idea though it is valid in this case */ - hw.private_data = pcie->private_data; - hw.setup = oxnas_pcie_setup; - hw.map_irq = of_irq_parse_and_map_pci; - hw.ops = &oxnas_pcie_ops; - - /* pass dev to maintain of tree, interrupt mapping rely on this */ - pci_common_init_dev(dev, &hw); -} - -static int oxnas_pcie_shared_init(struct platform_device *pdev, struct oxnas_pcie *pcie) -{ - if (++pcie_shared.refcount == 1) { - phy_init(pcie->phy); - phy_power_on(pcie->phy); - return 0; - } else { - return 0; - } -} - -#if 0 -/* maybe we will call it when enter low power state */ -static void oxnas_pcie_shared_deinit(struct platform_device *pdev) -{ - if (--pcie_shared.refcount == 0) { - /* no cleanup needed */; - } -} -#endif - -static int -oxnas_pcie_map_registers(struct platform_device *pdev, - struct device_node *np, - struct oxnas_pcie *pcie) -{ - struct resource regs; - int ret = 0; - u32 outbound_ctrl_offset; - u32 pcie_ctrl_offset; - - ret = of_address_to_resource(np, 0, ®s); - if (ret) { - dev_err(&pdev->dev, "failed to parse base register space\n"); - return -EINVAL; - } - - pcie->base = devm_ioremap_resource(&pdev->dev, ®s); - if (!pcie->base) { - dev_err(&pdev->dev, "failed to map base register space\n"); - return -ENOMEM; - } - - ret = of_address_to_resource(np, 1, ®s); - if (ret) { - dev_err(&pdev->dev, "failed to parse inbound register space\n"); - return -EINVAL; - } - - pcie->inbound = devm_ioremap_resource(&pdev->dev, ®s); - if (!pcie->inbound) { - dev_err(&pdev->dev, "failed to map inbound register space\n"); - return -ENOMEM; - } - - pcie->phy = devm_of_phy_get(&pdev->dev, np, NULL); - if (IS_ERR(pcie->phy)) { - if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) { - dev_err(&pdev->dev, "failed to probe phy\n"); - return PTR_ERR(pcie->phy); - } - dev_warn(&pdev->dev, "phy not attached\n"); - pcie->phy = NULL; - } - - if (of_property_read_u32(np, "plxtech,pcie-outbound-offset", - &outbound_ctrl_offset)) { - dev_err(&pdev->dev, "failed to parse outbound register offset\n"); - return -EINVAL; - } - pcie->outbound_offset = outbound_ctrl_offset; - - if (of_property_read_u32(np, "plxtech,pcie-ctrl-offset", - &pcie_ctrl_offset)) { - dev_err(&pdev->dev, "failed to parse pcie-ctrl register offset\n"); - return -EINVAL; - } - pcie->pcie_ctrl_offset = pcie_ctrl_offset; - - return 0; -} - -static int oxnas_pcie_init_res(struct platform_device *pdev, - struct oxnas_pcie *pcie, - struct device_node *np) -{ - struct of_pci_range range; - struct of_pci_range_parser parser; - int ret; - - if (of_pci_range_parser_init(&parser, np)) - return -EINVAL; - - /* Get the I/O and memory ranges from DT */ - for_each_of_pci_range(&parser, &range) { - - unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; - if (restype == IORESOURCE_IO) { - of_pci_range_to_resource(&range, np, &pcie->io); - pcie->io.name = "I/O"; - } - if (restype == IORESOURCE_MEM) { - if (range.flags & IORESOURCE_PREFETCH) { - of_pci_range_to_resource(&range, np, &pcie->pre_mem); - pcie->pre_mem.name = "PRE MEM"; - } else { - of_pci_range_to_resource(&range, np, &pcie->non_mem); - pcie->non_mem.name = "NON MEM"; - } - - } - if (restype == 0) - of_pci_range_to_resource(&range, np, &pcie->cfg); - } - - /* Get the bus range */ - ret = of_pci_parse_bus_range(np, &pcie->busn); - - if (ret) { - dev_err(&pdev->dev, "failed to parse bus-range property: %d\n", - ret); - return ret; - } - - pcie->card_reset = of_get_gpio(np, 0); - if (pcie->card_reset < 0) - dev_info(&pdev->dev, "card reset gpio pin not exists\n"); - - if (of_property_read_u32(np, "plxtech,pcie-hcsl-bit", &pcie->hcsl_en)) - return -EINVAL; - - pcie->clk = of_clk_get_by_name(np, "pcie"); - if (IS_ERR(pcie->clk)) { - return PTR_ERR(pcie->clk); - } - - pcie->busclk = of_clk_get_by_name(np, "busclk"); - if (IS_ERR(pcie->busclk)) { - clk_put(pcie->clk); - return PTR_ERR(pcie->busclk); - } - - return 0; -} - -static void oxnas_pcie_init_hw(struct platform_device *pdev, - struct oxnas_pcie *pcie) -{ - u32 version_id; - int ret; - - clk_prepare_enable(pcie->busclk); - - /* reset PCIe cards use hard-wired gpio pin */ - if (pcie->card_reset >= 0 && - !gpio_direction_output(pcie->card_reset, 0)) { - wmb(); - mdelay(10); - /* must tri-state the pin to pull it up */ - gpio_direction_input(pcie->card_reset); - wmb(); - mdelay(100); - } - - /* ToDo: use phy power-on port... */ - regmap_update_bits(pcie->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, - BIT(pcie->hcsl_en), BIT(pcie->hcsl_en)); - - /* core */ - ret = device_reset(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "core reset failed %d\n", ret); - return; - } - - /* Start PCIe core clocks */ - clk_prepare_enable(pcie->clk); - - version_id = readl_relaxed(pcie->base + PCI_CONFIG_VERSION_DEVICEID); - dev_info(&pdev->dev, "PCIe version/deviceID 0x%x\n", version_id); - - if (version_id != VERSION_ID_MAGIC) { - dev_info(&pdev->dev, "PCIe controller not found\n"); - pcie->haslink = 0; - return; - } - - /* allow entry to L23 state */ - regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, - PCIE_READY_ENTR_L23, PCIE_READY_ENTR_L23); - - /* Set PCIe core into RootCore mode */ - regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, - PCIE_DEVICE_TYPE_MASK, PCIE_DEVICE_TYPE_ROOT); - wmb(); - - /* Bring up the PCI core */ - regmap_write_bits(pcie->sys_ctrl, pcie->pcie_ctrl_offset, - PCIE_LTSSM, PCIE_LTSSM); - wmb(); -} - -static int oxnas_pcie_probe(struct platform_device *pdev) -{ - struct oxnas_pcie *pcie; - struct device_node *np = pdev->dev.of_node; - int ret; - - pcie = devm_kzalloc(&pdev->dev, sizeof(struct oxnas_pcie), - GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - pcie->pdev = pdev; - pcie->haslink = 1; - spin_lock_init(&pcie->lock); - - pcie->sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); - if (IS_ERR(pcie->sys_ctrl)) - return PTR_ERR(pcie->sys_ctrl); - - ret = oxnas_pcie_init_res(pdev, pcie, np); - if (ret) - return ret; - if (pcie->card_reset >= 0) { - ret = gpio_request_one(pcie->card_reset, GPIOF_DIR_IN, - dev_name(&pdev->dev)); - if (ret) { - dev_err(&pdev->dev, "cannot request gpio pin %d\n", - pcie->card_reset); - return ret; - } - } - - ret = oxnas_pcie_map_registers(pdev, np, pcie); - if (ret) { - dev_err(&pdev->dev, "cannot map registers\n"); - goto err_free_gpio; - } - - ret = oxnas_pcie_shared_init(pdev, pcie); - if (ret) - goto err_free_gpio; - - /* if hw not found, haslink cleared */ - oxnas_pcie_init_hw(pdev, pcie); - - if (pcie->haslink && oxnas_pcie_link_up(pcie)) { - pcie->haslink = 1; - dev_info(&pdev->dev, "link up\n"); - } else { - pcie->haslink = 0; - dev_info(&pdev->dev, "link down\n"); - } - /* should we register our controller even when pcie->haslink is 0 ? */ - /* register the controller with framework */ - oxnas_pcie_enable(&pdev->dev, pcie); - - return 0; - -err_free_gpio: - if (pcie->card_reset) - gpio_free(pcie->card_reset); - - return ret; -} - -static const struct of_device_id oxnas_pcie_of_match_table[] = { - { .compatible = "plxtech,nas782x-pcie", }, - {}, -}; - -static struct platform_driver oxnas_pcie_driver = { - .driver = { - .name = "oxnas-pcie", - .suppress_bind_attrs = true, - .of_match_table = oxnas_pcie_of_match_table, - }, - .probe = oxnas_pcie_probe, -}; - -builtin_platform_driver(oxnas_pcie_driver); diff --git a/target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c b/target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c deleted file mode 100644 index 676a5f9332..0000000000 --- a/target/linux/oxnas/files/drivers/phy/phy-oxnas-pcie.c +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2019 Daniel Golle - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ADDR_VAL(val) ((val) & 0xFFFF) -#define DATA_VAL(val) ((val) & 0xFFFF) - -#define SYS_CTRL_HCSL_CTRL_REGOFFSET 0x114 - -enum { - HCSL_BIAS_ON = BIT(0), - HCSL_PCIE_EN = BIT(1), - HCSL_PCIEA_EN = BIT(2), - HCSL_PCIEB_EN = BIT(3), -}; - -enum { - /* pcie phy reg offset */ - PHY_ADDR = 0, - PHY_DATA = 4, - /* phy data reg bits */ - READ_EN = BIT(16), - WRITE_EN = BIT(17), - CAP_DATA = BIT(18), -}; - -struct oxnas_pcie_phy { - struct device *dev; - void __iomem *membase; - const struct phy_ops *ops; - struct regmap *sys_ctrl; -}; - -static int oxnas_pcie_phy_init(struct phy *phy) -{ - struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); - struct reset_control *rstc; - int ret; - - /* generate clocks from HCSL buffers, shared parts */ - regmap_write(pciephy->sys_ctrl, SYS_CTRL_HCSL_CTRL_REGOFFSET, HCSL_BIAS_ON|HCSL_PCIE_EN); - - /* Ensure PCIe PHY is properly reset */ - rstc = reset_control_get(pciephy->dev, "phy"); - if (IS_ERR(rstc)) { - ret = PTR_ERR(rstc); - } else { - ret = reset_control_reset(rstc); - reset_control_put(rstc); - } - - if (ret) { - dev_err(pciephy->dev, "phy reset failed %d\n", ret); - return ret; - } - - return 0; -} - -static int oxnas_pcie_phy_power_on(struct phy *phy) -{ - struct oxnas_pcie_phy *pciephy = phy_get_drvdata(phy); - - /* Enable PCIe Pre-Emphasis: What these value means? */ - writel(ADDR_VAL(0x0014), pciephy->membase + PHY_ADDR); - writel(DATA_VAL(0xce10) | CAP_DATA, pciephy->membase + PHY_DATA); - writel(DATA_VAL(0xce10) | WRITE_EN, pciephy->membase + PHY_DATA); - - writel(ADDR_VAL(0x2004), pciephy->membase + PHY_ADDR); - writel(DATA_VAL(0x82c7) | CAP_DATA, pciephy->membase + PHY_DATA); - writel(DATA_VAL(0x82c7) | WRITE_EN, pciephy->membase + PHY_DATA); - - return 0; -} - -static const struct phy_ops ops = { - .init = oxnas_pcie_phy_init, - .power_on = oxnas_pcie_phy_power_on, - .owner = THIS_MODULE, -}; - -static int oxnas_pcie_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; - struct phy *generic_phy; - struct phy_provider *phy_provider; - struct oxnas_pcie_phy *pciephy; - struct regmap *sys_ctrl; - void __iomem *membase; - - membase = of_iomap(np, 0); - if (IS_ERR(membase)) - return PTR_ERR(membase); - - sys_ctrl = syscon_regmap_lookup_by_compatible("oxsemi,ox820-sys-ctrl"); - if (IS_ERR(sys_ctrl)) { - dev_err(dev, "Cannot find OX820 SYSCRTL\n"); - return PTR_ERR(sys_ctrl); - } - - pciephy = devm_kzalloc(dev, sizeof(*pciephy), GFP_KERNEL); - if (!pciephy) - return -ENOMEM; - - pciephy->sys_ctrl = sys_ctrl; - pciephy->membase = membase; - pciephy->dev = dev; - pciephy->ops = &ops; - - generic_phy = devm_phy_create(dev, dev->of_node, pciephy->ops); - if (IS_ERR(generic_phy)) { - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(generic_phy); - } - - phy_set_drvdata(generic_phy, pciephy); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id oxnas_pcie_phy_id_table[] = { - { .compatible = "oxsemi,ox820-pcie-phy" }, - { }, -}; - -static struct platform_driver oxnas_pcie_phy_driver = { - .probe = oxnas_pcie_phy_probe, - .driver = { - .name = "ox820-pcie-phy", - .of_match_table = oxnas_pcie_phy_id_table, - }, -}; - -builtin_platform_driver(oxnas_pcie_phy_driver); diff --git a/target/linux/oxnas/ox820/config-default b/target/linux/oxnas/ox820/config-default index 47cd89d0d2..ed757730ee 100644 --- a/target/linux/oxnas/ox820/config-default +++ b/target/linux/oxnas/ox820/config-default @@ -88,6 +88,7 @@ CONFIG_PCI_MSI_IRQ_DOMAIN=y # CONFIG_MTD_CFI is not set CONFIG_MTD_CMDLINE_PARTS=y # CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND=y CONFIG_MTD_NAND_ECC=y CONFIG_MTD_NAND_OXNAS=y diff --git a/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch b/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch index a6b7137584..2a4e4536dc 100644 --- a/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch +++ b/target/linux/oxnas/patches-4.14/340-oxnas-pcie.patch @@ -1,5 +1,5 @@ ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig @@ -220,4 +220,9 @@ config VMD To compile this driver as a module, choose M here: the module will be called vmd. @@ -10,8 +10,8 @@ + select PCIEPORTBUS + endmenu ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PCIE_ALTERA) += pcie-altera obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o diff --git a/target/linux/oxnas/patches-5.4/010-add-console-to-pogoplogv3-bootargs.patch b/target/linux/oxnas/patches-5.4/010-add-console-to-pogoplogv3-bootargs.patch new file mode 100644 index 0000000000..7fdc302a3f --- /dev/null +++ b/target/linux/oxnas/patches-5.4/010-add-console-to-pogoplogv3-bootargs.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts ++++ b/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts +@@ -14,7 +14,7 @@ + compatible = "cloudengines,pogoplugv3", "oxsemi,ox820"; + + chosen { +- bootargs = "earlyprintk"; ++ bootargs = "earlyprintk console=ttyS0,115200"; + stdout-path = "serial0:115200n8"; + }; + diff --git a/target/linux/oxnas/patches-5.4/020-nand-partitions-on-pogoplug-v3.patch b/target/linux/oxnas/patches-5.4/020-nand-partitions-on-pogoplug-v3.patch new file mode 100644 index 0000000000..4adf8bcd01 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/020-nand-partitions-on-pogoplug-v3.patch @@ -0,0 +1,32 @@ +--- a/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts ++++ b/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts +@@ -73,11 +73,27 @@ + nand-ecc-algo = "hamming"; + + partition@0 { +- label = "boot"; +- reg = <0x00000000 0x00e00000>; ++ label = "stage1"; ++ reg = <0x00000000 0x00040000>; + read-only; + }; + ++ partition@40000 { ++ label = "u-boot"; ++ reg = <0x00040000 0x00380000>; ++ read-only; ++ }; ++ ++ partition@3c0000 { ++ label = "u-boot-env"; ++ reg = <0x003c0000 0x00080000>; ++ }; ++ ++ partition@440000 { ++ label = "kernel"; ++ reg = <0x00440000 0x009c0000>; ++ }; ++ + partition@e00000 { + label = "ubi"; + reg = <0x00e00000 0x07200000>; diff --git a/target/linux/oxnas/patches-5.4/030-led-aliases-on-pogoplug-v3.patch b/target/linux/oxnas/patches-5.4/030-led-aliases-on-pogoplug-v3.patch new file mode 100644 index 0000000000..8fb1d23088 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/030-led-aliases-on-pogoplug-v3.patch @@ -0,0 +1,34 @@ +--- a/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts ++++ b/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts +@@ -27,24 +27,28 @@ + serial0 = &uart0; + gpio0 = &gpio0; + gpio1 = &gpio1; ++ led-boot = &led_status; ++ led-failsafe = &led_warn; ++ led-running = &led_act; ++ led-upgrade = &led_warn; + }; + + leds { + compatible = "gpio-leds"; + +- blue { ++ led_status: blue { + label = "pogoplug:blue"; + gpios = <&gpio0 2 0>; + default-state = "keep"; + }; + +- orange { ++ led_warn: orange { + label = "pogoplug:orange"; + gpios = <&gpio1 16 1>; + default-state = "keep"; + }; + +- green { ++ led_act: green { + label = "pogoplug:green"; + gpios = <&gpio1 17 1>; + default-state = "keep"; diff --git a/target/linux/oxnas/patches-5.4/040-pogoplug-series-3-compatible-string.patch b/target/linux/oxnas/patches-5.4/040-pogoplug-series-3-compatible-string.patch new file mode 100644 index 0000000000..625d660e0e --- /dev/null +++ b/target/linux/oxnas/patches-5.4/040-pogoplug-series-3-compatible-string.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts ++++ b/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts +@@ -11,7 +11,7 @@ + / { + model = "Cloud Engines PogoPlug Series 3"; + +- compatible = "cloudengines,pogoplugv3", "oxsemi,ox820"; ++ compatible = "cloudengines,pogoplug-series-3", "cloudengines,pogoplugv3", "oxsemi,ox820"; + + chosen { + bootargs = "earlyprintk console=ttyS0,115200"; diff --git a/target/linux/oxnas/patches-5.4/050-ox820-remove-left-overs.patch b/target/linux/oxnas/patches-5.4/050-ox820-remove-left-overs.patch new file mode 100644 index 0000000000..e30f328792 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/050-ox820-remove-left-overs.patch @@ -0,0 +1,63 @@ +From 552ed4955c1fee1109bf5ba137dc35a411a1448c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Fri, 1 Jun 2018 02:41:15 +0200 +Subject: [PATCH] arm: ox820: remove left-overs + +Signed-off-by: Daniel Golle +--- + drivers/clk/clk-oxnas.c | 2 -- + include/dt-bindings/clock/oxsemi,ox820.h | 32 +++++++++++------------- + 2 files changed, 14 insertions(+), 20 deletions(-) + +--- a/drivers/clk/clk-oxnas.c ++++ b/drivers/clk/clk-oxnas.c +@@ -29,8 +29,6 @@ struct oxnas_stdclk_data { + struct clk_hw_onecell_data *onecell_data; + struct clk_oxnas_gate **gates; + unsigned int ngates; +- struct clk_oxnas_pll **plls; +- unsigned int nplls; + }; + + /* Regmap offsets */ +--- a/include/dt-bindings/clock/oxsemi,ox820.h ++++ b/include/dt-bindings/clock/oxsemi,ox820.h +@@ -6,24 +6,20 @@ + #ifndef DT_CLOCK_OXSEMI_OX820_H + #define DT_CLOCK_OXSEMI_OX820_H + +-/* PLLs */ +-#define CLK_820_PLLA 0 +-#define CLK_820_PLLB 1 +- + /* Gate Clocks */ +-#define CLK_820_LEON 2 +-#define CLK_820_DMA_SGDMA 3 +-#define CLK_820_CIPHER 4 +-#define CLK_820_SD 5 +-#define CLK_820_SATA 6 +-#define CLK_820_AUDIO 7 +-#define CLK_820_USBMPH 8 +-#define CLK_820_ETHA 9 +-#define CLK_820_PCIEA 10 +-#define CLK_820_NAND 11 +-#define CLK_820_PCIEB 12 +-#define CLK_820_ETHB 13 +-#define CLK_820_REF600 14 +-#define CLK_820_USBDEV 15 ++#define CLK_820_LEON 0 ++#define CLK_820_DMA_SGDMA 1 ++#define CLK_820_CIPHER 2 ++#define CLK_820_SD 3 ++#define CLK_820_SATA 4 ++#define CLK_820_AUDIO 5 ++#define CLK_820_USBMPH 6 ++#define CLK_820_ETHA 7 ++#define CLK_820_PCIEA 8 ++#define CLK_820_NAND 9 ++#define CLK_820_PCIEB 10 ++#define CLK_820_ETHB 11 ++#define CLK_820_REF600 12 ++#define CLK_820_USBDEV 13 + + #endif /* DT_CLOCK_OXSEMI_OX820_H */ diff --git a/target/linux/oxnas/patches-5.4/100-oxnas-clk-plla-pllb.patch b/target/linux/oxnas/patches-5.4/100-oxnas-clk-plla-pllb.patch new file mode 100644 index 0000000000..29a9036498 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/100-oxnas-clk-plla-pllb.patch @@ -0,0 +1,273 @@ +--- a/drivers/clk/clk-oxnas.c ++++ b/drivers/clk/clk-oxnas.c +@@ -5,19 +5,42 @@ + * Copyright (C) 2016 Neil Armstrong + */ + ++#include ++#include + #include + #include + #include ++#include + #include + #include + #include + #include + #include + #include ++#include + + #include + #include + ++#define REF300_DIV_INT_SHIFT 8 ++#define REF300_DIV_FRAC_SHIFT 0 ++#define REF300_DIV_INT(val) ((val) << REF300_DIV_INT_SHIFT) ++#define REF300_DIV_FRAC(val) ((val) << REF300_DIV_FRAC_SHIFT) ++ ++#define PLLB_BYPASS 1 ++#define PLLB_ENSAT 3 ++#define PLLB_OUTDIV 4 ++#define PLLB_REFDIV 8 ++#define PLLB_DIV_INT_SHIFT 8 ++#define PLLB_DIV_FRAC_SHIFT 0 ++#define PLLB_DIV_INT(val) ((val) << PLLB_DIV_INT_SHIFT) ++#define PLLB_DIV_FRAC(val) ((val) << PLLB_DIV_FRAC_SHIFT) ++ ++#define PLLA_REFDIV_MASK 0x3F ++#define PLLA_REFDIV_SHIFT 8 ++#define PLLA_OUTDIV_MASK 0x7 ++#define PLLA_OUTDIV_SHIFT 4 ++ + /* Standard regmap gate clocks */ + struct clk_oxnas_gate { + struct clk_hw hw; +@@ -36,6 +59,135 @@ struct oxnas_stdclk_data { + #define CLK_SET_REGOFFSET 0x2c + #define CLK_CLR_REGOFFSET 0x30 + ++#define PLLA_CTRL0_REGOFFSET 0x1f0 ++#define PLLA_CTRL1_REGOFFSET 0x1f4 ++#define PLLB_CTRL0_REGOFFSET 0x1001f0 ++#define MHZ (1000 * 1000) ++ ++struct clk_oxnas_pll { ++ struct clk_hw hw; ++ struct device_node *devnode; ++ struct reset_control *rstc; ++ struct regmap *syscon; ++}; ++ ++#define to_clk_oxnas_pll(_hw) container_of(_hw, struct clk_oxnas_pll, hw) ++ ++static unsigned long plla_clk_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_oxnas_pll *plla = to_clk_oxnas_pll(hw); ++ unsigned long fin = parent_rate; ++ unsigned long refdiv, outdiv; ++ unsigned int pll0, fbdiv; ++ ++ BUG_ON(regmap_read(plla->syscon, PLLA_CTRL0_REGOFFSET, &pll0)); ++ ++ refdiv = (pll0 >> PLLA_REFDIV_SHIFT) & PLLA_REFDIV_MASK; ++ refdiv += 1; ++ outdiv = (pll0 >> PLLA_OUTDIV_SHIFT) & PLLA_OUTDIV_MASK; ++ outdiv += 1; ++ ++ BUG_ON(regmap_read(plla->syscon, PLLA_CTRL1_REGOFFSET, &fbdiv)); ++ /* seems we will not be here when pll is bypassed, so ignore this ++ * case */ ++ ++ return fin / MHZ * fbdiv / (refdiv * outdiv) / 32768 * MHZ; ++} ++ ++static const char *pll_clk_parents[] = { ++ "oscillator", ++}; ++ ++static struct clk_ops plla_ops = { ++ .recalc_rate = plla_clk_recalc_rate, ++}; ++ ++static struct clk_init_data clk_plla_init = { ++ .name = "plla", ++ .ops = &plla_ops, ++ .parent_names = pll_clk_parents, ++ .num_parents = ARRAY_SIZE(pll_clk_parents), ++}; ++ ++static int pllb_clk_is_prepared(struct clk_hw *hw) ++{ ++ struct clk_oxnas_pll *pllb = to_clk_oxnas_pll(hw); ++ ++ return !!pllb->rstc; ++} ++ ++static int pllb_clk_prepare(struct clk_hw *hw) ++{ ++ struct clk_oxnas_pll *pllb = to_clk_oxnas_pll(hw); ++ ++ pllb->rstc = of_reset_control_get(pllb->devnode, NULL); ++ ++ return IS_ERR(pllb->rstc) ? PTR_ERR(pllb->rstc) : 0; ++} ++ ++static void pllb_clk_unprepare(struct clk_hw *hw) ++{ ++ struct clk_oxnas_pll *pllb = to_clk_oxnas_pll(hw); ++ ++ BUG_ON(IS_ERR(pllb->rstc)); ++ ++ reset_control_put(pllb->rstc); ++ pllb->rstc = NULL; ++} ++ ++static int pllb_clk_enable(struct clk_hw *hw) ++{ ++ struct clk_oxnas_pll *pllb = to_clk_oxnas_pll(hw); ++ ++ BUG_ON(IS_ERR(pllb->rstc)); ++ ++ /* put PLL into bypass */ ++ regmap_update_bits(pllb->syscon, PLLB_CTRL0_REGOFFSET, BIT(PLLB_BYPASS), BIT(PLLB_BYPASS)); ++ wmb(); ++ udelay(10); ++ reset_control_assert(pllb->rstc); ++ udelay(10); ++ /* set PLL B control information */ ++ regmap_write_bits(pllb->syscon, PLLB_CTRL0_REGOFFSET, 0xffff, ++ (1 << PLLB_ENSAT) | (1 << PLLB_OUTDIV) | (2 << PLLB_REFDIV)); ++ reset_control_deassert(pllb->rstc); ++ udelay(100); ++ regmap_update_bits(pllb->syscon, PLLB_CTRL0_REGOFFSET, BIT(PLLB_BYPASS), 0); ++ ++ return 0; ++} ++ ++static void pllb_clk_disable(struct clk_hw *hw) ++{ ++ struct clk_oxnas_pll *pllb = to_clk_oxnas_pll(hw); ++ ++ BUG_ON(IS_ERR(pllb->rstc)); ++ ++ /* put PLL into bypass */ ++ regmap_update_bits(pllb->syscon, PLLB_CTRL0_REGOFFSET, BIT(PLLB_BYPASS), BIT(PLLB_BYPASS)); ++ ++ wmb(); ++ udelay(10); ++ ++ reset_control_assert(pllb->rstc); ++} ++ ++static struct clk_ops pllb_ops = { ++ .prepare = pllb_clk_prepare, ++ .unprepare = pllb_clk_unprepare, ++ .is_prepared = pllb_clk_is_prepared, ++ .enable = pllb_clk_enable, ++ .disable = pllb_clk_disable, ++}; ++ ++static struct clk_init_data clk_pllb_init = { ++ .name = "pllb", ++ .ops = &pllb_ops, ++ .parent_names = pll_clk_parents, ++ .num_parents = ARRAY_SIZE(pll_clk_parents), ++}; ++ + static inline struct clk_oxnas_gate *to_clk_oxnas_gate(struct clk_hw *hw) + { + return container_of(hw, struct clk_oxnas_gate, hw); +@@ -249,3 +401,42 @@ static struct platform_driver oxnas_stdc + }, + }; + builtin_platform_driver(oxnas_stdclk_driver); ++ ++void __init oxnas_init_plla(struct device_node *np) ++{ ++ struct clk *clk; ++ struct clk_oxnas_pll *plla; ++ ++ plla = kmalloc(sizeof(*plla), GFP_KERNEL); ++ BUG_ON(!plla); ++ ++ plla->syscon = syscon_node_to_regmap(of_get_parent(np)); ++ plla->hw.init = &clk_plla_init; ++ plla->devnode = np; ++ plla->rstc = NULL; ++ clk = clk_register(NULL, &plla->hw); ++ BUG_ON(IS_ERR(clk)); ++ /* mark it as enabled */ ++ clk_prepare_enable(clk); ++ of_clk_add_provider(np, of_clk_src_simple_get, clk); ++} ++CLK_OF_DECLARE(oxnas_plla, "plxtech,nas782x-plla", oxnas_init_plla); ++ ++void __init oxnas_init_pllb(struct device_node *np) ++{ ++ struct clk *clk; ++ struct clk_oxnas_pll *pllb; ++ ++ pllb = kmalloc(sizeof(*pllb), GFP_KERNEL); ++ BUG_ON(!pllb); ++ ++ pllb->syscon = syscon_node_to_regmap(of_get_parent(np)); ++ pllb->hw.init = &clk_pllb_init; ++ pllb->devnode = np; ++ pllb->rstc = NULL; ++ ++ clk = clk_register(NULL, &pllb->hw); ++ BUG_ON(IS_ERR(clk)); ++ of_clk_add_provider(np, of_clk_src_simple_get, clk); ++} ++CLK_OF_DECLARE(oxnas_pllb, "plxtech,nas782x-pllb", oxnas_init_pllb); +--- a/arch/arm/boot/dts/ox820.dtsi ++++ b/arch/arm/boot/dts/ox820.dtsi +@@ -61,12 +61,6 @@ + clocks = <&osc>; + }; + +- plla: plla { +- compatible = "fixed-clock"; +- #clock-cells = <0>; +- clock-frequency = <850000000>; +- }; +- + armclk: armclk { + compatible = "fixed-factor-clock"; + #clock-cells = <0>; +@@ -266,6 +260,19 @@ + compatible = "oxsemi,ox820-stdclk", "oxsemi,ox810se-stdclk"; + #clock-cells = <1>; + }; ++ ++ plla: plla { ++ compatible = "plxtech,nas782x-plla"; ++ #clock-cells = <0>; ++ clocks = <&osc>; ++ }; ++ ++ pllb: pllb { ++ compatible = "plxtech,nas782x-pllb"; ++ #clock-cells = <0>; ++ clocks = <&osc>; ++ resets = <&reset RESET_PLLB>; ++ }; + }; + }; + +@@ -287,6 +294,13 @@ + clocks = <&armclk>; + }; + ++ watchdog@620 { ++ compatible = "mpcore_wdt"; ++ reg = <0x620 0x20>; ++ interrupts = ; ++ clocks = <&armclk>; ++ }; ++ + gic: gic@1000 { + compatible = "arm,arm11mp-gic"; + interrupt-controller; diff --git a/target/linux/oxnas/patches-5.4/150-oxnas-restart.patch b/target/linux/oxnas/patches-5.4/150-oxnas-restart.patch new file mode 100644 index 0000000000..79fbd9cdbe --- /dev/null +++ b/target/linux/oxnas/patches-5.4/150-oxnas-restart.patch @@ -0,0 +1,25 @@ +--- a/drivers/power/reset/Kconfig ++++ b/drivers/power/reset/Kconfig +@@ -123,6 +123,12 @@ config POWER_RESET_OCELOT_RESET + help + This driver supports restart for Microsemi Ocelot SoC. + ++config POWER_RESET_OXNAS ++ bool "OXNAS SoC restart driver" ++ depends on ARCH_OXNAS ++ help ++ Restart support for OXNAS boards. ++ + config POWER_RESET_PIIX4_POWEROFF + tristate "Intel PIIX4 power-off driver" + depends on PCI +--- a/drivers/power/reset/Makefile ++++ b/drivers/power/reset/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-p + obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o + obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o + obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o ++obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o + obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o + obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o + obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o diff --git a/target/linux/oxnas/patches-5.4/320-oxnas-phy-pcie.patch b/target/linux/oxnas/patches-5.4/320-oxnas-phy-pcie.patch new file mode 100644 index 0000000000..cf272f76b7 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/320-oxnas-phy-pcie.patch @@ -0,0 +1,44 @@ +--- a/arch/arm/boot/dts/ox820.dtsi ++++ b/arch/arm/boot/dts/ox820.dtsi +@@ -247,6 +247,15 @@ + }; + }; + ++ pcie_phy: pcie-phy@a00000 { ++ compatible = "oxsemi,ox820-pcie-phy"; ++ reg = <0xa00000 0x10>; ++ #phy-cells = <0>; ++ resets = <&reset RESET_PCIEPHY>; ++ reset-names = "phy"; ++ status = "disabled"; ++ }; ++ + sys: sys-ctrl@e00000 { + compatible = "oxsemi,ox820-sys-ctrl", "syscon", "simple-mfd"; + reg = <0xe00000 0x200000>; +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -35,6 +35,13 @@ config PHY_LPC18XX_USB_OTG + This driver is need for USB0 support on LPC18xx/43xx and takes + care of enabling and clock setup. + ++config PHY_OXNAS ++ tristate "Oxford Semi. OX820 PCI-E PHY support" ++ depends on HAS_IOMEM && OF && (ARM || COMPILE_TEST) ++ select GENERIC_PHY ++ help ++ This option enables support for OXNAS OX820 SoC PCIE PHY. ++ + config PHY_PISTACHIO_USB + tristate "IMG Pistachio USB2.0 PHY driver" + depends on MACH_PISTACHIO +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_GENERIC_PHY) += phy-core.o + obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o + obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o ++obj-$(CONFIG_PHY_OXNAS) += phy-oxnas-pcie.o + obj-$(CONFIG_PHY_XGENE) += phy-xgene.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o + obj-$(CONFIG_ARCH_SUNXI) += allwinner/ diff --git a/target/linux/oxnas/patches-5.4/340-oxnas-pcie.patch b/target/linux/oxnas/patches-5.4/340-oxnas-pcie.patch new file mode 100644 index 0000000000..19065233cf --- /dev/null +++ b/target/linux/oxnas/patches-5.4/340-oxnas-pcie.patch @@ -0,0 +1,122 @@ +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -48,6 +48,11 @@ config PCIE_CADENCE_EP + endpoint mode. This PCIe controller may be embedded into many + different vendors SoCs. + ++config PCIE_OXNAS ++ bool "PLX Oxnas PCIe controller" ++ depends on ARCH_OXNAS ++ select PCIEPORTBUS ++ + endmenu + + config PCIE_XILINX_NWL +--- a/drivers/pci/controller/Makefile ++++ b/drivers/pci/controller/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-r + obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o + obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o + obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o ++obj-$(CONFIG_PCIE_OXNAS) += pcie-oxnas.o + obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o + obj-$(CONFIG_VMD) += vmd.o + # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW +--- a/arch/arm/boot/dts/ox820.dtsi ++++ b/arch/arm/boot/dts/ox820.dtsi +@@ -289,7 +289,7 @@ + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; +- ranges = <0 0x47000000 0x1000000>; ++ ranges = <0 0x47000000 0x2000>; + + scu: scu@0 { + compatible = "arm,arm11mp-scu"; +@@ -318,5 +318,86 @@ + <0x100 0x500>; + }; + }; ++ ++ pcie0: pcie-controller@47c00000 { ++ compatible = "plxtech,nas782x-pcie"; ++ device_type = "pci"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ /* flag & space bus address host address size */ ++ ranges = < 0x82000000 0 0x48000000 0x48000000 0 0x2000000 ++ 0xC2000000 0 0x4A000000 0x4A000000 0 0x1E00000 ++ 0x81000000 0 0x4BE00000 0x4BE00000 0 0x0100000 ++ 0x80000000 0 0x4BF00000 0x4BF00000 0 0x0100000>; ++ ++ bus-range = <0x00 0x7f>; ++ ++ /* cfg inbound translator */ ++ reg = <0x47c00000 0x1000>, <0x47d00000 0x100>; ++ ++ phys = <&pcie_phy>; ++ phy-names = "pcie-phy"; ++ ++ #interrupt-cells = <1>; ++ /* wild card mask, match all bus address & interrupt specifier */ ++ /* format: bus address mask, interrupt specifier mask */ ++ /* each bit 1 means need match, 0 means ignored when match */ ++ interrupt-map-mask = <0 0 0 0>; ++ /* format: a list of: bus address, interrupt specifier, ++ * parent interrupt controller & specifier */ ++ interrupt-map = <0 0 0 0 &gic 0 19 0x304>; ++ gpios = <&gpio1 12 0>; ++ clocks = <&stdclk CLK_820_PCIEA>, <&pllb>; ++ clock-names = "pcie", "busclk"; ++ resets = <&reset RESET_PCIEA>; ++ reset-names = "pcie"; ++ ++ plxtech,pcie-hcsl-bit = <2>; ++ plxtech,pcie-ctrl-offset = <0x120>; ++ plxtech,pcie-outbound-offset = <0x138>; ++ status = "disabled"; ++ }; ++ ++ pcie1: pcie-controller@47e00000 { ++ compatible = "plxtech,nas782x-pcie"; ++ device_type = "pci"; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ /* flag & space bus address host address size */ ++ ranges = < 0x82000000 0 0x4C000000 0x4C000000 0 0x2000000 ++ 0xC2000000 0 0x4E000000 0x4E000000 0 0x1E00000 ++ 0x81000000 0 0x4FE00000 0x4FE00000 0 0x0100000 ++ 0x80000000 0 0x4FF00000 0x4FF00000 0 0x0100000>; ++ ++ bus-range = <0x80 0xff>; ++ ++ /* cfg inbound translator */ ++ reg = <0x47e00000 0x1000>, <0x47f00000 0x100>; ++ ++ phys = <&pcie_phy>; ++ phy-names = "pcie-phy"; ++ ++ #interrupt-cells = <1>; ++ /* wild card mask, match all bus address & interrupt specifier */ ++ /* format: bus address mask, interrupt specifier mask */ ++ /* each bit 1 means need match, 0 means ignored when match */ ++ interrupt-map-mask = <0 0 0 0>; ++ /* format: a list of: bus address, interrupt specifier, ++ * parent interrupt controller & specifier */ ++ interrupt-map = <0 0 0 0 &gic 0 20 0x304>; ++ ++ /* gpios = <&gpio1 12 0>; */ ++ clocks = <&stdclk CLK_820_PCIEB>, <&pllb>; ++ clock-names = "pcie", "busclk"; ++ resets = <&reset RESET_PCIEB>; ++ reset-names = "pcie"; ++ ++ plxtech,pcie-hcsl-bit = <3>; ++ plxtech,pcie-ctrl-offset = <0x124>; ++ plxtech,pcie-outbound-offset = <0x174>; ++ status = "disabled"; ++ }; + }; + }; diff --git a/target/linux/oxnas/patches-5.4/500-oxnas-sata.patch b/target/linux/oxnas/patches-5.4/500-oxnas-sata.patch new file mode 100644 index 0000000000..ba0d0d79b4 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/500-oxnas-sata.patch @@ -0,0 +1,49 @@ +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -508,6 +508,13 @@ config SATA_VITESSE + + If unsure, say N. + ++config SATA_OXNAS ++ tristate "PLXTECH NAS782X SATA support" ++ help ++ This option enables support for Nas782x Serial ATA controller. ++ ++ If unsure, say N. ++ + comment "PATA SFF controllers with BMDMA" + + config PATA_ALI +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -46,6 +46,7 @@ obj-$(CONFIG_SATA_SVW) += sata_svw.o + obj-$(CONFIG_SATA_ULI) += sata_uli.o + obj-$(CONFIG_SATA_VIA) += sata_via.o + obj-$(CONFIG_SATA_VITESSE) += sata_vsc.o ++obj-$(CONFIG_SATA_OXNAS) += sata_oxnas.o + + # SFF PATA w/ BMDMA + obj-$(CONFIG_PATA_ALI) += pata_ali.o +--- a/arch/arm/boot/dts/ox820.dtsi ++++ b/arch/arm/boot/dts/ox820.dtsi +@@ -399,5 +399,20 @@ + plxtech,pcie-outbound-offset = <0x174>; + status = "disabled"; + }; ++ ++ sata: sata@45900000 { ++ compatible = "plxtech,nas782x-sata"; ++ /* ports dmactl sgdma */ ++ reg = <0x45900000 0x20000>, <0x459A0000 0x40>, <0x459B0000 0x20>, ++ /* core phy descriptors (optional) */ ++ <0x459E0000 0x2000>, <0x44900000 0x0C>, <0x50000000 0x1000>; ++ interrupts = ; ++ clocks = <&stdclk CLK_820_SATA>; ++ resets = <&reset RESET_SATA>, <&reset RESET_SATA_LINK>, <&reset RESET_SATA_PHY>; ++ reset-names = "sata", "link", "phy"; ++ nr-ports = <1>; ++ status = "disabled"; ++ }; ++ + }; + }; diff --git a/target/linux/oxnas/patches-5.4/510-ox820-libata-leds.patch b/target/linux/oxnas/patches-5.4/510-ox820-libata-leds.patch new file mode 100644 index 0000000000..05ae9ba44b --- /dev/null +++ b/target/linux/oxnas/patches-5.4/510-ox820-libata-leds.patch @@ -0,0 +1,10 @@ +--- a/arch/arm/mach-oxnas/Kconfig ++++ b/arch/arm/mach-oxnas/Kconfig +@@ -2,6 +2,7 @@ + menuconfig ARCH_OXNAS + bool "Oxford Semiconductor OXNAS Family SoCs" + select ARCH_HAS_RESET_CONTROLLER ++ select ARCH_WANT_LIBATA_LEDS + select COMMON_CLK_OXNAS + select GPIOLIB + select MFD_SYSCON diff --git a/target/linux/oxnas/patches-5.4/800-oxnas-ehci.patch b/target/linux/oxnas/patches-5.4/800-oxnas-ehci.patch new file mode 100644 index 0000000000..6c0f1cb109 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/800-oxnas-ehci.patch @@ -0,0 +1,73 @@ +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -321,6 +321,13 @@ config USB_OCTEON_EHCI + USB 2.0 device support. All CN6XXX based chips with USB are + supported. + ++config USB_EHCI_OXNAS ++ tristate "OXNAS EHCI Module" ++ depends on USB_EHCI_HCD && ARCH_OXNAS ++ select USB_EHCI_ROOT_HUB_TT ++ ---help--- ++ Enable support for the OX820 SOC's on-chip EHCI controller. ++ + endif # USB_EHCI_HCD + + config USB_OXU210HP_HCD +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -49,6 +49,7 @@ obj-$(CONFIG_USB_EHCI_HCD_STI) += ehci-s + obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o + obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o + obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o ++obj-$(CONFIG_USB_EHCI_OXNAS) += ehci-oxnas.o + + obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o + obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o +--- a/arch/arm/boot/dts/ox820.dtsi ++++ b/arch/arm/boot/dts/ox820.dtsi +@@ -106,6 +106,31 @@ + status = "disabled"; + }; + ++ ehci: ehci@40200100 { ++ compatible = "plxtech,nas782x-ehci"; ++ reg = <0x40200100 0xf00>; ++ interrupts = ; ++ clocks = <&stdclk CLK_820_USBMPH>, <&pllb>, <&stdclk CLK_820_REF600>; ++ clock-names = "usb", "refsrc", "phyref"; ++ resets = <&reset RESET_USBHS>, <&reset RESET_USBPHYA>, <&reset RESET_USBPHYB>; ++ reset-names = "host", "phya", "phyb"; ++ oxsemi,sys-ctrl = <&sys>; ++ /* Otherwise ref300 is used, which is derived from sata phy ++ * in that case, usb depends on sata initialization */ ++ /* FIXME: how to make this dependency explicit ? */ ++ oxsemi,ehci_use_pllb; ++ status = "disabled"; ++ ++ ehci_port1: port@1 { ++ reg = <1>; ++ #trigger-source-cells = <0>; ++ }; ++ ehci_port2: port@2 { ++ reg = <2>; ++ #trigger-source-cells = <0>; ++ }; ++ }; ++ + apb-bridge@44000000 { + #address-cells = <1>; + #size-cells = <1>; +--- a/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts ++++ b/arch/arm/boot/dts/ox820-cloudengines-pogoplug-series-3.dts +@@ -105,6 +105,10 @@ + }; + }; + ++&ehci { ++ status = "okay"; ++}; ++ + ða { + status = "okay"; + diff --git a/target/linux/oxnas/patches-5.4/996-generic-Mangle-bootloader-s-kernel-arguments.patch b/target/linux/oxnas/patches-5.4/996-generic-Mangle-bootloader-s-kernel-arguments.patch new file mode 100644 index 0000000000..042461e73b --- /dev/null +++ b/target/linux/oxnas/patches-5.4/996-generic-Mangle-bootloader-s-kernel-arguments.patch @@ -0,0 +1,189 @@ +From 71270226b14733a4b1f2cde58ea9265caa50b38d Mon Sep 17 00:00:00 2001 +From: Adrian Panella +Date: Thu, 9 Mar 2017 09:37:17 +0100 +Subject: [PATCH 67/69] generic: Mangle bootloader's kernel arguments + +The command-line arguments provided by the boot loader will be +appended to a new device tree property: bootloader-args. +If there is a property "append-rootblock" in DT under /chosen +and a root= option in bootloaders command line it will be parsed +and added to DT bootargs with the form: XX. +Only command line ATAG will be processed, the rest of the ATAGs +sent by bootloader will be ignored. +This is usefull in dual boot systems, to get the current root partition +without afecting the rest of the system. + +Signed-off-by: Adrian Panella +--- + arch/arm/Kconfig | 11 +++++ + arch/arm/boot/compressed/atags_to_fdt.c | 72 ++++++++++++++++++++++++++++++++- + init/main.c | 16 ++++++++ + 3 files changed, 98 insertions(+), 1 deletion(-) + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1825,6 +1825,17 @@ config ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEN + The command-line arguments provided by the boot loader will be + appended to the the device tree bootargs property. + ++config ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ bool "Append rootblock parsing bootloader's kernel arguments" ++ help ++ The command-line arguments provided by the boot loader will be ++ appended to a new device tree property: bootloader-args. ++ If there is a property "append-rootblock" in DT under /chosen ++ and a root= option in bootloaders command line it will be parsed ++ and added to DT bootargs with the form: XX. ++ Only command line ATAG will be processed, the rest of the ATAGs ++ sent by bootloader will be ignored. ++ + endchoice + + config CMDLINE +--- a/arch/arm/boot/compressed/atags_to_fdt.c ++++ b/arch/arm/boot/compressed/atags_to_fdt.c +@@ -4,6 +4,8 @@ + + #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) + #define do_extend_cmdline 1 ++#elif defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#define do_extend_cmdline 1 + #else + #define do_extend_cmdline 0 + #endif +@@ -67,6 +69,59 @@ static uint32_t get_cell_size(const void + return cell_size; + } + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ ++static char *append_rootblock(char *dest, const char *str, int len, void *fdt) ++{ ++ char *ptr, *end; ++ char *root="root="; ++ int i, l; ++ const char *rootblock; ++ ++ //ARM doesn't have __HAVE_ARCH_STRSTR, so search manually ++ ptr = str - 1; ++ ++ do { ++ //first find an 'r' at the begining or after a space ++ do { ++ ptr++; ++ ptr = strchr(ptr, 'r'); ++ if(!ptr) return dest; ++ ++ } while (ptr != str && *(ptr-1) != ' '); ++ ++ //then check for the rest ++ for(i = 1; i <= 4; i++) ++ if(*(ptr+i) != *(root+i)) break; ++ ++ } while (i != 5); ++ ++ end = strchr(ptr, ' '); ++ end = end ? (end - 1) : (strchr(ptr, 0) - 1); ++ ++ //find partition number (assumes format root=/dev/mtdXX | /dev/mtdblockXX | yy:XX ) ++ for( i = 0; end >= ptr && *end >= '0' && *end <= '9'; end--, i++); ++ ptr = end + 1; ++ ++ /* if append-rootblock property is set use it to append to command line */ ++ rootblock = getprop(fdt, "/chosen", "append-rootblock", &l); ++ if(rootblock != NULL) { ++ if(*dest != ' ') { ++ *dest = ' '; ++ dest++; ++ len++; ++ } ++ if (len + l + i <= COMMAND_LINE_SIZE) { ++ memcpy(dest, rootblock, l); ++ dest += l - 1; ++ memcpy(dest, ptr, i); ++ dest += i; ++ } ++ } ++ return dest; ++} ++#endif ++ + static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) + { + char cmdline[COMMAND_LINE_SIZE]; +@@ -86,12 +141,21 @@ static void merge_fdt_bootargs(void *fdt + + /* and append the ATAG_CMDLINE */ + if (fdt_cmdline) { ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //save original bootloader args ++ //and append ubi.mtd with root partition number to current cmdline ++ setprop_string(fdt, "/chosen", "bootloader-args", fdt_cmdline); ++ ptr = append_rootblock(ptr, fdt_cmdline, len, fdt); ++ ++#else + len = strlen(fdt_cmdline); + if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { + *ptr++ = ' '; + memcpy(ptr, fdt_cmdline, len); + ptr += len; + } ++#endif + } + *ptr = '\0'; + +@@ -166,7 +230,9 @@ int atags_to_fdt(void *atag_list, void * + else + setprop_string(fdt, "/chosen", "bootargs", + atag->u.cmdline.cmdline); +- } else if (atag->hdr.tag == ATAG_MEM) { ++ } ++#ifndef CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE ++ else if (atag->hdr.tag == ATAG_MEM) { + if (memcount >= sizeof(mem_reg_property)/4) + continue; + if (!atag->u.mem.size) +@@ -210,6 +276,10 @@ int atags_to_fdt(void *atag_list, void * + setprop(fdt, "/memory", "reg", mem_reg_property, + 4 * memcount * memsize); + } ++#else ++ ++ } ++#endif + + return fdt_pack(fdt); + } +--- a/init/main.c ++++ b/init/main.c +@@ -103,6 +103,10 @@ + #define CREATE_TRACE_POINTS + #include + ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++#include ++#endif ++ + static int kernel_init(void *); + + extern void init_IRQ(void); +@@ -630,6 +634,18 @@ asmlinkage __visible void __init start_k + page_alloc_init(); + + pr_notice("Kernel command line: %s\n", boot_command_line); ++ ++#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_MANGLE) ++ //Show bootloader's original command line for reference ++ if(of_chosen) { ++ const char *prop = of_get_property(of_chosen, "bootloader-args", NULL); ++ if(prop) ++ pr_notice("Bootloader command line (ignored): %s\n", prop); ++ else ++ pr_notice("Bootloader command line not present\n"); ++ } ++#endif ++ + /* parameters may set static keys */ + jump_label_init(); + parse_early_param(); diff --git a/target/linux/oxnas/patches-5.4/999-libata-hacks.patch b/target/linux/oxnas/patches-5.4/999-libata-hacks.patch new file mode 100644 index 0000000000..7c70a597d0 --- /dev/null +++ b/target/linux/oxnas/patches-5.4/999-libata-hacks.patch @@ -0,0 +1,57 @@ +--- a/drivers/ata/libata-core.c ++++ b/drivers/ata/libata-core.c +@@ -1584,6 +1584,14 @@ unsigned ata_exec_internal_sg(struct ata + return AC_ERR_SYSTEM; + } + ++ if (ap->ops->acquire_hw && !ap->ops->acquire_hw(ap, 0, 0)) { ++ spin_unlock_irqrestore(ap->lock, flags); ++ if (!ap->ops->acquire_hw(ap, 1, (2*HZ))) { ++ return AC_ERR_TIMEOUT; ++ } ++ spin_lock_irqsave(ap->lock, flags); ++ } ++ + /* initialize internal qc */ + qc = __ata_qc_from_tag(ap, ATA_TAG_INTERNAL); + +@@ -5123,6 +5131,9 @@ struct ata_queued_cmd *ata_qc_new_init(s + if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) + return NULL; + ++ if (ap->ops->qc_new && ap->ops->qc_new(ap)) ++ return NULL; ++ + /* libsas case */ + if (ap->flags & ATA_FLAG_SAS_HOST) { + tag = ata_sas_allocate_tag(ap); +@@ -5168,6 +5179,8 @@ void ata_qc_free(struct ata_queued_cmd * + qc->tag = ATA_TAG_POISON; + if (ap->flags & ATA_FLAG_SAS_HOST) + ata_sas_free_tag(tag, ap); ++ if (ap->ops->qc_free) ++ ap->ops->qc_free(qc); + } + } + +--- a/include/linux/libata.h ++++ b/include/linux/libata.h +@@ -905,6 +905,8 @@ struct ata_port_operations { + void (*qc_prep)(struct ata_queued_cmd *qc); + unsigned int (*qc_issue)(struct ata_queued_cmd *qc); + bool (*qc_fill_rtf)(struct ata_queued_cmd *qc); ++ int (*qc_new)(struct ata_port *ap); ++ void (*qc_free)(struct ata_queued_cmd *qc); + + /* + * Configuration and exception handling +@@ -995,6 +997,9 @@ struct ata_port_operations { + void (*phy_reset)(struct ata_port *ap); + void (*eng_timeout)(struct ata_port *ap); + ++ int (*acquire_hw)(struct ata_port *ap, int may_sleep, ++ int timeout_jiffies); ++ + /* + * ->inherits must be the last field and all the preceding + * fields must be pointers.