ramips: add linux 4.4 support, update mt7621 subtarget to 4.4
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 48951
This commit is contained in:
parent
e559de2397
commit
d041e8b44b
70 changed files with 30082 additions and 14 deletions
|
@ -1596,7 +1596,11 @@ endef
|
|||
|
||||
$(eval $(call KernelPackage,usbmon))
|
||||
|
||||
XHCI_FILES := $(wildcard $(patsubst %,$(LINUX_DIR)/drivers/usb/host/%.ko,xhci-hcd xhci-pci xhci-plat-hcd))
|
||||
XHCI_MODULES := xhci-hcd xhci-pci xhci-plat-hcd
|
||||
ifdef CONFIG_TARGET_ramips_mt7621
|
||||
XHCI_MODULES += xhci-mtk
|
||||
endif
|
||||
XHCI_FILES := $(wildcard $(patsubst %,$(LINUX_DIR)/drivers/usb/host/%.ko,$(XHCI_MODULES)))
|
||||
XHCI_AUTOLOAD := $(patsubst $(LINUX_DIR)/drivers/usb/host/%.ko,%,$(XHCI_FILES))
|
||||
|
||||
define KernelPackage/usb3
|
||||
|
@ -1609,6 +1613,7 @@ define KernelPackage/usb3
|
|||
CONFIG_USB_XHCI_PCI \
|
||||
CONFIG_USB_XHCI_PLATFORM \
|
||||
CONFIG_USB_XHCI_MVEBU=y \
|
||||
CONFIG_USB_XHCI_MTK \
|
||||
CONFIG_USB_XHCI_HCD_DEBUGGING=n
|
||||
FILES:= \
|
||||
$(XHCI_FILES)
|
||||
|
|
|
@ -13,7 +13,7 @@ SUBTARGETS:=rt305x mt7620 mt7621 mt7628 mt7688 rt3883 rt288x
|
|||
FEATURES:=squashfs gpio mips16
|
||||
MAINTAINER:=John Crispin <blogic@openwrt.org>
|
||||
|
||||
KERNEL_PATCHVER:=4.3
|
||||
KERNEL_PATCHVER:=4.4
|
||||
|
||||
include $(INCLUDE_DIR)/target.mk
|
||||
DEFAULT_PACKAGES += \
|
||||
|
|
|
@ -248,8 +248,12 @@
|
|||
xhci@1E1C0000 {
|
||||
status = "okay";
|
||||
|
||||
compatible = "xhci-platform";
|
||||
reg = <0x1E1C0000 4000>;
|
||||
compatible = "mediatek,mt8173-xhci";
|
||||
reg = <0x1e1c0000 0x1000
|
||||
0x1e1d0700 0x0100>;
|
||||
|
||||
clocks = <&sysclock>;
|
||||
clock-names = "sys_ck";
|
||||
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <GIC_SHARED 22 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
CONFIG_ARCH_BINFMT_ELF_STATE=y
|
||||
CONFIG_ARCH_CLOCKSOURCE_DATA=y
|
||||
CONFIG_ARCH_DISCARD_MEMBLOCK=y
|
||||
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
|
||||
CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
|
||||
|
@ -11,6 +12,7 @@ CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
|
|||
CONFIG_ARCH_REQUIRE_GPIOLIB=y
|
||||
CONFIG_ARCH_SUPPORTS_UPROBES=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
|
||||
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
|
||||
CONFIG_BOARD_SCACHE=y
|
||||
CONFIG_BOUNCE=y
|
||||
|
@ -20,6 +22,7 @@ CONFIG_CEVT_R4K=y
|
|||
CONFIG_CLKDEV_LOOKUP=y
|
||||
CONFIG_CLKSRC_MIPS_GIC=y
|
||||
CONFIG_CLKSRC_OF=y
|
||||
CONFIG_CLKSRC_PROBE=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_CMDLINE="rootfstype=squashfs,jffs2"
|
||||
CONFIG_CMDLINE_BOOL=y
|
||||
|
@ -63,6 +66,7 @@ CONFIG_GENERIC_IRQ_SHOW=y
|
|||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_TIME_VSYSCALL=y
|
||||
CONFIG_GPIOLIB=y
|
||||
CONFIG_GPIO_DEVRES=y
|
||||
CONFIG_GPIO_MT7621=y
|
||||
|
@ -98,6 +102,7 @@ CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
|||
CONFIG_HAVE_IDE=y
|
||||
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
|
||||
CONFIG_HAVE_KVM=y
|
||||
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
|
||||
CONFIG_HAVE_MACH_CLKDEV=y
|
||||
CONFIG_HAVE_MEMBLOCK=y
|
||||
CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
|
||||
|
@ -119,15 +124,18 @@ CONFIG_IRQ_WORK=y
|
|||
CONFIG_LIBFDT=y
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
# CONFIG_MACH_INGENIC is not set
|
||||
# CONFIG_MACH_LOONGSON32 is not set
|
||||
# CONFIG_MACH_LOONGSON64 is not set
|
||||
CONFIG_MDIO_BOARDINFO=y
|
||||
CONFIG_MIPS=y
|
||||
CONFIG_MIPS_CLOCK_VSYSCALL=y
|
||||
CONFIG_MIPS_CM=y
|
||||
# CONFIG_MIPS_CMDLINE_DTB_EXTEND is not set
|
||||
# CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER is not set
|
||||
CONFIG_MIPS_CMDLINE_FROM_DTB=y
|
||||
CONFIG_MIPS_CPC=y
|
||||
CONFIG_MIPS_CPS=y
|
||||
# CONFIG_MIPS_CPS_NS16550 is not set
|
||||
CONFIG_MIPS_CPU_SCACHE=y
|
||||
# CONFIG_MIPS_ELF_APPENDED_DTB is not set
|
||||
CONFIG_MIPS_GIC=y
|
||||
CONFIG_MIPS_GIC_IPI=y
|
||||
# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set
|
||||
|
@ -184,7 +192,6 @@ CONFIG_OF_MTD=y
|
|||
CONFIG_OF_NET=y
|
||||
CONFIG_OF_PCI=y
|
||||
CONFIG_OF_PCI_IRQ=y
|
||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
||||
CONFIG_PCI=y
|
||||
CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
|
||||
CONFIG_PCI_DOMAINS=y
|
||||
|
@ -200,6 +207,7 @@ CONFIG_POWER_RESET_GPIO=y
|
|||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_RALINK=y
|
||||
# CONFIG_RALINK_WDT is not set
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_RCU_STALL_COMMON=y
|
||||
CONFIG_RESET_CONTROLLER=y
|
||||
CONFIG_RFS_ACCEL=y
|
||||
|
@ -209,12 +217,9 @@ CONFIG_SCHED_HRTICK=y
|
|||
# CONFIG_SCHED_INFO is not set
|
||||
CONFIG_SCHED_SMT=y
|
||||
# CONFIG_SCSI_DMA is not set
|
||||
# CONFIG_SERIAL_8250_FSL is not set
|
||||
CONFIG_SERIAL_8250_NR_UARTS=4
|
||||
CONFIG_SERIAL_OF_PLATFORM=y
|
||||
# CONFIG_SG_SPLIT is not set
|
||||
# CONFIG_SLAB is not set
|
||||
CONFIG_SLUB=y
|
||||
CONFIG_SLUB_CPU_PARTIAL=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_SMP_UP=y
|
||||
# CONFIG_SOC_MT7620 is not set
|
||||
|
@ -227,8 +232,6 @@ CONFIG_SPI_MASTER=y
|
|||
CONFIG_SPI_MT7621=y
|
||||
# CONFIG_SPI_RT2880 is not set
|
||||
CONFIG_SRCU=y
|
||||
CONFIG_STOP_MACHINE=y
|
||||
# CONFIG_SUNXI_SRAM is not set
|
||||
CONFIG_SWCONFIG=y
|
||||
CONFIG_SYNC_R4K=y
|
||||
CONFIG_SYSCTL_EXCEPTION_TRACE=y
|
|
@ -0,0 +1,437 @@
|
|||
From 450b6e8257e22708173d0c1c86d34394fba0c5eb Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:08:31 +0100
|
||||
Subject: [PATCH 01/53] arch: mips: ralink: add mt7621 support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/include/asm/mach-ralink/irq.h | 9 +
|
||||
arch/mips/include/asm/mach-ralink/mt7621.h | 39 ++++
|
||||
arch/mips/kernel/mips-cm.c | 4 +-
|
||||
arch/mips/kernel/vmlinux.lds.S | 1 +
|
||||
arch/mips/ralink/Kconfig | 18 ++
|
||||
arch/mips/ralink/Makefile | 7 +-
|
||||
arch/mips/ralink/Platform | 5 +
|
||||
arch/mips/ralink/irq-gic.c | 268 ++++++++++++++++++++++++++++
|
||||
arch/mips/ralink/malta-amon.c | 81 +++++++++
|
||||
arch/mips/ralink/mt7621.c | 209 ++++++++++++++++++++++
|
||||
10 files changed, 638 insertions(+), 3 deletions(-)
|
||||
create mode 100644 arch/mips/include/asm/mach-ralink/irq.h
|
||||
create mode 100644 arch/mips/include/asm/mach-ralink/mt7621.h
|
||||
create mode 100644 arch/mips/ralink/irq-gic.c
|
||||
create mode 100644 arch/mips/ralink/malta-amon.c
|
||||
create mode 100644 arch/mips/ralink/mt7621.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/include/asm/mach-ralink/irq.h
|
||||
@@ -0,0 +1,9 @@
|
||||
+#ifndef __ASM_MACH_RALINK_IRQ_H
|
||||
+#define __ASM_MACH_RALINK_IRQ_H
|
||||
+
|
||||
+#define GIC_NUM_INTRS 64
|
||||
+#define NR_IRQS 256
|
||||
+
|
||||
+#include_next <irq.h>
|
||||
+
|
||||
+#endif
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/include/asm/mach-ralink/mt7621.h
|
||||
@@ -0,0 +1,42 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ *
|
||||
+ * Parts of this file are based on Ralink's 2.6.21 BSP
|
||||
+ *
|
||||
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#ifndef _MT7621_REGS_H_
|
||||
+#define _MT7621_REGS_H_
|
||||
+
|
||||
+#define MT7621_PALMBUS_BASE 0x1C000000
|
||||
+#define MT7621_PALMBUS_SIZE 0x03FFFFFF
|
||||
+
|
||||
+#define MT7621_SYSC_BASE 0x1E000000
|
||||
+
|
||||
+#define SYSC_REG_CHIP_NAME0 0x00
|
||||
+#define SYSC_REG_CHIP_NAME1 0x04
|
||||
+#define SYSC_REG_CHIP_REV 0x0c
|
||||
+#define SYSC_REG_SYSTEM_CONFIG0 0x10
|
||||
+#define SYSC_REG_SYSTEM_CONFIG1 0x14
|
||||
+
|
||||
+#define CHIP_REV_PKG_MASK 0x1
|
||||
+#define CHIP_REV_PKG_SHIFT 16
|
||||
+#define CHIP_REV_VER_MASK 0xf
|
||||
+#define CHIP_REV_VER_SHIFT 8
|
||||
+#define CHIP_REV_ECO_MASK 0xf
|
||||
+
|
||||
+#define MT7621_DRAM_BASE 0x0
|
||||
+#define MT7621_DDR2_SIZE_MIN 32
|
||||
+#define MT7621_DDR2_SIZE_MAX 256
|
||||
+
|
||||
+#define MT7621_CHIP_NAME0 0x3637544D
|
||||
+#define MT7621_CHIP_NAME1 0x20203132
|
||||
+
|
||||
+#define MIPS_GIC_IRQ_BASE (MIPS_CPU_IRQ_BASE + 8)
|
||||
+
|
||||
+#endif
|
||||
--- a/arch/mips/kernel/vmlinux.lds.S
|
||||
+++ b/arch/mips/kernel/vmlinux.lds.S
|
||||
@@ -53,6 +53,7 @@ SECTIONS
|
||||
/* read-only */
|
||||
_text = .; /* Text and read-only data */
|
||||
.text : {
|
||||
+ /*. = . + 0x8000; */
|
||||
TEXT_TEXT
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
--- a/arch/mips/ralink/Kconfig
|
||||
+++ b/arch/mips/ralink/Kconfig
|
||||
@@ -12,6 +12,11 @@ config RALINK_ILL_ACC
|
||||
depends on SOC_RT305X
|
||||
default y
|
||||
|
||||
+config IRQ_INTC
|
||||
+ bool
|
||||
+ default y
|
||||
+ depends on !SOC_MT7621
|
||||
+
|
||||
choice
|
||||
prompt "Ralink SoC selection"
|
||||
default SOC_RT305X
|
||||
@@ -34,6 +39,16 @@ choice
|
||||
config SOC_MT7620
|
||||
bool "MT7620/8"
|
||||
|
||||
+ config SOC_MT7621
|
||||
+ bool "MT7621"
|
||||
+ select MIPS_CPU_SCACHE
|
||||
+ select SYS_SUPPORTS_MULTITHREADING
|
||||
+ select SYS_SUPPORTS_SMP
|
||||
+ select SYS_SUPPORTS_MIPS_CPS
|
||||
+ select MIPS_GIC
|
||||
+ select COMMON_CLK
|
||||
+ select CLKSRC_MIPS_GIC
|
||||
+ select HW_HAS_PCI
|
||||
endchoice
|
||||
|
||||
choice
|
||||
@@ -65,6 +80,10 @@ choice
|
||||
depends on SOC_MT7620
|
||||
select BUILTIN_DTB
|
||||
|
||||
+ config DTB_MT7621_EVAL
|
||||
+ bool "MT7621 eval kit"
|
||||
+ depends on SOC_MT7621
|
||||
+
|
||||
endchoice
|
||||
|
||||
endif
|
||||
--- a/arch/mips/ralink/Makefile
|
||||
+++ b/arch/mips/ralink/Makefile
|
||||
@@ -6,16 +6,24 @@
|
||||
# Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
# Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
|
||||
-obj-y := prom.o of.o reset.o clk.o irq.o timer.o
|
||||
+obj-y := prom.o of.o reset.o
|
||||
+
|
||||
+ifndef CONFIG_MIPS_GIC
|
||||
+ obj-y += clk.o timer.o
|
||||
+endif
|
||||
|
||||
obj-$(CONFIG_CLKEVT_RT3352) += cevt-rt3352.o
|
||||
|
||||
obj-$(CONFIG_RALINK_ILL_ACC) += ill_acc.o
|
||||
|
||||
+obj-$(CONFIG_IRQ_INTC) += irq.o
|
||||
+obj-$(CONFIG_MIPS_GIC) += irq-gic.o timer-gic.o
|
||||
+
|
||||
obj-$(CONFIG_SOC_RT288X) += rt288x.o
|
||||
obj-$(CONFIG_SOC_RT305X) += rt305x.o
|
||||
obj-$(CONFIG_SOC_RT3883) += rt3883.o
|
||||
obj-$(CONFIG_SOC_MT7620) += mt7620.o
|
||||
+obj-$(CONFIG_SOC_MT7621) += mt7621.o
|
||||
|
||||
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
|
||||
|
||||
--- a/arch/mips/ralink/Platform
|
||||
+++ b/arch/mips/ralink/Platform
|
||||
@@ -27,3 +27,8 @@ cflags-$(CONFIG_SOC_RT3883) += -I$(srctr
|
||||
#
|
||||
load-$(CONFIG_SOC_MT7620) += 0xffffffff80000000
|
||||
cflags-$(CONFIG_SOC_MT7620) += -I$(srctree)/arch/mips/include/asm/mach-ralink/mt7620
|
||||
+
|
||||
+# Ralink MT7621
|
||||
+#
|
||||
+load-$(CONFIG_SOC_MT7621) += 0xffffffff80001000
|
||||
+cflags-$(CONFIG_SOC_MT7621) += -I$(srctree)/arch/mips/include/asm/mach-ralink/mt7621
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/ralink/irq-gic.c
|
||||
@@ -0,0 +1,18 @@
|
||||
+#include <linux/init.h>
|
||||
+
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/irqchip.h>
|
||||
+#include <linux/irqchip/mips-gic.h>
|
||||
+
|
||||
+int get_c0_perfcount_int(void)
|
||||
+{
|
||||
+ return gic_get_c0_perfcount_int();
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(get_c0_perfcount_int);
|
||||
+
|
||||
+void __init
|
||||
+arch_init_irq(void)
|
||||
+{
|
||||
+ irqchip_init();
|
||||
+}
|
||||
+
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/ralink/mt7621.c
|
||||
@@ -0,0 +1,223 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ *
|
||||
+ * Parts of this file are based on Ralink's 2.6.21 BSP
|
||||
+ *
|
||||
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include <asm/mipsregs.h>
|
||||
+#include <asm/smp-ops.h>
|
||||
+#include <asm/mips-cm.h>
|
||||
+#include <asm/mips-cpc.h>
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+#include <asm/mach-ralink/mt7621.h>
|
||||
+
|
||||
+#include <pinmux.h>
|
||||
+
|
||||
+#include "common.h"
|
||||
+
|
||||
+#define SYSC_REG_SYSCFG 0x10
|
||||
+#define SYSC_REG_CPLL_CLKCFG0 0x2c
|
||||
+#define SYSC_REG_CUR_CLK_STS 0x44
|
||||
+#define CPU_CLK_SEL (BIT(30) | BIT(31))
|
||||
+
|
||||
+#define MT7621_GPIO_MODE_UART1 1
|
||||
+#define MT7621_GPIO_MODE_I2C 2
|
||||
+#define MT7621_GPIO_MODE_UART3_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_UART3_SHIFT 3
|
||||
+#define MT7621_GPIO_MODE_UART3_GPIO 1
|
||||
+#define MT7621_GPIO_MODE_UART2_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_UART2_SHIFT 5
|
||||
+#define MT7621_GPIO_MODE_UART2_GPIO 1
|
||||
+#define MT7621_GPIO_MODE_JTAG 7
|
||||
+#define MT7621_GPIO_MODE_WDT_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_WDT_SHIFT 8
|
||||
+#define MT7621_GPIO_MODE_WDT_GPIO 1
|
||||
+#define MT7621_GPIO_MODE_PCIE_RST 0
|
||||
+#define MT7621_GPIO_MODE_PCIE_REF 2
|
||||
+#define MT7621_GPIO_MODE_PCIE_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_PCIE_SHIFT 10
|
||||
+#define MT7621_GPIO_MODE_PCIE_GPIO 1
|
||||
+#define MT7621_GPIO_MODE_MDIO_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_MDIO_SHIFT 12
|
||||
+#define MT7621_GPIO_MODE_MDIO_GPIO 1
|
||||
+#define MT7621_GPIO_MODE_RGMII1 14
|
||||
+#define MT7621_GPIO_MODE_RGMII2 15
|
||||
+#define MT7621_GPIO_MODE_SPI_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_SPI_SHIFT 16
|
||||
+#define MT7621_GPIO_MODE_SPI_GPIO 1
|
||||
+#define MT7621_GPIO_MODE_SDHCI_MASK 0x3
|
||||
+#define MT7621_GPIO_MODE_SDHCI_SHIFT 18
|
||||
+#define MT7621_GPIO_MODE_SDHCI_GPIO 1
|
||||
+
|
||||
+static struct rt2880_pmx_func uart1_grp[] = { FUNC("uart1", 0, 1, 2) };
|
||||
+static struct rt2880_pmx_func i2c_grp[] = { FUNC("i2c", 0, 3, 2) };
|
||||
+static struct rt2880_pmx_func uart3_grp[] = {
|
||||
+ FUNC("uart3", 0, 5, 4),
|
||||
+ FUNC("i2s", 2, 5, 4),
|
||||
+ FUNC("spdif3", 3, 5, 4),
|
||||
+};
|
||||
+static struct rt2880_pmx_func uart2_grp[] = {
|
||||
+ FUNC("uart2", 0, 9, 4),
|
||||
+ FUNC("pcm", 2, 9, 4),
|
||||
+ FUNC("spdif2", 3, 9, 4),
|
||||
+};
|
||||
+static struct rt2880_pmx_func jtag_grp[] = { FUNC("jtag", 0, 13, 5) };
|
||||
+static struct rt2880_pmx_func wdt_grp[] = {
|
||||
+ FUNC("wdt rst", 0, 18, 1),
|
||||
+ FUNC("wdt refclk", 2, 18, 1),
|
||||
+};
|
||||
+static struct rt2880_pmx_func pcie_rst_grp[] = {
|
||||
+ FUNC("pcie rst", MT7621_GPIO_MODE_PCIE_RST, 19, 1),
|
||||
+ FUNC("pcie refclk", MT7621_GPIO_MODE_PCIE_REF, 19, 1)
|
||||
+};
|
||||
+static struct rt2880_pmx_func mdio_grp[] = { FUNC("mdio", 0, 20, 2) };
|
||||
+static struct rt2880_pmx_func rgmii2_grp[] = { FUNC("rgmii2", 0, 22, 12) };
|
||||
+static struct rt2880_pmx_func spi_grp[] = {
|
||||
+ FUNC("spi", 0, 34, 7),
|
||||
+ FUNC("nand1", 2, 34, 7),
|
||||
+};
|
||||
+static struct rt2880_pmx_func sdhci_grp[] = {
|
||||
+ FUNC("sdhci", 0, 41, 8),
|
||||
+ FUNC("nand2", 2, 41, 8),
|
||||
+};
|
||||
+static struct rt2880_pmx_func rgmii1_grp[] = { FUNC("rgmii1", 0, 49, 12) };
|
||||
+
|
||||
+static struct rt2880_pmx_group mt7621_pinmux_data[] = {
|
||||
+ GRP("uart1", uart1_grp, 1, MT7621_GPIO_MODE_UART1),
|
||||
+ GRP("i2c", i2c_grp, 1, MT7621_GPIO_MODE_I2C),
|
||||
+ GRP_G("uart3", uart3_grp, MT7621_GPIO_MODE_UART3_MASK,
|
||||
+ MT7621_GPIO_MODE_UART3_GPIO, MT7621_GPIO_MODE_UART3_SHIFT),
|
||||
+ GRP_G("uart2", uart2_grp, MT7621_GPIO_MODE_UART2_MASK,
|
||||
+ MT7621_GPIO_MODE_UART2_GPIO, MT7621_GPIO_MODE_UART2_SHIFT),
|
||||
+ GRP("jtag", jtag_grp, 1, MT7621_GPIO_MODE_JTAG),
|
||||
+ GRP_G("wdt", wdt_grp, MT7621_GPIO_MODE_WDT_MASK,
|
||||
+ MT7621_GPIO_MODE_WDT_GPIO, MT7621_GPIO_MODE_WDT_SHIFT),
|
||||
+ GRP_G("pcie", pcie_rst_grp, MT7621_GPIO_MODE_PCIE_MASK,
|
||||
+ MT7621_GPIO_MODE_PCIE_GPIO, MT7621_GPIO_MODE_PCIE_SHIFT),
|
||||
+ GRP_G("mdio", mdio_grp, MT7621_GPIO_MODE_MDIO_MASK,
|
||||
+ MT7621_GPIO_MODE_MDIO_GPIO, MT7621_GPIO_MODE_MDIO_SHIFT),
|
||||
+ GRP("rgmii2", rgmii2_grp, 1, MT7621_GPIO_MODE_RGMII2),
|
||||
+ GRP_G("spi", spi_grp, MT7621_GPIO_MODE_SPI_MASK,
|
||||
+ MT7621_GPIO_MODE_SPI_GPIO, MT7621_GPIO_MODE_SPI_SHIFT),
|
||||
+ GRP_G("sdhci", sdhci_grp, MT7621_GPIO_MODE_SDHCI_MASK,
|
||||
+ MT7621_GPIO_MODE_SDHCI_GPIO, MT7621_GPIO_MODE_SDHCI_SHIFT),
|
||||
+ GRP("rgmii1", rgmii1_grp, 1, MT7621_GPIO_MODE_RGMII1),
|
||||
+ { 0 }
|
||||
+};
|
||||
+
|
||||
+phys_addr_t mips_cpc_default_phys_base() {
|
||||
+ panic("Cannot detect cpc address");
|
||||
+}
|
||||
+
|
||||
+void __init ralink_clk_init(void)
|
||||
+{
|
||||
+ int cpu_fdiv = 0;
|
||||
+ int cpu_ffrac = 0;
|
||||
+ int fbdiv = 0;
|
||||
+ u32 clk_sts, syscfg;
|
||||
+ u8 clk_sel = 0, xtal_mode;
|
||||
+ u32 cpu_clk;
|
||||
+
|
||||
+ if ((rt_sysc_r32(SYSC_REG_CPLL_CLKCFG0) & CPU_CLK_SEL) != 0)
|
||||
+ clk_sel = 1;
|
||||
+
|
||||
+ switch (clk_sel) {
|
||||
+ case 0:
|
||||
+ clk_sts = rt_sysc_r32(SYSC_REG_CUR_CLK_STS);
|
||||
+ cpu_fdiv = ((clk_sts >> 8) & 0x1F);
|
||||
+ cpu_ffrac = (clk_sts & 0x1F);
|
||||
+ cpu_clk = (500 * cpu_ffrac / cpu_fdiv) * 1000 * 1000;
|
||||
+ break;
|
||||
+
|
||||
+ case 1:
|
||||
+ fbdiv = ((rt_sysc_r32(0x648) >> 4) & 0x7F) + 1;
|
||||
+ syscfg = rt_sysc_r32(SYSC_REG_SYSCFG);
|
||||
+ xtal_mode = (syscfg >> 6) & 0x7;
|
||||
+ if(xtal_mode >= 6) { //25Mhz Xtal
|
||||
+ cpu_clk = 25 * fbdiv * 1000 * 1000;
|
||||
+ } else if(xtal_mode >=3) { //40Mhz Xtal
|
||||
+ cpu_clk = 40 * fbdiv * 1000 * 1000;
|
||||
+ } else { // 20Mhz Xtal
|
||||
+ cpu_clk = 20 * fbdiv * 1000 * 1000;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void __init ralink_of_remap(void)
|
||||
+{
|
||||
+ rt_sysc_membase = plat_of_remap_node("mtk,mt7621-sysc");
|
||||
+ rt_memc_membase = plat_of_remap_node("mtk,mt7621-memc");
|
||||
+
|
||||
+ if (!rt_sysc_membase || !rt_memc_membase)
|
||||
+ panic("Failed to remap core resources");
|
||||
+}
|
||||
+
|
||||
+void prom_soc_init(struct ralink_soc_info *soc_info)
|
||||
+{
|
||||
+ void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7621_SYSC_BASE);
|
||||
+ unsigned char *name = NULL;
|
||||
+ u32 n0;
|
||||
+ u32 n1;
|
||||
+ u32 rev;
|
||||
+
|
||||
+ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0);
|
||||
+ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1);
|
||||
+
|
||||
+ if (n0 == MT7621_CHIP_NAME0 && n1 == MT7621_CHIP_NAME1) {
|
||||
+ name = "MT7621";
|
||||
+ soc_info->compatible = "mtk,mt7621-soc";
|
||||
+ } else {
|
||||
+ panic("mt7621: unknown SoC, n0:%08x n1:%08x\n", n0, n1);
|
||||
+ }
|
||||
+
|
||||
+ rev = __raw_readl(sysc + SYSC_REG_CHIP_REV);
|
||||
+
|
||||
+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
|
||||
+ "MediaTek %s ver:%u eco:%u",
|
||||
+ name,
|
||||
+ (rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK,
|
||||
+ (rev & CHIP_REV_ECO_MASK));
|
||||
+
|
||||
+ soc_info->mem_size_min = MT7621_DDR2_SIZE_MIN;
|
||||
+ soc_info->mem_size_max = MT7621_DDR2_SIZE_MAX;
|
||||
+ soc_info->mem_base = MT7621_DRAM_BASE;
|
||||
+
|
||||
+ rt2880_pinmux_data = mt7621_pinmux_data;
|
||||
+
|
||||
+ /* Early detection of CMP support */
|
||||
+ mips_cm_probe();
|
||||
+ mips_cpc_probe();
|
||||
+
|
||||
+ if (mips_cm_numiocu()) {
|
||||
+ /* mips_cm_probe() wipes out bootloader
|
||||
+ config for CM regions and we have to configure them
|
||||
+ again. This SoC cannot talk to pamlbus devices
|
||||
+ witout proper iocu region set up.
|
||||
+
|
||||
+ FIXME: it would be better to do this with values
|
||||
+ from DT, but we need this very early because
|
||||
+ without this we cannot talk to pretty much anything
|
||||
+ including serial.
|
||||
+ */
|
||||
+ write_gcr_reg0_base(MT7621_PALMBUS_BASE);
|
||||
+ write_gcr_reg0_mask(~MT7621_PALMBUS_SIZE | CM_GCR_REGn_MASK_CMTGT_IOCU0);
|
||||
+ }
|
||||
+
|
||||
+ if (!register_cps_smp_ops())
|
||||
+ return;
|
||||
+ if (!register_cmp_smp_ops())
|
||||
+ return;
|
||||
+ if (!register_vsmp_smp_ops())
|
||||
+ return;
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/ralink/timer-gic.c
|
||||
@@ -0,0 +1,15 @@
|
||||
+#include <linux/init.h>
|
||||
+
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/clk-provider.h>
|
||||
+#include <linux/clocksource.h>
|
||||
+
|
||||
+#include "common.h"
|
||||
+
|
||||
+void __init plat_time_init(void)
|
||||
+{
|
||||
+ ralink_of_remap();
|
||||
+
|
||||
+ of_clk_init(NULL);
|
||||
+ clocksource_probe();
|
||||
+}
|
|
@ -0,0 +1,211 @@
|
|||
From c96f2cc4d5f6e1bb11f3e7e04a7e21503a214d7c Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 27 Jan 2014 13:12:41 +0000
|
||||
Subject: [PATCH 02/53] MIPS: ralink: add MT7621 defconfig
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/configs/mt7621_defconfig | 197 ++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 197 insertions(+)
|
||||
create mode 100644 arch/mips/configs/mt7621_defconfig
|
||||
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/configs/mt7621_defconfig
|
||||
@@ -0,0 +1,197 @@
|
||||
+# CONFIG_LOCALVERSION_AUTO is not set
|
||||
+CONFIG_SYSVIPC=y
|
||||
+CONFIG_HIGH_RES_TIMERS=y
|
||||
+CONFIG_RCU_FANOUT=32
|
||||
+CONFIG_UIDGID_STRICT_TYPE_CHECKS=y
|
||||
+CONFIG_BLK_DEV_INITRD=y
|
||||
+CONFIG_INITRAMFS_SOURCE="/openwrt/trunk/build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips /openwrt/trunk/target/linux/generic/image/initramfs-base-files.txt"
|
||||
+CONFIG_INITRAMFS_ROOT_UID=1000
|
||||
+CONFIG_INITRAMFS_ROOT_GID=1000
|
||||
+# CONFIG_RD_GZIP is not set
|
||||
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
|
||||
+# CONFIG_AIO is not set
|
||||
+CONFIG_EMBEDDED=y
|
||||
+# CONFIG_VM_EVENT_COUNTERS is not set
|
||||
+# CONFIG_SLUB_DEBUG is not set
|
||||
+# CONFIG_COMPAT_BRK is not set
|
||||
+CONFIG_MODULES=y
|
||||
+CONFIG_MODULE_UNLOAD=y
|
||||
+# CONFIG_BLK_DEV_BSG is not set
|
||||
+CONFIG_PARTITION_ADVANCED=y
|
||||
+# CONFIG_IOSCHED_CFQ is not set
|
||||
+CONFIG_SMP=y
|
||||
+CONFIG_NR_CPUS=4
|
||||
+CONFIG_SCHED_SMT=y
|
||||
+# CONFIG_COMPACTION is not set
|
||||
+# CONFIG_CROSS_MEMORY_ATTACH is not set
|
||||
+# CONFIG_SECCOMP is not set
|
||||
+CONFIG_HZ_100=y
|
||||
+CONFIG_CMDLINE_BOOL=y
|
||||
+CONFIG_CMDLINE="rootfstype=squashfs,jffs2"
|
||||
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
|
||||
+CONFIG_NET=y
|
||||
+CONFIG_PACKET=y
|
||||
+CONFIG_UNIX=y
|
||||
+CONFIG_INET=y
|
||||
+CONFIG_IP_MULTICAST=y
|
||||
+CONFIG_IP_ADVANCED_ROUTER=y
|
||||
+CONFIG_IP_MULTIPLE_TABLES=y
|
||||
+CONFIG_IP_ROUTE_MULTIPATH=y
|
||||
+CONFIG_IP_ROUTE_VERBOSE=y
|
||||
+CONFIG_IP_MROUTE=y
|
||||
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
|
||||
+CONFIG_ARPD=y
|
||||
+CONFIG_SYN_COOKIES=y
|
||||
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
|
||||
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
|
||||
+# CONFIG_INET_XFRM_MODE_BEET is not set
|
||||
+# CONFIG_INET_LRO is not set
|
||||
+# CONFIG_INET_DIAG is not set
|
||||
+CONFIG_TCP_CONG_ADVANCED=y
|
||||
+# CONFIG_TCP_CONG_BIC is not set
|
||||
+# CONFIG_TCP_CONG_WESTWOOD is not set
|
||||
+# CONFIG_TCP_CONG_HTCP is not set
|
||||
+CONFIG_IPV6_PRIVACY=y
|
||||
+# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
|
||||
+# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
|
||||
+# CONFIG_INET6_XFRM_MODE_BEET is not set
|
||||
+# CONFIG_IPV6_SIT is not set
|
||||
+CONFIG_IPV6_MULTIPLE_TABLES=y
|
||||
+CONFIG_IPV6_SUBTREES=y
|
||||
+CONFIG_IPV6_MROUTE=y
|
||||
+CONFIG_NETFILTER=y
|
||||
+# CONFIG_BRIDGE_NETFILTER is not set
|
||||
+CONFIG_NF_CONNTRACK=m
|
||||
+CONFIG_NF_CONNTRACK_FTP=m
|
||||
+CONFIG_NF_CONNTRACK_IRC=m
|
||||
+CONFIG_NETFILTER_XT_MARK=m
|
||||
+CONFIG_NETFILTER_XT_TARGET_LOG=m
|
||||
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
|
||||
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_MAC=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_STATE=m
|
||||
+CONFIG_NETFILTER_XT_MATCH_TIME=m
|
||||
+CONFIG_NF_CONNTRACK_IPV4=m
|
||||
+# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set
|
||||
+CONFIG_IP_NF_IPTABLES=m
|
||||
+CONFIG_IP_NF_FILTER=m
|
||||
+CONFIG_IP_NF_TARGET_REJECT=m
|
||||
+CONFIG_NF_NAT_IPV4=m
|
||||
+CONFIG_IP_NF_TARGET_MASQUERADE=m
|
||||
+CONFIG_IP_NF_TARGET_REDIRECT=m
|
||||
+CONFIG_IP_NF_MANGLE=m
|
||||
+CONFIG_IP_NF_RAW=m
|
||||
+CONFIG_NF_CONNTRACK_IPV6=m
|
||||
+CONFIG_IP6_NF_IPTABLES=m
|
||||
+CONFIG_IP6_NF_MATCH_AH=m
|
||||
+CONFIG_IP6_NF_MATCH_EUI64=m
|
||||
+CONFIG_IP6_NF_MATCH_FRAG=m
|
||||
+CONFIG_IP6_NF_MATCH_OPTS=m
|
||||
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
|
||||
+CONFIG_IP6_NF_MATCH_MH=m
|
||||
+CONFIG_IP6_NF_MATCH_RT=m
|
||||
+CONFIG_IP6_NF_FILTER=m
|
||||
+CONFIG_IP6_NF_TARGET_REJECT=m
|
||||
+CONFIG_IP6_NF_MANGLE=m
|
||||
+CONFIG_IP6_NF_RAW=m
|
||||
+CONFIG_BRIDGE=m
|
||||
+# CONFIG_BRIDGE_IGMP_SNOOPING is not set
|
||||
+CONFIG_VLAN_8021Q=y
|
||||
+CONFIG_NET_SCHED=y
|
||||
+CONFIG_NET_SCH_FQ_CODEL=y
|
||||
+CONFIG_HAMRADIO=y
|
||||
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
+# CONFIG_FIRMWARE_IN_KERNEL is not set
|
||||
+CONFIG_MTD=y
|
||||
+CONFIG_MTD_CMDLINE_PARTS=y
|
||||
+CONFIG_MTD_BLOCK=y
|
||||
+CONFIG_MTD_CFI=y
|
||||
+CONFIG_MTD_CFI_AMDSTD=y
|
||||
+CONFIG_MTD_COMPLEX_MAPPINGS=y
|
||||
+CONFIG_MTD_PHYSMAP=y
|
||||
+CONFIG_MTD_M25P80=y
|
||||
+CONFIG_EEPROM_93CX6=m
|
||||
+CONFIG_SCSI=y
|
||||
+CONFIG_BLK_DEV_SD=y
|
||||
+CONFIG_NETDEVICES=y
|
||||
+# CONFIG_NET_PACKET_ENGINE is not set
|
||||
+# CONFIG_NET_VENDOR_WIZNET is not set
|
||||
+CONFIG_PHYLIB=y
|
||||
+CONFIG_SWCONFIG=y
|
||||
+CONFIG_PPP=m
|
||||
+CONFIG_PPP_FILTER=y
|
||||
+CONFIG_PPP_MULTILINK=y
|
||||
+CONFIG_PPPOE=m
|
||||
+CONFIG_PPP_ASYNC=m
|
||||
+CONFIG_ISDN=y
|
||||
+# CONFIG_INPUT is not set
|
||||
+# CONFIG_SERIO is not set
|
||||
+# CONFIG_VT is not set
|
||||
+# CONFIG_LEGACY_PTYS is not set
|
||||
+# CONFIG_DEVKMEM is not set
|
||||
+CONFIG_SERIAL_8250=y
|
||||
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
|
||||
+CONFIG_SERIAL_8250_CONSOLE=y
|
||||
+# CONFIG_SERIAL_8250_PCI is not set
|
||||
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
|
||||
+CONFIG_SPI=y
|
||||
+CONFIG_GPIOLIB=y
|
||||
+CONFIG_GPIO_SYSFS=y
|
||||
+# CONFIG_HWMON is not set
|
||||
+CONFIG_WATCHDOG=y
|
||||
+CONFIG_WATCHDOG_CORE=y
|
||||
+# CONFIG_VGA_ARB is not set
|
||||
+CONFIG_USB=y
|
||||
+CONFIG_USB_XHCI_HCD=y
|
||||
+CONFIG_USB_XHCI_PLATFORM=y
|
||||
+CONFIG_USB_MT7621_XHCI_PLATFORM=y
|
||||
+CONFIG_USB_STORAGE=y
|
||||
+CONFIG_USB_PHY=y
|
||||
+CONFIG_NEW_LEDS=y
|
||||
+CONFIG_LEDS_CLASS=y
|
||||
+CONFIG_LEDS_GPIO=m
|
||||
+CONFIG_LEDS_TRIGGERS=y
|
||||
+CONFIG_LEDS_TRIGGER_TIMER=y
|
||||
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
|
||||
+CONFIG_STAGING=y
|
||||
+CONFIG_USB_DWC2=m
|
||||
+# CONFIG_IOMMU_SUPPORT is not set
|
||||
+CONFIG_RESET_CONTROLLER=y
|
||||
+# CONFIG_FIRMWARE_MEMMAP is not set
|
||||
+# CONFIG_DNOTIFY is not set
|
||||
+# CONFIG_PROC_PAGE_MONITOR is not set
|
||||
+CONFIG_TMPFS=y
|
||||
+CONFIG_TMPFS_XATTR=y
|
||||
+CONFIG_JFFS2_FS=y
|
||||
+CONFIG_JFFS2_SUMMARY=y
|
||||
+CONFIG_JFFS2_FS_XATTR=y
|
||||
+# CONFIG_JFFS2_FS_POSIX_ACL is not set
|
||||
+# CONFIG_JFFS2_FS_SECURITY is not set
|
||||
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
|
||||
+# CONFIG_JFFS2_ZLIB is not set
|
||||
+CONFIG_SQUASHFS=y
|
||||
+# CONFIG_SQUASHFS_ZLIB is not set
|
||||
+CONFIG_SQUASHFS_XZ=y
|
||||
+CONFIG_PRINTK_TIME=y
|
||||
+# CONFIG_ENABLE_MUST_CHECK is not set
|
||||
+CONFIG_FRAME_WARN=1024
|
||||
+CONFIG_MAGIC_SYSRQ=y
|
||||
+CONFIG_STRIP_ASM_SYMS=y
|
||||
+# CONFIG_UNUSED_SYMBOLS is not set
|
||||
+CONFIG_DEBUG_FS=y
|
||||
+# CONFIG_SCHED_DEBUG is not set
|
||||
+CONFIG_DEBUG_INFO=y
|
||||
+CONFIG_DEBUG_INFO_REDUCED=y
|
||||
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
|
||||
+# CONFIG_FTRACE is not set
|
||||
+CONFIG_CRYPTO_ARC4=m
|
||||
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
|
||||
+# CONFIG_VIRTUALIZATION is not set
|
||||
+CONFIG_CRC_ITU_T=m
|
||||
+CONFIG_CRC32_SARWATE=y
|
||||
+# CONFIG_XZ_DEC_X86 is not set
|
||||
+CONFIG_AVERAGE=y
|
|
@ -0,0 +1,857 @@
|
|||
From fec11d4e8dc5cc79bcd7c8fd55038ac21ac39965 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 16 Mar 2014 05:22:39 +0000
|
||||
Subject: [PATCH 04/53] MIPS: ralink: add MT7621 pcie driver
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/pci/Makefile | 1 +
|
||||
arch/mips/pci/pci-mt7621.c | 813 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 814 insertions(+)
|
||||
create mode 100644 arch/mips/pci/pci-mt7621.c
|
||||
|
||||
--- a/arch/mips/pci/Makefile
|
||||
+++ b/arch/mips/pci/Makefile
|
||||
@@ -43,6 +43,7 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1
|
||||
obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o
|
||||
obj-$(CONFIG_LANTIQ) += fixup-lantiq.o
|
||||
obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o
|
||||
+obj-$(CONFIG_SOC_MT7621) += pci-mt7621.o
|
||||
obj-$(CONFIG_SOC_RT288X) += pci-rt2880.o
|
||||
obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o
|
||||
obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/pci/pci-mt7621.c
|
||||
@@ -0,0 +1,832 @@
|
||||
+/**************************************************************************
|
||||
+ *
|
||||
+ * BRIEF MODULE DESCRIPTION
|
||||
+ * PCI init for Ralink RT2880 solution
|
||||
+ *
|
||||
+ * Copyright 2007 Ralink Inc. (bruce_chang@ralinktech.com.tw)
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License as published by the
|
||||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||
+ * option) any later version.
|
||||
+ *
|
||||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License along
|
||||
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
+ *
|
||||
+ *
|
||||
+ **************************************************************************
|
||||
+ * May 2007 Bruce Chang
|
||||
+ * Initial Release
|
||||
+ *
|
||||
+ * May 2009 Bruce Chang
|
||||
+ * support RT2880/RT3883 PCIe
|
||||
+ *
|
||||
+ * May 2011 Bruce Chang
|
||||
+ * support RT6855/MT7620 PCIe
|
||||
+ *
|
||||
+ **************************************************************************
|
||||
+ */
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/pci.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/version.h>
|
||||
+#include <asm/pci.h>
|
||||
+#include <asm/io.h>
|
||||
+#include <asm/mips-cm.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_pci.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#include <ralink_regs.h>
|
||||
+
|
||||
+extern void pcie_phy_init(void);
|
||||
+extern void chk_phy_pll(void);
|
||||
+
|
||||
+/*
|
||||
+ * These functions and structures provide the BIOS scan and mapping of the PCI
|
||||
+ * devices.
|
||||
+ */
|
||||
+
|
||||
+#define CONFIG_PCIE_PORT0
|
||||
+#define CONFIG_PCIE_PORT1
|
||||
+#define CONFIG_PCIE_PORT2
|
||||
+#define RALINK_PCIE0_CLK_EN (1<<24)
|
||||
+#define RALINK_PCIE1_CLK_EN (1<<25)
|
||||
+#define RALINK_PCIE2_CLK_EN (1<<26)
|
||||
+
|
||||
+#define RALINK_PCI_CONFIG_ADDR 0x20
|
||||
+#define RALINK_PCI_CONFIG_DATA_VIRTUAL_REG 0x24
|
||||
+#define SURFBOARDINT_PCIE0 11 /* PCIE0 */
|
||||
+#define RALINK_INT_PCIE0 SURFBOARDINT_PCIE0
|
||||
+#define RALINK_INT_PCIE1 SURFBOARDINT_PCIE1
|
||||
+#define RALINK_INT_PCIE2 SURFBOARDINT_PCIE2
|
||||
+#define SURFBOARDINT_PCIE1 31 /* PCIE1 */
|
||||
+#define SURFBOARDINT_PCIE2 32 /* PCIE2 */
|
||||
+#define RALINK_PCI_MEMBASE *(volatile u32 *)(RALINK_PCI_BASE + 0x0028)
|
||||
+#define RALINK_PCI_IOBASE *(volatile u32 *)(RALINK_PCI_BASE + 0x002C)
|
||||
+#define RALINK_PCIE0_RST (1<<24)
|
||||
+#define RALINK_PCIE1_RST (1<<25)
|
||||
+#define RALINK_PCIE2_RST (1<<26)
|
||||
+#define RALINK_SYSCTL_BASE 0xBE000000
|
||||
+
|
||||
+#define RALINK_PCI_PCICFG_ADDR *(volatile u32 *)(RALINK_PCI_BASE + 0x0000)
|
||||
+#define RALINK_PCI_PCIMSK_ADDR *(volatile u32 *)(RALINK_PCI_BASE + 0x000C)
|
||||
+#define RALINK_PCI_BASE 0xBE140000
|
||||
+
|
||||
+#define RALINK_PCIEPHY_P0P1_CTL_OFFSET (RALINK_PCI_BASE + 0x9000)
|
||||
+#define RT6855_PCIE0_OFFSET 0x2000
|
||||
+#define RT6855_PCIE1_OFFSET 0x3000
|
||||
+#define RT6855_PCIE2_OFFSET 0x4000
|
||||
+
|
||||
+#define RALINK_PCI0_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0010)
|
||||
+#define RALINK_PCI0_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0018)
|
||||
+#define RALINK_PCI0_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0030)
|
||||
+#define RALINK_PCI0_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0034)
|
||||
+#define RALINK_PCI0_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0038)
|
||||
+#define RALINK_PCI0_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0050)
|
||||
+#define RALINK_PCI0_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0060)
|
||||
+#define RALINK_PCI0_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0064)
|
||||
+
|
||||
+#define RALINK_PCI1_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0010)
|
||||
+#define RALINK_PCI1_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0018)
|
||||
+#define RALINK_PCI1_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0030)
|
||||
+#define RALINK_PCI1_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0034)
|
||||
+#define RALINK_PCI1_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0038)
|
||||
+#define RALINK_PCI1_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0050)
|
||||
+#define RALINK_PCI1_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0060)
|
||||
+#define RALINK_PCI1_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0064)
|
||||
+
|
||||
+#define RALINK_PCI2_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0010)
|
||||
+#define RALINK_PCI2_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0018)
|
||||
+#define RALINK_PCI2_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0030)
|
||||
+#define RALINK_PCI2_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0034)
|
||||
+#define RALINK_PCI2_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0038)
|
||||
+#define RALINK_PCI2_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0050)
|
||||
+#define RALINK_PCI2_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0060)
|
||||
+#define RALINK_PCI2_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0064)
|
||||
+
|
||||
+#define RALINK_PCIEPHY_P0P1_CTL_OFFSET (RALINK_PCI_BASE + 0x9000)
|
||||
+#define RALINK_PCIEPHY_P2_CTL_OFFSET (RALINK_PCI_BASE + 0xA000)
|
||||
+
|
||||
+
|
||||
+#define MV_WRITE(ofs, data) \
|
||||
+ *(volatile u32 *)(RALINK_PCI_BASE+(ofs)) = cpu_to_le32(data)
|
||||
+#define MV_READ(ofs, data) \
|
||||
+ *(data) = le32_to_cpu(*(volatile u32 *)(RALINK_PCI_BASE+(ofs)))
|
||||
+#define MV_READ_DATA(ofs) \
|
||||
+ le32_to_cpu(*(volatile u32 *)(RALINK_PCI_BASE+(ofs)))
|
||||
+
|
||||
+#define MV_WRITE_16(ofs, data) \
|
||||
+ *(volatile u16 *)(RALINK_PCI_BASE+(ofs)) = cpu_to_le16(data)
|
||||
+#define MV_READ_16(ofs, data) \
|
||||
+ *(data) = le16_to_cpu(*(volatile u16 *)(RALINK_PCI_BASE+(ofs)))
|
||||
+
|
||||
+#define MV_WRITE_8(ofs, data) \
|
||||
+ *(volatile u8 *)(RALINK_PCI_BASE+(ofs)) = data
|
||||
+#define MV_READ_8(ofs, data) \
|
||||
+ *(data) = *(volatile u8 *)(RALINK_PCI_BASE+(ofs))
|
||||
+
|
||||
+
|
||||
+
|
||||
+#define RALINK_PCI_MM_MAP_BASE 0x60000000
|
||||
+#define RALINK_PCI_IO_MAP_BASE 0x1e160000
|
||||
+
|
||||
+#define RALINK_SYSTEM_CONTROL_BASE 0xbe000000
|
||||
+#define GPIO_PERST
|
||||
+#define ASSERT_SYSRST_PCIE(val) do { \
|
||||
+ if (*(unsigned int *)(0xbe00000c) == 0x00030101) \
|
||||
+ RALINK_RSTCTRL |= val; \
|
||||
+ else \
|
||||
+ RALINK_RSTCTRL &= ~val; \
|
||||
+ } while(0)
|
||||
+#define DEASSERT_SYSRST_PCIE(val) do { \
|
||||
+ if (*(unsigned int *)(0xbe00000c) == 0x00030101) \
|
||||
+ RALINK_RSTCTRL &= ~val; \
|
||||
+ else \
|
||||
+ RALINK_RSTCTRL |= val; \
|
||||
+ } while(0)
|
||||
+#define RALINK_SYSCFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x14)
|
||||
+#define RALINK_CLKCFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x30)
|
||||
+#define RALINK_RSTCTRL *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x34)
|
||||
+#define RALINK_GPIOMODE *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x60)
|
||||
+#define RALINK_PCIE_CLK_GEN *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x7c)
|
||||
+#define RALINK_PCIE_CLK_GEN1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x80)
|
||||
+#define PPLL_CFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x9c)
|
||||
+#define PPLL_DRV *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0xa0)
|
||||
+//RALINK_SYSCFG1 bit
|
||||
+#define RALINK_PCI_HOST_MODE_EN (1<<7)
|
||||
+#define RALINK_PCIE_RC_MODE_EN (1<<8)
|
||||
+//RALINK_RSTCTRL bit
|
||||
+#define RALINK_PCIE_RST (1<<23)
|
||||
+#define RALINK_PCI_RST (1<<24)
|
||||
+//RALINK_CLKCFG1 bit
|
||||
+#define RALINK_PCI_CLK_EN (1<<19)
|
||||
+#define RALINK_PCIE_CLK_EN (1<<21)
|
||||
+//RALINK_GPIOMODE bit
|
||||
+#define PCI_SLOTx2 (1<<11)
|
||||
+#define PCI_SLOTx1 (2<<11)
|
||||
+//MTK PCIE PLL bit
|
||||
+#define PDRV_SW_SET (1<<31)
|
||||
+#define LC_CKDRVPD_ (1<<19)
|
||||
+
|
||||
+#define MEMORY_BASE 0x0
|
||||
+static int pcie_link_status = 0;
|
||||
+
|
||||
+#define PCI_ACCESS_READ_1 0
|
||||
+#define PCI_ACCESS_READ_2 1
|
||||
+#define PCI_ACCESS_READ_4 2
|
||||
+#define PCI_ACCESS_WRITE_1 3
|
||||
+#define PCI_ACCESS_WRITE_2 4
|
||||
+#define PCI_ACCESS_WRITE_4 5
|
||||
+
|
||||
+static int config_access(unsigned char access_type, struct pci_bus *bus,
|
||||
+ unsigned int devfn, unsigned int where, u32 * data)
|
||||
+{
|
||||
+ unsigned int slot = PCI_SLOT(devfn);
|
||||
+ u8 func = PCI_FUNC(devfn);
|
||||
+ uint32_t address_reg, data_reg;
|
||||
+ unsigned int address;
|
||||
+
|
||||
+ address_reg = RALINK_PCI_CONFIG_ADDR;
|
||||
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
|
||||
+
|
||||
+ address = (((where&0xF00)>>8)<<24) |(bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000;
|
||||
+ MV_WRITE(address_reg, address);
|
||||
+
|
||||
+ switch(access_type) {
|
||||
+ case PCI_ACCESS_WRITE_1:
|
||||
+ MV_WRITE_8(data_reg+(where&0x3), *data);
|
||||
+ break;
|
||||
+ case PCI_ACCESS_WRITE_2:
|
||||
+ MV_WRITE_16(data_reg+(where&0x3), *data);
|
||||
+ break;
|
||||
+ case PCI_ACCESS_WRITE_4:
|
||||
+ MV_WRITE(data_reg, *data);
|
||||
+ break;
|
||||
+ case PCI_ACCESS_READ_1:
|
||||
+ MV_READ_8( data_reg+(where&0x3), data);
|
||||
+ break;
|
||||
+ case PCI_ACCESS_READ_2:
|
||||
+ MV_READ_16(data_reg+(where&0x3), data);
|
||||
+ break;
|
||||
+ case PCI_ACCESS_READ_4:
|
||||
+ MV_READ(data_reg, data);
|
||||
+ break;
|
||||
+ default:
|
||||
+ printk("no specify access type\n");
|
||||
+ break;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+read_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 * val)
|
||||
+{
|
||||
+ return config_access(PCI_ACCESS_READ_1, bus, devfn, (unsigned int)where, (u32 *)val);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+read_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 * val)
|
||||
+{
|
||||
+ return config_access(PCI_ACCESS_READ_2, bus, devfn, (unsigned int)where, (u32 *)val);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+read_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 * val)
|
||||
+{
|
||||
+ return config_access(PCI_ACCESS_READ_4, bus, devfn, (unsigned int)where, (u32 *)val);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 val)
|
||||
+{
|
||||
+ if (config_access(PCI_ACCESS_WRITE_1, bus, devfn, (unsigned int)where, (u32 *)&val))
|
||||
+ return -1;
|
||||
+
|
||||
+ return PCIBIOS_SUCCESSFUL;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+write_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 val)
|
||||
+{
|
||||
+ if (config_access(PCI_ACCESS_WRITE_2, bus, devfn, where, (u32 *)&val))
|
||||
+ return -1;
|
||||
+
|
||||
+ return PCIBIOS_SUCCESSFUL;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val)
|
||||
+{
|
||||
+ if (config_access(PCI_ACCESS_WRITE_4, bus, devfn, where, &val))
|
||||
+ return -1;
|
||||
+
|
||||
+ return PCIBIOS_SUCCESSFUL;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static int
|
||||
+pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val)
|
||||
+{
|
||||
+ switch (size) {
|
||||
+ case 1:
|
||||
+ return read_config_byte(bus, devfn, where, (u8 *) val);
|
||||
+ case 2:
|
||||
+ return read_config_word(bus, devfn, where, (u16 *) val);
|
||||
+ default:
|
||||
+ return read_config_dword(bus, devfn, where, val);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
|
||||
+{
|
||||
+ switch (size) {
|
||||
+ case 1:
|
||||
+ return write_config_byte(bus, devfn, where, (u8) val);
|
||||
+ case 2:
|
||||
+ return write_config_word(bus, devfn, where, (u16) val);
|
||||
+ default:
|
||||
+ return write_config_dword(bus, devfn, where, val);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+struct pci_ops mt7621_pci_ops= {
|
||||
+ .read = pci_config_read,
|
||||
+ .write = pci_config_write,
|
||||
+};
|
||||
+
|
||||
+static struct resource mt7621_res_pci_mem1 = {
|
||||
+ .name = "PCI MEM1",
|
||||
+ .start = RALINK_PCI_MM_MAP_BASE,
|
||||
+ .end = (u32)((RALINK_PCI_MM_MAP_BASE + (unsigned char *)0x0fffffff)),
|
||||
+ .flags = IORESOURCE_MEM,
|
||||
+};
|
||||
+static struct resource mt7621_res_pci_io1 = {
|
||||
+ .name = "PCI I/O1",
|
||||
+ .start = RALINK_PCI_IO_MAP_BASE,
|
||||
+ .end = (u32)((RALINK_PCI_IO_MAP_BASE + (unsigned char *)0x0ffff)),
|
||||
+ .flags = IORESOURCE_IO,
|
||||
+};
|
||||
+
|
||||
+static struct pci_controller mt7621_controller = {
|
||||
+ .pci_ops = &mt7621_pci_ops,
|
||||
+ .mem_resource = &mt7621_res_pci_mem1,
|
||||
+ .io_resource = &mt7621_res_pci_io1,
|
||||
+ .mem_offset = 0x00000000UL,
|
||||
+ .io_offset = 0x00000000UL,
|
||||
+ .io_map_base = 0xa0000000,
|
||||
+};
|
||||
+
|
||||
+static void
|
||||
+read_config(unsigned long bus, unsigned long dev, unsigned long func, unsigned long reg, unsigned long *val)
|
||||
+{
|
||||
+ unsigned int address_reg, data_reg, address;
|
||||
+
|
||||
+ address_reg = RALINK_PCI_CONFIG_ADDR;
|
||||
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
|
||||
+ address = (((reg & 0xF00)>>8)<<24) | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 0x80000000 ;
|
||||
+ MV_WRITE(address_reg, address);
|
||||
+ MV_READ(data_reg, val);
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+write_config(unsigned long bus, unsigned long dev, unsigned long func, unsigned long reg, unsigned long val)
|
||||
+{
|
||||
+ unsigned int address_reg, data_reg, address;
|
||||
+
|
||||
+ address_reg = RALINK_PCI_CONFIG_ADDR;
|
||||
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
|
||||
+ address = (((reg & 0xF00)>>8)<<24) | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 0x80000000 ;
|
||||
+ MV_WRITE(address_reg, address);
|
||||
+ MV_WRITE(data_reg, val);
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int __init
|
||||
+pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
+{
|
||||
+ u16 cmd;
|
||||
+ u32 val;
|
||||
+ int irq = 0;
|
||||
+
|
||||
+ if ((dev->bus->number == 0) && (slot == 0)) {
|
||||
+ write_config(0, 0, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
|
||||
+ read_config(0, 0, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
|
||||
+ printk("BAR0 at slot 0 = %x\n", val);
|
||||
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
|
||||
+ } else if((dev->bus->number == 0) && (slot == 0x1)) {
|
||||
+ write_config(0, 1, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
|
||||
+ read_config(0, 1, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
|
||||
+ printk("BAR0 at slot 1 = %x\n", val);
|
||||
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
|
||||
+ } else if((dev->bus->number == 0) && (slot == 0x2)) {
|
||||
+ write_config(0, 2, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
|
||||
+ read_config(0, 2, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
|
||||
+ printk("BAR0 at slot 2 = %x\n", val);
|
||||
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
|
||||
+ } else if ((dev->bus->number == 1) && (slot == 0x0)) {
|
||||
+ switch (pcie_link_status) {
|
||||
+ case 2:
|
||||
+ case 6:
|
||||
+ irq = RALINK_INT_PCIE1;
|
||||
+ break;
|
||||
+ case 4:
|
||||
+ irq = RALINK_INT_PCIE2;
|
||||
+ break;
|
||||
+ default:
|
||||
+ irq = RALINK_INT_PCIE0;
|
||||
+ }
|
||||
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
|
||||
+ } else if ((dev->bus->number == 2) && (slot == 0x0)) {
|
||||
+ switch (pcie_link_status) {
|
||||
+ case 5:
|
||||
+ case 6:
|
||||
+ irq = RALINK_INT_PCIE2;
|
||||
+ break;
|
||||
+ default:
|
||||
+ irq = RALINK_INT_PCIE1;
|
||||
+ }
|
||||
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
|
||||
+ } else if ((dev->bus->number == 2) && (slot == 0x1)) {
|
||||
+ switch (pcie_link_status) {
|
||||
+ case 5:
|
||||
+ case 6:
|
||||
+ irq = RALINK_INT_PCIE2;
|
||||
+ break;
|
||||
+ default:
|
||||
+ irq = RALINK_INT_PCIE1;
|
||||
+ }
|
||||
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
|
||||
+ } else if ((dev->bus->number ==3) && (slot == 0x0)) {
|
||||
+ irq = RALINK_INT_PCIE2;
|
||||
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
|
||||
+ } else if ((dev->bus->number ==3) && (slot == 0x1)) {
|
||||
+ irq = RALINK_INT_PCIE2;
|
||||
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
|
||||
+ } else if ((dev->bus->number ==3) && (slot == 0x2)) {
|
||||
+ irq = RALINK_INT_PCIE2;
|
||||
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
|
||||
+ } else {
|
||||
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x14); //configure cache line size 0x14
|
||||
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xFF); //configure latency timer 0x10
|
||||
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
+ cmd = cmd | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
|
||||
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
|
||||
+ return irq;
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+set_pcie_phy(u32 *addr, int start_b, int bits, int val)
|
||||
+{
|
||||
+// printk("0x%p:", addr);
|
||||
+// printk(" %x", *addr);
|
||||
+ *(unsigned int *)(addr) &= ~(((1<<bits) - 1)<<start_b);
|
||||
+ *(unsigned int *)(addr) |= val << start_b;
|
||||
+// printk(" -> %x\n", *addr);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+bypass_pipe_rst(void)
|
||||
+{
|
||||
+#if defined (CONFIG_PCIE_PORT0)
|
||||
+ /* PCIe Port 0 */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x02c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x02c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT1)
|
||||
+ /* PCIe Port 1 */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x12c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x12c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ /* PCIe Port 2 */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x02c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x02c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+set_phy_for_ssc(void)
|
||||
+{
|
||||
+ unsigned long reg = (*(volatile u32 *)(RALINK_SYSCTL_BASE + 0x10));
|
||||
+
|
||||
+ reg = (reg >> 6) & 0x7;
|
||||
+#if defined (CONFIG_PCIE_PORT0) || defined (CONFIG_PCIE_PORT1)
|
||||
+ /* Set PCIe Port0 & Port1 PHY to disable SSC */
|
||||
+ /* Debug Xtal Type */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x400), 8, 1, 0x01); // rg_pe1_frc_h_xtal_type
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x400), 9, 2, 0x00); // rg_pe1_h_xtal_type
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 0 enable control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 1 enable control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 5, 1, 0x00); // rg_pe1_phy_en //Port 0 disable
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 5, 1, 0x00); // rg_pe1_phy_en //Port 1 disable
|
||||
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 6, 2, 0x01); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
|
||||
+ printk("***** Xtal 40MHz *****\n");
|
||||
+ } else { // 25MHz | 20MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 6, 2, 0x00); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
|
||||
+ if (reg >= 6) {
|
||||
+ printk("***** Xtal 25MHz *****\n");
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4bc), 4, 2, 0x01); // RG_PE1_H_PLL_FBKSEL //Feedback clock select
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x49c), 0,31, 0x18000000); // RG_PE1_H_LCDDS_PCW_NCPO //DDS NCPO PCW (for host mode)
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a4), 0,16, 0x18d); // RG_PE1_H_LCDDS_SSC_PRD //DDS SSC dither period control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a8), 0,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA //DDS SSC dither amplitude control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a8), 16,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA1 //DDS SSC dither amplitude control for initial
|
||||
+ } else {
|
||||
+ printk("***** Xtal 20MHz *****\n");
|
||||
+ }
|
||||
+ }
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a0), 5, 1, 0x01); // RG_PE1_LCDDS_CLK_PH_INV //DDS clock inversion
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 22, 2, 0x02); // RG_PE1_H_PLL_BC
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 18, 4, 0x06); // RG_PE1_H_PLL_BP
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 12, 4, 0x02); // RG_PE1_H_PLL_IR
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 8, 4, 0x01); // RG_PE1_H_PLL_IC
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4ac), 16, 3, 0x00); // RG_PE1_H_PLL_BR
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 1, 3, 0x02); // RG_PE1_PLL_DIVEN
|
||||
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x414), 6, 2, 0x01); // rg_pe1_mstckdiv //value of da_pe1_mstckdiv when force mode enable
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x414), 5, 1, 0x01); // rg_pe1_frc_mstckdiv //force mode enable of da_pe1_mstckdiv
|
||||
+ }
|
||||
+ /* Enable PHY and disable force mode */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 5, 1, 0x01); // rg_pe1_phy_en //Port 0 enable
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 5, 1, 0x01); // rg_pe1_phy_en //Port 1 enable
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 0 disable control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 1 disable control
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ /* Set PCIe Port2 PHY to disable SSC */
|
||||
+ /* Debug Xtal Type */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x400), 8, 1, 0x01); // rg_pe1_frc_h_xtal_type
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x400), 9, 2, 0x00); // rg_pe1_h_xtal_type
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 0 enable control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 5, 1, 0x00); // rg_pe1_phy_en //Port 0 disable
|
||||
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 6, 2, 0x01); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
|
||||
+ } else { // 25MHz | 20MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 6, 2, 0x00); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
|
||||
+ if (reg >= 6) { // 25MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4bc), 4, 2, 0x01); // RG_PE1_H_PLL_FBKSEL //Feedback clock select
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x49c), 0,31, 0x18000000); // RG_PE1_H_LCDDS_PCW_NCPO //DDS NCPO PCW (for host mode)
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a4), 0,16, 0x18d); // RG_PE1_H_LCDDS_SSC_PRD //DDS SSC dither period control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a8), 0,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA //DDS SSC dither amplitude control
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a8), 16,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA1 //DDS SSC dither amplitude control for initial
|
||||
+ }
|
||||
+ }
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a0), 5, 1, 0x01); // RG_PE1_LCDDS_CLK_PH_INV //DDS clock inversion
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 22, 2, 0x02); // RG_PE1_H_PLL_BC
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 18, 4, 0x06); // RG_PE1_H_PLL_BP
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 12, 4, 0x02); // RG_PE1_H_PLL_IR
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 8, 4, 0x01); // RG_PE1_H_PLL_IC
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4ac), 16, 3, 0x00); // RG_PE1_H_PLL_BR
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 1, 3, 0x02); // RG_PE1_PLL_DIVEN
|
||||
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x414), 6, 2, 0x01); // rg_pe1_mstckdiv //value of da_pe1_mstckdiv when force mode enable
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x414), 5, 1, 0x01); // rg_pe1_frc_mstckdiv //force mode enable of da_pe1_mstckdiv
|
||||
+ }
|
||||
+ /* Enable PHY and disable force mode */
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 5, 1, 0x01); // rg_pe1_phy_en //Port 0 enable
|
||||
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 0 disable control
|
||||
+#endif
|
||||
+}
|
||||
+
|
||||
+void setup_cm_memory_region(struct resource *mem_resource)
|
||||
+{
|
||||
+ resource_size_t mask;
|
||||
+ if (mips_cm_numiocu()) {
|
||||
+ /* FIXME: hardware doesn't accept mask values with 1s after
|
||||
+ 0s (e.g. 0xffef), so it would be great to warn if that's
|
||||
+ about to happen */
|
||||
+ mask = ~(mem_resource->end - mem_resource->start);
|
||||
+
|
||||
+ write_gcr_reg1_base(mem_resource->start);
|
||||
+ write_gcr_reg1_mask(mask | CM_GCR_REGn_MASK_CMTGT_IOCU0);
|
||||
+ printk("PCI coherence region base: 0x%08lx, mask/settings: 0x%08lx\n",
|
||||
+ read_gcr_reg1_base(),
|
||||
+ read_gcr_reg1_mask());
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int mt7621_pci_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ unsigned long val = 0;
|
||||
+
|
||||
+ iomem_resource.start = 0;
|
||||
+ iomem_resource.end= ~0;
|
||||
+ ioport_resource.start= 0;
|
||||
+ ioport_resource.end = ~0;
|
||||
+
|
||||
+#if defined (CONFIG_PCIE_PORT0)
|
||||
+ val = RALINK_PCIE0_RST;
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT1)
|
||||
+ val |= RALINK_PCIE1_RST;
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ val |= RALINK_PCIE2_RST;
|
||||
+#endif
|
||||
+ ASSERT_SYSRST_PCIE(RALINK_PCIE0_RST | RALINK_PCIE1_RST | RALINK_PCIE2_RST);
|
||||
+ printk("pull PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
|
||||
+#if defined GPIO_PERST /* add GPIO control instead of PERST_N */ /*chhung*/
|
||||
+ *(unsigned int *)(0xbe000060) &= ~(0x3<<10 | 0x3<<3);
|
||||
+ *(unsigned int *)(0xbe000060) |= 0x1<<10 | 0x1<<3;
|
||||
+ mdelay(100);
|
||||
+ *(unsigned int *)(0xbe000600) |= 0x1<<19 | 0x1<<8 | 0x1<<7; // use GPIO19/GPIO8/GPIO7 (PERST_N/UART_RXD3/UART_TXD3)
|
||||
+ mdelay(100);
|
||||
+ *(unsigned int *)(0xbe000620) &= ~(0x1<<19 | 0x1<<8 | 0x1<<7); // clear DATA
|
||||
+
|
||||
+ mdelay(100);
|
||||
+#else
|
||||
+ *(unsigned int *)(0xbe000060) &= ~0x00000c00;
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT0)
|
||||
+ val = RALINK_PCIE0_RST;
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT1)
|
||||
+ val |= RALINK_PCIE1_RST;
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ val |= RALINK_PCIE2_RST;
|
||||
+#endif
|
||||
+ DEASSERT_SYSRST_PCIE(val);
|
||||
+ printk("release PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
|
||||
+
|
||||
+ if ((*(unsigned int *)(0xbe00000c)&0xFFFF) == 0x0101) // MT7621 E2
|
||||
+ bypass_pipe_rst();
|
||||
+ set_phy_for_ssc();
|
||||
+ printk("release PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
|
||||
+
|
||||
+#if defined (CONFIG_PCIE_PORT0)
|
||||
+ read_config(0, 0, 0, 0x70c, &val);
|
||||
+ printk("Port 0 N_FTS = %x\n", (unsigned int)val);
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT1)
|
||||
+ read_config(0, 1, 0, 0x70c, &val);
|
||||
+ printk("Port 1 N_FTS = %x\n", (unsigned int)val);
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ read_config(0, 2, 0, 0x70c, &val);
|
||||
+ printk("Port 2 N_FTS = %x\n", (unsigned int)val);
|
||||
+#endif
|
||||
+
|
||||
+ RALINK_RSTCTRL = (RALINK_RSTCTRL | RALINK_PCIE_RST);
|
||||
+ RALINK_SYSCFG1 &= ~(0x30);
|
||||
+ RALINK_SYSCFG1 |= (2<<4);
|
||||
+ RALINK_PCIE_CLK_GEN &= 0x7fffffff;
|
||||
+ RALINK_PCIE_CLK_GEN1 &= 0x80ffffff;
|
||||
+ RALINK_PCIE_CLK_GEN1 |= 0xa << 24;
|
||||
+ RALINK_PCIE_CLK_GEN |= 0x80000000;
|
||||
+ mdelay(50);
|
||||
+ RALINK_RSTCTRL = (RALINK_RSTCTRL & ~RALINK_PCIE_RST);
|
||||
+
|
||||
+
|
||||
+#if defined GPIO_PERST /* add GPIO control instead of PERST_N */ /*chhung*/
|
||||
+ *(unsigned int *)(0xbe000620) |= 0x1<<19 | 0x1<<8 | 0x1<<7; // set DATA
|
||||
+ mdelay(100);
|
||||
+#else
|
||||
+ RALINK_PCI_PCICFG_ADDR &= ~(1<<1); //de-assert PERST
|
||||
+#endif
|
||||
+ mdelay(500);
|
||||
+
|
||||
+
|
||||
+ mdelay(500);
|
||||
+#if defined (CONFIG_PCIE_PORT0)
|
||||
+ if(( RALINK_PCI0_STATUS & 0x1) == 0)
|
||||
+ {
|
||||
+ printk("PCIE0 no card, disable it(RST&CLK)\n");
|
||||
+ ASSERT_SYSRST_PCIE(RALINK_PCIE0_RST);
|
||||
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE0_CLK_EN);
|
||||
+ pcie_link_status &= ~(1<<0);
|
||||
+ } else {
|
||||
+ pcie_link_status |= 1<<0;
|
||||
+ RALINK_PCI_PCIMSK_ADDR |= (1<<20); // enable pcie1 interrupt
|
||||
+ }
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT1)
|
||||
+ if(( RALINK_PCI1_STATUS & 0x1) == 0)
|
||||
+ {
|
||||
+ printk("PCIE1 no card, disable it(RST&CLK)\n");
|
||||
+ ASSERT_SYSRST_PCIE(RALINK_PCIE1_RST);
|
||||
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE1_CLK_EN);
|
||||
+ pcie_link_status &= ~(1<<1);
|
||||
+ } else {
|
||||
+ pcie_link_status |= 1<<1;
|
||||
+ RALINK_PCI_PCIMSK_ADDR |= (1<<21); // enable pcie1 interrupt
|
||||
+ }
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ if (( RALINK_PCI2_STATUS & 0x1) == 0) {
|
||||
+ printk("PCIE2 no card, disable it(RST&CLK)\n");
|
||||
+ ASSERT_SYSRST_PCIE(RALINK_PCIE2_RST);
|
||||
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE2_CLK_EN);
|
||||
+ pcie_link_status &= ~(1<<2);
|
||||
+ } else {
|
||||
+ pcie_link_status |= 1<<2;
|
||||
+ RALINK_PCI_PCIMSK_ADDR |= (1<<22); // enable pcie2 interrupt
|
||||
+ }
|
||||
+#endif
|
||||
+ if (pcie_link_status == 0)
|
||||
+ return 0;
|
||||
+
|
||||
+/*
|
||||
+pcie(2/1/0) link status pcie2_num pcie1_num pcie0_num
|
||||
+3'b000 x x x
|
||||
+3'b001 x x 0
|
||||
+3'b010 x 0 x
|
||||
+3'b011 x 1 0
|
||||
+3'b100 0 x x
|
||||
+3'b101 1 x 0
|
||||
+3'b110 1 0 x
|
||||
+3'b111 2 1 0
|
||||
+*/
|
||||
+ switch(pcie_link_status) {
|
||||
+ case 2:
|
||||
+ RALINK_PCI_PCICFG_ADDR &= ~0x00ff0000;
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 16; //port0
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 20; //port1
|
||||
+ break;
|
||||
+ case 4:
|
||||
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 16; //port0
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 20; //port1
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 24; //port2
|
||||
+ break;
|
||||
+ case 5:
|
||||
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 16; //port0
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 20; //port1
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 24; //port2
|
||||
+ break;
|
||||
+ case 6:
|
||||
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 16; //port0
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 20; //port1
|
||||
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 24; //port2
|
||||
+ break;
|
||||
+ }
|
||||
+ printk(" -> %x\n", RALINK_PCI_PCICFG_ADDR);
|
||||
+ //printk(" RALINK_PCI_ARBCTL = %x\n", RALINK_PCI_ARBCTL);
|
||||
+
|
||||
+/*
|
||||
+ ioport_resource.start = mt7621_res_pci_io1.start;
|
||||
+ ioport_resource.end = mt7621_res_pci_io1.end;
|
||||
+*/
|
||||
+
|
||||
+ RALINK_PCI_MEMBASE = 0xffffffff; //RALINK_PCI_MM_MAP_BASE;
|
||||
+ RALINK_PCI_IOBASE = RALINK_PCI_IO_MAP_BASE;
|
||||
+
|
||||
+#if defined (CONFIG_PCIE_PORT0)
|
||||
+ //PCIe0
|
||||
+ if((pcie_link_status & 0x1) != 0) {
|
||||
+ RALINK_PCI0_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
|
||||
+ RALINK_PCI0_IMBASEBAR0_ADDR = MEMORY_BASE;
|
||||
+ RALINK_PCI0_CLASS = 0x06040001;
|
||||
+ printk("PCIE0 enabled\n");
|
||||
+ }
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT1)
|
||||
+ //PCIe1
|
||||
+ if ((pcie_link_status & 0x2) != 0) {
|
||||
+ RALINK_PCI1_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
|
||||
+ RALINK_PCI1_IMBASEBAR0_ADDR = MEMORY_BASE;
|
||||
+ RALINK_PCI1_CLASS = 0x06040001;
|
||||
+ printk("PCIE1 enabled\n");
|
||||
+ }
|
||||
+#endif
|
||||
+#if defined (CONFIG_PCIE_PORT2)
|
||||
+ //PCIe2
|
||||
+ if ((pcie_link_status & 0x4) != 0) {
|
||||
+ RALINK_PCI2_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
|
||||
+ RALINK_PCI2_IMBASEBAR0_ADDR = MEMORY_BASE;
|
||||
+ RALINK_PCI2_CLASS = 0x06040001;
|
||||
+ printk("PCIE2 enabled\n");
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
+ switch(pcie_link_status) {
|
||||
+ case 7:
|
||||
+ read_config(0, 2, 0, 0x4, &val);
|
||||
+ write_config(0, 2, 0, 0x4, val|0x4);
|
||||
+ // write_config(0, 1, 0, 0x4, val|0x7);
|
||||
+ read_config(0, 2, 0, 0x70c, &val);
|
||||
+ val &= ~(0xff)<<8;
|
||||
+ val |= 0x50<<8;
|
||||
+ write_config(0, 2, 0, 0x70c, val);
|
||||
+ case 3:
|
||||
+ case 5:
|
||||
+ case 6:
|
||||
+ read_config(0, 1, 0, 0x4, &val);
|
||||
+ write_config(0, 1, 0, 0x4, val|0x4);
|
||||
+ // write_config(0, 1, 0, 0x4, val|0x7);
|
||||
+ read_config(0, 1, 0, 0x70c, &val);
|
||||
+ val &= ~(0xff)<<8;
|
||||
+ val |= 0x50<<8;
|
||||
+ write_config(0, 1, 0, 0x70c, val);
|
||||
+ default:
|
||||
+ read_config(0, 0, 0, 0x4, &val);
|
||||
+ write_config(0, 0, 0, 0x4, val|0x4); //bus master enable
|
||||
+ // write_config(0, 0, 0, 0x4, val|0x7); //bus master enable
|
||||
+ read_config(0, 0, 0, 0x70c, &val);
|
||||
+ val &= ~(0xff)<<8;
|
||||
+ val |= 0x50<<8;
|
||||
+ write_config(0, 0, 0, 0x70c, val);
|
||||
+ }
|
||||
+
|
||||
+ pci_load_of_ranges(&mt7621_controller, pdev->dev.of_node);
|
||||
+ setup_cm_memory_region(mt7621_controller.mem_resource);
|
||||
+ register_pci_controller(&mt7621_controller);
|
||||
+ return 0;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+int pcibios_plat_dev_init(struct pci_dev *dev)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mt7621_pci_ids[] = {
|
||||
+ { .compatible = "mediatek,mt7621-pci" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mt7621_pci_ids);
|
||||
+
|
||||
+static struct platform_driver mt7621_pci_driver = {
|
||||
+ .probe = mt7621_pci_probe,
|
||||
+ .driver = {
|
||||
+ .name = "mt7621-pci",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = of_match_ptr(mt7621_pci_ids),
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init mt7621_pci_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&mt7621_pci_driver);
|
||||
+}
|
||||
+
|
||||
+arch_initcall(mt7621_pci_init);
|
|
@ -0,0 +1,82 @@
|
|||
From ce3d4a4111a5f7e6b4e74bceae5faa6ce388e8ec Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 14 Jul 2013 23:08:11 +0200
|
||||
Subject: [PATCH 05/53] MIPS: use set_mode() to enable/disable the cevt-r4k
|
||||
irq
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/Kconfig | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/arch/mips/ralink/Kconfig
|
||||
+++ b/arch/mips/ralink/Kconfig
|
||||
@@ -1,11 +1,16 @@
|
||||
if RALINK
|
||||
|
||||
+config CEVT_SYSTICK_QUIRK
|
||||
+ bool
|
||||
+ default n
|
||||
+
|
||||
config CLKEVT_RT3352
|
||||
bool
|
||||
depends on SOC_RT305X || SOC_MT7620
|
||||
default y
|
||||
select CLKSRC_OF
|
||||
select CLKSRC_MMIO
|
||||
+ select CEVT_SYSTICK_QUIRK
|
||||
|
||||
config RALINK_ILL_ACC
|
||||
bool
|
||||
--- a/arch/mips/kernel/cevt-r4k.c
|
||||
+++ b/arch/mips/kernel/cevt-r4k.c
|
||||
@@ -15,6 +15,26 @@
|
||||
#include <asm/time.h>
|
||||
#include <asm/cevt-r4k.h>
|
||||
|
||||
+static int mips_state_oneshot(struct clock_event_device *evt)
|
||||
+{
|
||||
+ if (!cp0_timer_irq_installed) {
|
||||
+ cp0_timer_irq_installed = 1;
|
||||
+ setup_irq(evt->irq, &c0_compare_irqaction);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mips_state_shutdown(struct clock_event_device *evt)
|
||||
+{
|
||||
+ if (cp0_timer_irq_installed) {
|
||||
+ cp0_timer_irq_installed = 0;
|
||||
+ remove_irq(evt->irq, &c0_compare_irqaction);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int mips_next_event(unsigned long delta,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
@@ -208,18 +228,21 @@ int r4k_clockevent_init(void)
|
||||
cd->rating = 300;
|
||||
cd->irq = irq;
|
||||
cd->cpumask = cpumask_of(cpu);
|
||||
+ cd->set_state_shutdown = mips_state_shutdown;
|
||||
+ cd->set_state_oneshot = mips_state_oneshot;
|
||||
cd->set_next_event = mips_next_event;
|
||||
cd->event_handler = mips_event_handler;
|
||||
|
||||
clockevents_register_device(cd);
|
||||
|
||||
+#ifndef CONFIG_CEVT_SYSTICK_QUIRK
|
||||
if (cp0_timer_irq_installed)
|
||||
return 0;
|
||||
|
||||
cp0_timer_irq_installed = 1;
|
||||
|
||||
setup_irq(irq, &c0_compare_irqaction);
|
||||
-
|
||||
+#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
From bd30f19a006fb52bac80c6463c49dd2f4159f4ac Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 28 Jul 2013 16:26:41 +0200
|
||||
Subject: [PATCH 06/53] MIPS: ralink: add cpu frequency scaling
|
||||
|
||||
This feature will break udelay() and cause the delay loop to have longer delays
|
||||
when the frequency is scaled causing a performance hit.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/cevt-rt3352.c | 38 ++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 38 insertions(+)
|
||||
|
||||
--- a/arch/mips/ralink/cevt-rt3352.c
|
||||
+++ b/arch/mips/ralink/cevt-rt3352.c
|
||||
@@ -29,6 +29,10 @@
|
||||
/* enable the counter */
|
||||
#define CFG_CNT_EN 0x1
|
||||
|
||||
+/* mt7620 frequency scaling defines */
|
||||
+#define CLK_LUT_CFG 0x40
|
||||
+#define SLEEP_EN BIT(31)
|
||||
+
|
||||
struct systick_device {
|
||||
void __iomem *membase;
|
||||
struct clock_event_device dev;
|
||||
@@ -36,9 +40,26 @@ struct systick_device {
|
||||
int freq_scale;
|
||||
};
|
||||
|
||||
+static void (*systick_freq_scaling)(struct systick_device *sdev, int status);
|
||||
+
|
||||
static int systick_set_oneshot(struct clock_event_device *evt);
|
||||
static int systick_shutdown(struct clock_event_device *evt);
|
||||
|
||||
+static inline void mt7620_freq_scaling(struct systick_device *sdev, int status)
|
||||
+{
|
||||
+ if (sdev->freq_scale == status)
|
||||
+ return;
|
||||
+
|
||||
+ sdev->freq_scale = status;
|
||||
+
|
||||
+ pr_info("%s: %s autosleep mode\n", systick.dev.name,
|
||||
+ (status) ? ("enable") : ("disable"));
|
||||
+ if (status)
|
||||
+ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) | SLEEP_EN, CLK_LUT_CFG);
|
||||
+ else
|
||||
+ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) & ~SLEEP_EN, CLK_LUT_CFG);
|
||||
+}
|
||||
+
|
||||
static int systick_next_event(unsigned long delta,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
@@ -99,6 +120,9 @@ static int systick_shutdown(struct clock
|
||||
sdev->irq_requested = 0;
|
||||
iowrite32(0, systick.membase + SYSTICK_CONFIG);
|
||||
|
||||
+ if (systick_freq_scaling)
|
||||
+ systick_freq_scaling(sdev, 0);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -114,15 +138,29 @@ static int systick_set_oneshot(struct cl
|
||||
iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN,
|
||||
systick.membase + SYSTICK_CONFIG);
|
||||
|
||||
+ if (systick_freq_scaling)
|
||||
+ systick_freq_scaling(sdev, 1);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static const struct of_device_id systick_match[] = {
|
||||
+ { .compatible = "ralink,mt7620-systick", .data = mt7620_freq_scaling},
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
static void __init ralink_systick_init(struct device_node *np)
|
||||
{
|
||||
+ const struct of_device_id *match;
|
||||
+
|
||||
systick.membase = of_iomap(np, 0);
|
||||
if (!systick.membase)
|
||||
return;
|
||||
|
||||
+ match = of_match_node(systick_match, np);
|
||||
+ if (match)
|
||||
+ systick_freq_scaling = match->data;
|
||||
+
|
||||
systick_irqaction.name = np->name;
|
||||
systick.dev.name = np->name;
|
||||
clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
|
|
@ -0,0 +1,21 @@
|
|||
From 67b7bff0fd364c194e653f69baa623ba2141bd4c Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 4 Aug 2014 18:46:02 +0200
|
||||
Subject: [PATCH 07/53] MIPS: ralink: copy the commandline from the devicetree
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/of.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/arch/mips/ralink/of.c
|
||||
+++ b/arch/mips/ralink/of.c
|
||||
@@ -74,6 +74,8 @@ void __init plat_mem_setup(void)
|
||||
*/
|
||||
__dt_setup_arch(__dtb_start);
|
||||
|
||||
+ strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
|
||||
+
|
||||
of_scan_flat_dt(early_init_dt_find_memory, NULL);
|
||||
if (memory_dtb)
|
||||
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
|
|
@ -0,0 +1,433 @@
|
|||
From 41aa7fc236fdb1f4c9b8b10df9b71f0d248cb36b Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:11:12 +0100
|
||||
Subject: [PATCH 09/53] PCI: MIPS: adds mt7620a pcie driver
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/include/asm/mach-ralink/mt7620.h | 1 +
|
||||
arch/mips/pci/Makefile | 1 +
|
||||
arch/mips/pci/pci-mt7620.c | 396 ++++++++++++++++++++++++++++
|
||||
arch/mips/ralink/Kconfig | 1 +
|
||||
4 files changed, 399 insertions(+)
|
||||
create mode 100644 arch/mips/pci/pci-mt7620.c
|
||||
|
||||
--- a/arch/mips/pci/Makefile
|
||||
+++ b/arch/mips/pci/Makefile
|
||||
@@ -43,6 +43,7 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1
|
||||
obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o
|
||||
obj-$(CONFIG_LANTIQ) += fixup-lantiq.o
|
||||
obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o
|
||||
+obj-$(CONFIG_SOC_MT7620) += pci-mt7620.o
|
||||
obj-$(CONFIG_SOC_MT7621) += pci-mt7621.o
|
||||
obj-$(CONFIG_SOC_RT288X) += pci-rt2880.o
|
||||
obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/pci/pci-mt7620.c
|
||||
@@ -0,0 +1,396 @@
|
||||
+/*
|
||||
+ * Ralink MT7620A SoC PCI support
|
||||
+ *
|
||||
+ * Copyright (C) 2007-2013 Bruce Chang
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/pci.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/of_pci.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+#include <asm/mach-ralink/mt7620.h>
|
||||
+
|
||||
+#define RALINK_PCI_MM_MAP_BASE 0x20000000
|
||||
+#define RALINK_PCI_IO_MAP_BASE 0x10160000
|
||||
+
|
||||
+#define RALINK_INT_PCIE0 4
|
||||
+#define RALINK_SYSCFG1 0x14
|
||||
+#define RALINK_CLKCFG1 0x30
|
||||
+#define RALINK_GPIOMODE 0x60
|
||||
+#define RALINK_PCIE_CLK_GEN 0x7c
|
||||
+#define RALINK_PCIE_CLK_GEN1 0x80
|
||||
+#define PCIEPHY0_CFG 0x90
|
||||
+#define PPLL_CFG1 0x9c
|
||||
+#define PPLL_DRV 0xa0
|
||||
+#define PDRV_SW_SET (1<<31)
|
||||
+#define LC_CKDRVPD_ (1<<19)
|
||||
+
|
||||
+#define RALINK_PCI_CONFIG_ADDR 0x20
|
||||
+#define RALINK_PCI_CONFIG_DATA_VIRT_REG 0x24
|
||||
+#define MEMORY_BASE 0x0
|
||||
+#define RALINK_PCIE0_RST (1<<26)
|
||||
+#define RALINK_PCI_BASE 0xB0140000
|
||||
+#define RALINK_PCI_MEMBASE 0x28
|
||||
+#define RALINK_PCI_IOBASE 0x2C
|
||||
+
|
||||
+#define RT6855_PCIE0_OFFSET 0x2000
|
||||
+
|
||||
+#define RALINK_PCI_PCICFG_ADDR 0x00
|
||||
+#define RALINK_PCI0_BAR0SETUP_ADDR 0x10
|
||||
+#define RALINK_PCI0_IMBASEBAR0_ADDR 0x18
|
||||
+#define RALINK_PCI0_ID 0x30
|
||||
+#define RALINK_PCI0_CLASS 0x34
|
||||
+#define RALINK_PCI0_SUBID 0x38
|
||||
+#define RALINK_PCI0_STATUS 0x50
|
||||
+#define RALINK_PCI_PCIMSK_ADDR 0x0C
|
||||
+
|
||||
+#define RALINK_PCIEPHY_P0_CTL_OFFSET 0x7498
|
||||
+#define RALINK_PCIE0_CLK_EN (1 << 26)
|
||||
+
|
||||
+#define BUSY 0x80000000
|
||||
+#define WAITRETRY_MAX 10
|
||||
+#define WRITE_MODE (1UL << 23)
|
||||
+#define DATA_SHIFT 0
|
||||
+#define ADDR_SHIFT 8
|
||||
+
|
||||
+static void __iomem *bridge_base;
|
||||
+static void __iomem *pcie_base;
|
||||
+
|
||||
+static struct reset_control *rstpcie0;
|
||||
+
|
||||
+static inline void bridge_w32(u32 val, unsigned reg)
|
||||
+{
|
||||
+ iowrite32(val, bridge_base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline u32 bridge_r32(unsigned reg)
|
||||
+{
|
||||
+ return ioread32(bridge_base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void pcie_w32(u32 val, unsigned reg)
|
||||
+{
|
||||
+ iowrite32(val, pcie_base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline u32 pcie_r32(unsigned reg)
|
||||
+{
|
||||
+ return ioread32(pcie_base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void pcie_m32(u32 clr, u32 set, unsigned reg)
|
||||
+{
|
||||
+ u32 val = pcie_r32(reg);
|
||||
+
|
||||
+ val &= ~clr;
|
||||
+ val |= set;
|
||||
+ pcie_w32(val, reg);
|
||||
+}
|
||||
+
|
||||
+static int wait_pciephy_busy(void)
|
||||
+{
|
||||
+ unsigned long reg_value = 0x0, retry = 0;
|
||||
+
|
||||
+ while (1) {
|
||||
+ reg_value = pcie_r32(PCIEPHY0_CFG);
|
||||
+
|
||||
+ if (reg_value & BUSY)
|
||||
+ mdelay(100);
|
||||
+ else
|
||||
+ break;
|
||||
+ if (retry++ > WAITRETRY_MAX){
|
||||
+ printk("PCIE-PHY retry failed.\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void pcie_phy(unsigned long addr, unsigned long val)
|
||||
+{
|
||||
+ wait_pciephy_busy();
|
||||
+ pcie_w32(WRITE_MODE | (val << DATA_SHIFT) | (addr << ADDR_SHIFT), PCIEPHY0_CFG);
|
||||
+ mdelay(1);
|
||||
+ wait_pciephy_busy();
|
||||
+}
|
||||
+
|
||||
+static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val)
|
||||
+{
|
||||
+ unsigned int slot = PCI_SLOT(devfn);
|
||||
+ u8 func = PCI_FUNC(devfn);
|
||||
+ u32 address;
|
||||
+ u32 data;
|
||||
+ u32 num = 0;
|
||||
+
|
||||
+ if (bus)
|
||||
+ num = bus->number;
|
||||
+
|
||||
+ address = (((where & 0xF00) >> 8) << 24) | (num << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000;
|
||||
+ bridge_w32(address, RALINK_PCI_CONFIG_ADDR);
|
||||
+ data = bridge_r32(RALINK_PCI_CONFIG_DATA_VIRT_REG);
|
||||
+
|
||||
+ switch (size) {
|
||||
+ case 1:
|
||||
+ *val = (data >> ((where & 3) << 3)) & 0xff;
|
||||
+ break;
|
||||
+ case 2:
|
||||
+ *val = (data >> ((where & 3) << 3)) & 0xffff;
|
||||
+ break;
|
||||
+ case 4:
|
||||
+ *val = data;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return PCIBIOS_SUCCESSFUL;
|
||||
+}
|
||||
+
|
||||
+static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
|
||||
+{
|
||||
+ unsigned int slot = PCI_SLOT(devfn);
|
||||
+ u8 func = PCI_FUNC(devfn);
|
||||
+ u32 address;
|
||||
+ u32 data;
|
||||
+ u32 num = 0;
|
||||
+
|
||||
+ if (bus)
|
||||
+ num = bus->number;
|
||||
+
|
||||
+ address = (((where & 0xF00) >> 8) << 24) | (num << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000;
|
||||
+ bridge_w32(address, RALINK_PCI_CONFIG_ADDR);
|
||||
+ data = bridge_r32(RALINK_PCI_CONFIG_DATA_VIRT_REG);
|
||||
+
|
||||
+ switch (size) {
|
||||
+ case 1:
|
||||
+ data = (data & ~(0xff << ((where & 3) << 3))) |
|
||||
+ (val << ((where & 3) << 3));
|
||||
+ break;
|
||||
+ case 2:
|
||||
+ data = (data & ~(0xffff << ((where & 3) << 3))) |
|
||||
+ (val << ((where & 3) << 3));
|
||||
+ break;
|
||||
+ case 4:
|
||||
+ data = val;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ bridge_w32(data, RALINK_PCI_CONFIG_DATA_VIRT_REG);
|
||||
+
|
||||
+ return PCIBIOS_SUCCESSFUL;
|
||||
+}
|
||||
+
|
||||
+struct pci_ops mt7620_pci_ops= {
|
||||
+ .read = pci_config_read,
|
||||
+ .write = pci_config_write,
|
||||
+};
|
||||
+
|
||||
+static struct resource mt7620_res_pci_mem1;
|
||||
+static struct resource mt7620_res_pci_io1;
|
||||
+struct pci_controller mt7620_controller = {
|
||||
+ .pci_ops = &mt7620_pci_ops,
|
||||
+ .mem_resource = &mt7620_res_pci_mem1,
|
||||
+ .mem_offset = 0x00000000UL,
|
||||
+ .io_resource = &mt7620_res_pci_io1,
|
||||
+ .io_offset = 0x00000000UL,
|
||||
+ .io_map_base = 0xa0000000,
|
||||
+};
|
||||
+
|
||||
+static int mt7620_pci_hw_init(struct platform_device *pdev) {
|
||||
+ /* PCIE: bypass PCIe DLL */
|
||||
+ pcie_phy(0x0, 0x80);
|
||||
+ pcie_phy(0x1, 0x04);
|
||||
+
|
||||
+ /* PCIE: Elastic buffer control */
|
||||
+ pcie_phy(0x68, 0xB4);
|
||||
+
|
||||
+ pcie_m32(0, BIT(1), RALINK_PCI_PCICFG_ADDR);
|
||||
+
|
||||
+ reset_control_assert(rstpcie0);
|
||||
+
|
||||
+ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1);
|
||||
+ rt_sysc_m32(BIT(19), BIT(31), PPLL_DRV);
|
||||
+
|
||||
+ reset_control_deassert(rstpcie0);
|
||||
+ rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1);
|
||||
+
|
||||
+ mdelay(100);
|
||||
+
|
||||
+ if (!(rt_sysc_r32(PPLL_CFG1) & BIT(23))) {
|
||||
+ dev_err(&pdev->dev, "MT7620 PPLL unlock\n");
|
||||
+ reset_control_assert(rstpcie0);
|
||||
+ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ rt_sysc_m32(BIT(18) | BIT(17), BIT(19) | BIT(31), PPLL_DRV);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7628_pci_hw_init(struct platform_device *pdev) {
|
||||
+ u32 val = 0;
|
||||
+
|
||||
+ rt_sysc_m32(BIT(16), 0, RALINK_GPIOMODE);
|
||||
+ reset_control_deassert(rstpcie0);
|
||||
+ rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1);
|
||||
+ mdelay(100);
|
||||
+
|
||||
+ pcie_m32(~0xff, 0x5, RALINK_PCIEPHY_P0_CTL_OFFSET);
|
||||
+
|
||||
+ pci_config_read(NULL, 0, 0x70c, 4, &val);
|
||||
+ val &= ~(0xff) << 8;
|
||||
+ val |= 0x50 << 8;
|
||||
+ pci_config_write(NULL, 0, 0x70c, 4, val);
|
||||
+
|
||||
+ pci_config_read(NULL, 0, 0x70c, 4, &val);
|
||||
+ dev_err(&pdev->dev, "Port 0 N_FTS = %x\n", (unsigned int) val);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_pci_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *bridge_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ struct resource *pcie_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
+ u32 val = 0;
|
||||
+
|
||||
+ rstpcie0 = devm_reset_control_get(&pdev->dev, "pcie0");
|
||||
+ if (IS_ERR(rstpcie0))
|
||||
+ return PTR_ERR(rstpcie0);
|
||||
+
|
||||
+ bridge_base = devm_ioremap_resource(&pdev->dev, bridge_res);
|
||||
+ if (!bridge_base)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ pcie_base = devm_ioremap_resource(&pdev->dev, pcie_res);
|
||||
+ if (!pcie_base)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ iomem_resource.start = 0;
|
||||
+ iomem_resource.end = ~0;
|
||||
+ ioport_resource.start = 0;
|
||||
+ ioport_resource.end = ~0;
|
||||
+
|
||||
+ /* bring up the pci core */
|
||||
+ switch (ralink_soc) {
|
||||
+ case MT762X_SOC_MT7620A:
|
||||
+ if (mt7620_pci_hw_init(pdev))
|
||||
+ return -1;
|
||||
+ break;
|
||||
+
|
||||
+ case MT762X_SOC_MT7628AN:
|
||||
+ if (mt7628_pci_hw_init(pdev))
|
||||
+ return -1;
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ dev_err(&pdev->dev, "pcie is not supported on this hardware\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+ mdelay(50);
|
||||
+
|
||||
+ /* enable write access */
|
||||
+ pcie_m32(BIT(1), 0, RALINK_PCI_PCICFG_ADDR);
|
||||
+ mdelay(100);
|
||||
+
|
||||
+ /* check if there is a card present */
|
||||
+ if ((pcie_r32(RALINK_PCI0_STATUS) & 0x1) == 0) {
|
||||
+ reset_control_assert(rstpcie0);
|
||||
+ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1);
|
||||
+ if (ralink_soc == MT762X_SOC_MT7620A)
|
||||
+ rt_sysc_m32(LC_CKDRVPD_, PDRV_SW_SET, PPLL_DRV);
|
||||
+ dev_err(&pdev->dev, "PCIE0 no card, disable it(RST&CLK)\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* setup ranges */
|
||||
+ bridge_w32(0xffffffff, RALINK_PCI_MEMBASE);
|
||||
+ bridge_w32(RALINK_PCI_IO_MAP_BASE, RALINK_PCI_IOBASE);
|
||||
+
|
||||
+ pcie_w32(0x7FFF0001, RALINK_PCI0_BAR0SETUP_ADDR);
|
||||
+ pcie_w32(MEMORY_BASE, RALINK_PCI0_IMBASEBAR0_ADDR);
|
||||
+ pcie_w32(0x06040001, RALINK_PCI0_CLASS);
|
||||
+
|
||||
+ /* enable interrupts */
|
||||
+ pcie_m32(0, BIT(20), RALINK_PCI_PCIMSK_ADDR);
|
||||
+
|
||||
+ /* voodoo from the SDK driver */
|
||||
+ pci_config_read(NULL, 0, 4, 4, &val);
|
||||
+ pci_config_write(NULL, 0, 4, 4, val | 0x7);
|
||||
+
|
||||
+ pci_load_of_ranges(&mt7620_controller, pdev->dev.of_node);
|
||||
+ register_pci_controller(&mt7620_controller);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
+{
|
||||
+ u16 cmd;
|
||||
+ u32 val;
|
||||
+ int irq = 0;
|
||||
+
|
||||
+ if ((dev->bus->number == 0) && (slot == 0)) {
|
||||
+ pcie_w32(0x7FFF0001, RALINK_PCI0_BAR0SETUP_ADDR); //open 7FFF:2G; ENABLE
|
||||
+ pci_config_write(dev->bus, 0, PCI_BASE_ADDRESS_0, 4, MEMORY_BASE);
|
||||
+ pci_config_read(dev->bus, 0, PCI_BASE_ADDRESS_0, 4, &val);
|
||||
+ } else if ((dev->bus->number == 1) && (slot == 0x0)) {
|
||||
+ irq = RALINK_INT_PCIE0;
|
||||
+ } else {
|
||||
+ dev_err(&dev->dev, "no irq found - bus=0x%x, slot = 0x%x\n", dev->bus->number, slot);
|
||||
+ return 0;
|
||||
+ }
|
||||
+ dev_err(&dev->dev, "card - bus=0x%x, slot = 0x%x irq=%d\n", dev->bus->number, slot, irq);
|
||||
+
|
||||
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x14); //configure cache line size 0x14
|
||||
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xFF); //configure latency timer 0x10
|
||||
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
+
|
||||
+ // FIXME
|
||||
+ cmd = cmd | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
|
||||
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
|
||||
+ //pci_write_config_byte(dev, PCI_INTERRUPT_PIN, dev->irq);
|
||||
+
|
||||
+ return irq;
|
||||
+}
|
||||
+
|
||||
+int pcibios_plat_dev_init(struct pci_dev *dev)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mt7620_pci_ids[] = {
|
||||
+ { .compatible = "mediatek,mt7620-pci" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mt7620_pci_ids);
|
||||
+
|
||||
+static struct platform_driver mt7620_pci_driver = {
|
||||
+ .probe = mt7620_pci_probe,
|
||||
+ .driver = {
|
||||
+ .name = "mt7620-pci",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = of_match_ptr(mt7620_pci_ids),
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init mt7620_pci_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&mt7620_pci_driver);
|
||||
+}
|
||||
+
|
||||
+arch_initcall(mt7620_pci_init);
|
||||
--- a/arch/mips/ralink/Kconfig
|
||||
+++ b/arch/mips/ralink/Kconfig
|
||||
@@ -43,6 +43,7 @@ choice
|
||||
|
||||
config SOC_MT7620
|
||||
bool "MT7620/8"
|
||||
+ select HW_HAS_PCI
|
||||
|
||||
config SOC_MT7621
|
||||
bool "MT7621"
|
|
@ -0,0 +1,44 @@
|
|||
From 39ce22c870f4503bed5e451acfcab21eba3b6239 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:49:07 +0100
|
||||
Subject: [PATCH 10/53] arch: mips: ralink: add spi1 clocks
|
||||
|
||||
based on f3bc64d6d1f21c1b92d75f233a37b75d77af6963
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/mt7620.c | 1 +
|
||||
arch/mips/ralink/rt305x.c | 1 +
|
||||
arch/mips/ralink/rt3883.c | 1 +
|
||||
3 files changed, 3 insertions(+)
|
||||
|
||||
--- a/arch/mips/ralink/mt7620.c
|
||||
+++ b/arch/mips/ralink/mt7620.c
|
||||
@@ -436,6 +436,7 @@ void __init ralink_clk_init(void)
|
||||
ralink_clk_add("10000100.timer", periph_rate);
|
||||
ralink_clk_add("10000120.watchdog", periph_rate);
|
||||
ralink_clk_add("10000b00.spi", sys_rate);
|
||||
+ ralink_clk_add("10000b40.spi", sys_rate);
|
||||
ralink_clk_add("10000c00.uartlite", periph_rate);
|
||||
ralink_clk_add("10180000.wmac", xtal_rate);
|
||||
|
||||
--- a/arch/mips/ralink/rt305x.c
|
||||
+++ b/arch/mips/ralink/rt305x.c
|
||||
@@ -201,6 +201,7 @@ void __init ralink_clk_init(void)
|
||||
ralink_clk_add("cpu", cpu_rate);
|
||||
ralink_clk_add("sys", sys_rate);
|
||||
ralink_clk_add("10000b00.spi", sys_rate);
|
||||
+ ralink_clk_add("10000b40.spi", sys_rate);
|
||||
ralink_clk_add("10000100.timer", wdt_rate);
|
||||
ralink_clk_add("10000120.watchdog", wdt_rate);
|
||||
ralink_clk_add("10000500.uart", uart_rate);
|
||||
--- a/arch/mips/ralink/rt3883.c
|
||||
+++ b/arch/mips/ralink/rt3883.c
|
||||
@@ -109,6 +109,7 @@ void __init ralink_clk_init(void)
|
||||
ralink_clk_add("10000120.watchdog", sys_rate);
|
||||
ralink_clk_add("10000500.uart", 40000000);
|
||||
ralink_clk_add("10000b00.spi", sys_rate);
|
||||
+ ralink_clk_add("10000b40.spi", sys_rate);
|
||||
ralink_clk_add("10000c00.uartlite", 40000000);
|
||||
ralink_clk_add("10100000.ethernet", sys_rate);
|
||||
ralink_clk_add("10180000.wmac", 40000000);
|
|
@ -0,0 +1,28 @@
|
|||
From 5ede027f6c4a57ed25da872420508b7f1168b36b Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:15:32 +0100
|
||||
Subject: [PATCH 13/53] owrt: hack: fix mt7688 cache issue
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/kernel/setup.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/mips/kernel/setup.c
|
||||
+++ b/arch/mips/kernel/setup.c
|
||||
@@ -696,7 +696,6 @@ static void __init arch_mem_init(char **
|
||||
crashk_res.end - crashk_res.start + 1,
|
||||
BOOTMEM_DEFAULT);
|
||||
#endif
|
||||
- device_tree_init();
|
||||
sparse_init();
|
||||
plat_swiotlb_setup();
|
||||
paging_init();
|
||||
@@ -806,6 +805,7 @@ void __init setup_arch(char **cmdline_p)
|
||||
prefill_possible_map();
|
||||
|
||||
cpu_cache_init();
|
||||
+ device_tree_init();
|
||||
}
|
||||
|
||||
unsigned long kernelsp[NR_CPUS];
|
|
@ -0,0 +1,165 @@
|
|||
From e6ed424c36458aff8738fb1fbb0141196678058a Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:17:23 +0100
|
||||
Subject: [PATCH 14/53] arch: mips: cleanup cevt-rt3352
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/cevt-rt3352.c | 85 ++++++++++++++++++++++++++--------------
|
||||
1 file changed, 56 insertions(+), 29 deletions(-)
|
||||
|
||||
--- a/arch/mips/ralink/cevt-rt3352.c
|
||||
+++ b/arch/mips/ralink/cevt-rt3352.c
|
||||
@@ -52,7 +52,7 @@ static inline void mt7620_freq_scaling(s
|
||||
|
||||
sdev->freq_scale = status;
|
||||
|
||||
- pr_info("%s: %s autosleep mode\n", systick.dev.name,
|
||||
+ pr_info("%s: %s autosleep mode\n", sdev->dev.name,
|
||||
(status) ? ("enable") : ("disable"));
|
||||
if (status)
|
||||
rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) | SLEEP_EN, CLK_LUT_CFG);
|
||||
@@ -60,18 +60,33 @@ static inline void mt7620_freq_scaling(s
|
||||
rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) & ~SLEEP_EN, CLK_LUT_CFG);
|
||||
}
|
||||
|
||||
+static inline unsigned int read_count(struct systick_device *sdev)
|
||||
+{
|
||||
+ return ioread32(sdev->membase + SYSTICK_COUNT);
|
||||
+}
|
||||
+
|
||||
+static inline unsigned int read_compare(struct systick_device *sdev)
|
||||
+{
|
||||
+ return ioread32(sdev->membase + SYSTICK_COMPARE);
|
||||
+}
|
||||
+
|
||||
+static inline void write_compare(struct systick_device *sdev, unsigned int val)
|
||||
+{
|
||||
+ iowrite32(val, sdev->membase + SYSTICK_COMPARE);
|
||||
+}
|
||||
+
|
||||
static int systick_next_event(unsigned long delta,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
struct systick_device *sdev;
|
||||
- u32 count;
|
||||
+ int res;
|
||||
|
||||
sdev = container_of(evt, struct systick_device, dev);
|
||||
- count = ioread32(sdev->membase + SYSTICK_COUNT);
|
||||
- count = (count + delta) % SYSTICK_FREQ;
|
||||
- iowrite32(count, sdev->membase + SYSTICK_COMPARE);
|
||||
+ delta += read_count(sdev);
|
||||
+ write_compare(sdev, delta);
|
||||
+ res = ((int)(read_count(sdev) - delta) >= 0) ? -ETIME : 0;
|
||||
|
||||
- return 0;
|
||||
+ return res;
|
||||
}
|
||||
|
||||
static void systick_event_handler(struct clock_event_device *dev)
|
||||
@@ -81,20 +96,25 @@ static void systick_event_handler(struct
|
||||
|
||||
static irqreturn_t systick_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
- struct clock_event_device *dev = (struct clock_event_device *) dev_id;
|
||||
+ int ret = 0;
|
||||
+ struct clock_event_device *cdev;
|
||||
+ struct systick_device *sdev;
|
||||
|
||||
- dev->event_handler(dev);
|
||||
+ if (read_c0_cause() & STATUSF_IP7) {
|
||||
+ cdev = (struct clock_event_device *) dev_id;
|
||||
+ sdev = container_of(cdev, struct systick_device, dev);
|
||||
+
|
||||
+ /* Clear Count/Compare Interrupt */
|
||||
+ write_compare(sdev, read_compare(sdev));
|
||||
+ cdev->event_handler(cdev);
|
||||
+ ret = 1;
|
||||
+ }
|
||||
|
||||
- return IRQ_HANDLED;
|
||||
+ return IRQ_RETVAL(ret);
|
||||
}
|
||||
|
||||
static struct systick_device systick = {
|
||||
.dev = {
|
||||
- /*
|
||||
- * cevt-r4k uses 300, make sure systick
|
||||
- * gets used if available
|
||||
- */
|
||||
- .rating = 310,
|
||||
.features = CLOCK_EVT_FEAT_ONESHOT,
|
||||
.set_next_event = systick_next_event,
|
||||
.set_state_shutdown = systick_shutdown,
|
||||
@@ -116,9 +136,9 @@ static int systick_shutdown(struct clock
|
||||
sdev = container_of(evt, struct systick_device, dev);
|
||||
|
||||
if (sdev->irq_requested)
|
||||
- free_irq(systick.dev.irq, &systick_irqaction);
|
||||
+ remove_irq(systick.dev.irq, &systick_irqaction);
|
||||
sdev->irq_requested = 0;
|
||||
- iowrite32(0, systick.membase + SYSTICK_CONFIG);
|
||||
+ iowrite32(CFG_CNT_EN, systick.membase + SYSTICK_CONFIG);
|
||||
|
||||
if (systick_freq_scaling)
|
||||
systick_freq_scaling(sdev, 0);
|
||||
@@ -145,38 +165,45 @@ static int systick_set_oneshot(struct cl
|
||||
}
|
||||
|
||||
static const struct of_device_id systick_match[] = {
|
||||
- { .compatible = "ralink,mt7620-systick", .data = mt7620_freq_scaling},
|
||||
+ { .compatible = "ralink,mt7620a-systick", .data = mt7620_freq_scaling},
|
||||
{},
|
||||
};
|
||||
|
||||
static void __init ralink_systick_init(struct device_node *np)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
+ int rating = 200;
|
||||
|
||||
systick.membase = of_iomap(np, 0);
|
||||
if (!systick.membase)
|
||||
return;
|
||||
|
||||
match = of_match_node(systick_match, np);
|
||||
- if (match)
|
||||
+ if (match) {
|
||||
systick_freq_scaling = match->data;
|
||||
+ /*
|
||||
+ * cevt-r4k uses 300, make sure systick
|
||||
+ * gets used if available
|
||||
+ */
|
||||
+ rating = 310;
|
||||
+ }
|
||||
|
||||
- systick_irqaction.name = np->name;
|
||||
- systick.dev.name = np->name;
|
||||
- clockevents_calc_mult_shift(&systick.dev, SYSTICK_FREQ, 60);
|
||||
- systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev);
|
||||
- systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev);
|
||||
+ /* enable counter than register clock source */
|
||||
+ iowrite32(CFG_CNT_EN, systick.membase + SYSTICK_CONFIG);
|
||||
+ clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
|
||||
+ SYSTICK_FREQ, rating, 16, clocksource_mmio_readl_up);
|
||||
+
|
||||
+ /* register clock event */
|
||||
systick.dev.irq = irq_of_parse_and_map(np, 0);
|
||||
if (!systick.dev.irq) {
|
||||
pr_err("%s: request_irq failed", np->name);
|
||||
return;
|
||||
}
|
||||
-
|
||||
- clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
|
||||
- SYSTICK_FREQ, 301, 16, clocksource_mmio_readl_up);
|
||||
-
|
||||
- clockevents_register_device(&systick.dev);
|
||||
-
|
||||
+ systick_irqaction.name = np->name;
|
||||
+ systick.dev.name = np->name;
|
||||
+ systick.dev.rating = rating;
|
||||
+ systick.dev.cpumask = cpumask_of(0);
|
||||
+ clockevents_config_and_register(&systick.dev, SYSTICK_FREQ, 0x3, 0x7fff);
|
||||
pr_info("%s: running - mult: %d, shift: %d\n",
|
||||
np->name, systick.dev.mult, systick.dev.shift);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
From 9e6ce539092a1dd605a20bf73c655a9de58d8641 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:18:05 +0100
|
||||
Subject: [PATCH 15/53] arch: mips: do not select illegal access driver by
|
||||
default
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/Kconfig | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/mips/ralink/Kconfig
|
||||
+++ b/arch/mips/ralink/Kconfig
|
||||
@@ -13,9 +13,9 @@ config CLKEVT_RT3352
|
||||
select CEVT_SYSTICK_QUIRK
|
||||
|
||||
config RALINK_ILL_ACC
|
||||
- bool
|
||||
+ bool "illegal access irq"
|
||||
depends on SOC_RT305X
|
||||
- default y
|
||||
+ default n
|
||||
|
||||
config IRQ_INTC
|
||||
bool
|
|
@ -0,0 +1,79 @@
|
|||
From 43372c2be9fcf68bc40c322039c75893ce4e982c Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:20:47 +0100
|
||||
Subject: [PATCH 19/53] arch: mips: ralink: add mt7621 cpu-feature-overrides
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
.../asm/mach-ralink/mt7621/cpu-feature-overrides.h | 65 ++++++++++++++++++++
|
||||
1 file changed, 65 insertions(+)
|
||||
create mode 100644 arch/mips/include/asm/mach-ralink/mt7621/cpu-feature-overrides.h
|
||||
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/include/asm/mach-ralink/mt7621/cpu-feature-overrides.h
|
||||
@@ -0,0 +1,65 @@
|
||||
+/*
|
||||
+ * Ralink MT7621 specific CPU feature overrides
|
||||
+ *
|
||||
+ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
+ * Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ *
|
||||
+ * This file was derived from: include/asm-mips/cpu-features.h
|
||||
+ * Copyright (C) 2003, 2004 Ralf Baechle
|
||||
+ * Copyright (C) 2004 Maciej W. Rozycki
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ *
|
||||
+ */
|
||||
+#ifndef _MT7621_CPU_FEATURE_OVERRIDES_H
|
||||
+#define _MT7621_CPU_FEATURE_OVERRIDES_H
|
||||
+
|
||||
+#define cpu_has_tlb 1
|
||||
+#define cpu_has_4kex 1
|
||||
+#define cpu_has_3k_cache 0
|
||||
+#define cpu_has_4k_cache 1
|
||||
+#define cpu_has_tx39_cache 0
|
||||
+#define cpu_has_sb1_cache 0
|
||||
+#define cpu_has_fpu 0
|
||||
+#define cpu_has_32fpr 0
|
||||
+#define cpu_has_counter 1
|
||||
+#define cpu_has_watch 1
|
||||
+#define cpu_has_divec 1
|
||||
+
|
||||
+#define cpu_has_prefetch 1
|
||||
+#define cpu_has_ejtag 1
|
||||
+#define cpu_has_llsc 1
|
||||
+
|
||||
+#define cpu_has_mips16 1
|
||||
+#define cpu_has_mdmx 0
|
||||
+#define cpu_has_mips3d 0
|
||||
+#define cpu_has_smartmips 0
|
||||
+
|
||||
+#define cpu_has_mips32r1 1
|
||||
+#define cpu_has_mips32r2 1
|
||||
+#define cpu_has_mips64r1 0
|
||||
+#define cpu_has_mips64r2 0
|
||||
+
|
||||
+#define cpu_has_dsp 1
|
||||
+#define cpu_has_dsp2 0
|
||||
+#define cpu_has_mipsmt 1
|
||||
+
|
||||
+#define cpu_has_64bits 0
|
||||
+#define cpu_has_64bit_zero_reg 0
|
||||
+#define cpu_has_64bit_gp_regs 0
|
||||
+#define cpu_has_64bit_addresses 0
|
||||
+
|
||||
+#define cpu_dcache_line_size() 32
|
||||
+#define cpu_icache_line_size() 32
|
||||
+
|
||||
+#define cpu_has_dc_aliases 0
|
||||
+#define cpu_has_vtag_icache 0
|
||||
+
|
||||
+#define cpu_has_rixi 0
|
||||
+#define cpu_has_tlbinv 0
|
||||
+#define cpu_has_userlocal 1
|
||||
+
|
||||
+#endif /* _MT7621_CPU_FEATURE_OVERRIDES_H */
|
|
@ -0,0 +1,21 @@
|
|||
From 2e5d90398aacde3e46dfd87e6f716b00a0ffcd83 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:30:11 +0100
|
||||
Subject: [PATCH 22/53] arch: mips: ralink: proper vendor id srtring
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/mt7620.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/mips/ralink/mt7620.c
|
||||
+++ b/arch/mips/ralink/mt7620.c
|
||||
@@ -553,7 +553,7 @@ void prom_soc_init(struct ralink_soc_inf
|
||||
}
|
||||
|
||||
snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN,
|
||||
- "Ralink %s ver:%u eco:%u",
|
||||
+ "MediaTek %s ver:%u eco:%u",
|
||||
name,
|
||||
(rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK,
|
||||
(rev & CHIP_REV_ECO_MASK));
|
|
@ -0,0 +1,166 @@
|
|||
From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Tue, 12 Aug 2014 20:49:27 +0200
|
||||
Subject: [PATCH 24/53] GPIO: add named gpio exports
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/gpio/gpiolib-sysfs.c | 10 +++++-
|
||||
include/asm-generic/gpio.h | 6 ++++
|
||||
include/linux/gpio/consumer.h | 8 +++++
|
||||
4 files changed, 91 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/gpio/gpiolib-of.c
|
||||
+++ b/drivers/gpio/gpiolib-of.c
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/platform_device.h>
|
||||
|
||||
#include "gpiolib.h"
|
||||
|
||||
@@ -450,3 +452,69 @@ void of_gpiochip_remove(struct gpio_chip
|
||||
gpiochip_remove_pin_ranges(chip);
|
||||
of_node_put(chip->of_node);
|
||||
}
|
||||
+
|
||||
+static struct of_device_id gpio_export_ids[] = {
|
||||
+ { .compatible = "gpio-export" },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+
|
||||
+static int __init of_gpio_export_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct device_node *cnp;
|
||||
+ u32 val;
|
||||
+ int nb = 0;
|
||||
+
|
||||
+ for_each_child_of_node(np, cnp) {
|
||||
+ const char *name = NULL;
|
||||
+ int gpio;
|
||||
+ bool dmc;
|
||||
+ int max_gpio = 1;
|
||||
+ int i;
|
||||
+
|
||||
+ of_property_read_string(cnp, "gpio-export,name", &name);
|
||||
+
|
||||
+ if (!name)
|
||||
+ max_gpio = of_gpio_count(cnp);
|
||||
+
|
||||
+ for (i = 0; i < max_gpio; i++) {
|
||||
+ unsigned flags = 0;
|
||||
+ enum of_gpio_flags of_flags;
|
||||
+
|
||||
+ gpio = of_get_gpio_flags(cnp, i, &of_flags);
|
||||
+
|
||||
+ if (of_flags == OF_GPIO_ACTIVE_LOW)
|
||||
+ flags |= GPIOF_ACTIVE_LOW;
|
||||
+
|
||||
+ if (!of_property_read_u32(cnp, "gpio-export,output", &val))
|
||||
+ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
|
||||
+ else
|
||||
+ flags |= GPIOF_IN;
|
||||
+
|
||||
+ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
|
||||
+ continue;
|
||||
+
|
||||
+ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
|
||||
+ gpio_export_with_name(gpio, dmc, name);
|
||||
+ nb++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver gpio_export_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "gpio-export",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = of_match_ptr(gpio_export_ids),
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init of_gpio_export_init(void)
|
||||
+{
|
||||
+ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe);
|
||||
+}
|
||||
+device_initcall(of_gpio_export_init);
|
||||
--- a/drivers/gpio/gpiolib-sysfs.c
|
||||
+++ b/drivers/gpio/gpiolib-sysfs.c
|
||||
@@ -544,7 +544,7 @@ static struct class gpio_class = {
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
struct gpiod_data *data;
|
||||
@@ -604,6 +604,8 @@ int gpiod_export(struct gpio_desc *desc,
|
||||
offset = gpio_chip_hwgpio(desc);
|
||||
if (chip->names && chip->names[offset])
|
||||
ioname = chip->names[offset];
|
||||
+ if (name)
|
||||
+ ioname = name;
|
||||
|
||||
dev = device_create_with_groups(&gpio_class, chip->dev,
|
||||
MKDEV(0, 0), data, gpio_groups,
|
||||
@@ -625,6 +627,12 @@ err_unlock:
|
||||
gpiod_dbg(desc, "%s: status %d\n", __func__, status);
|
||||
return status;
|
||||
}
|
||||
+EXPORT_SYMBOL_GPL(__gpiod_export);
|
||||
+
|
||||
+int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
|
||||
+{
|
||||
+ return __gpiod_export(desc, direction_may_change, NULL);
|
||||
+}
|
||||
EXPORT_SYMBOL_GPL(gpiod_export);
|
||||
|
||||
static int match_export(struct device *dev, const void *desc)
|
||||
--- a/include/asm-generic/gpio.h
|
||||
+++ b/include/asm-generic/gpio.h
|
||||
@@ -122,6 +122,12 @@ static inline int gpio_export(unsigned g
|
||||
return gpiod_export(gpio_to_desc(gpio), direction_may_change);
|
||||
}
|
||||
|
||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
|
||||
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
|
||||
+{
|
||||
+ return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
|
||||
+}
|
||||
+
|
||||
static inline int gpio_export_link(struct device *dev, const char *name,
|
||||
unsigned gpio)
|
||||
{
|
||||
--- a/include/linux/gpio/consumer.h
|
||||
+++ b/include/linux/gpio/consumer.h
|
||||
@@ -427,6 +427,7 @@ static inline struct gpio_desc *devm_get
|
||||
|
||||
#if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
|
||||
|
||||
+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
|
||||
int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
|
||||
int gpiod_export_link(struct device *dev, const char *name,
|
||||
struct gpio_desc *desc);
|
||||
@@ -434,6 +435,13 @@ void gpiod_unexport(struct gpio_desc *de
|
||||
|
||||
#else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
|
||||
|
||||
+static inline int _gpiod_export(struct gpio_desc *desc,
|
||||
+ bool direction_may_change,
|
||||
+ const char *name)
|
||||
+{
|
||||
+ return -ENOSYS;
|
||||
+}
|
||||
+
|
||||
static inline int gpiod_export(struct gpio_desc *desc,
|
||||
bool direction_may_change)
|
||||
{
|
|
@ -0,0 +1,524 @@
|
|||
From 7adbe9a88c33c6e362a10b109d963b5500a21f00 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:34:05 +0100
|
||||
Subject: [PATCH 25/53] pinctrl: ralink: add pinctrl driver
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/Kconfig | 2 +
|
||||
drivers/pinctrl/Kconfig | 5 +
|
||||
drivers/pinctrl/Makefile | 1 +
|
||||
drivers/pinctrl/pinctrl-rt2880.c | 474 ++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 482 insertions(+)
|
||||
create mode 100644 drivers/pinctrl/pinctrl-rt2880.c
|
||||
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -585,6 +585,8 @@ config RALINK
|
||||
select CLKDEV_LOOKUP
|
||||
select ARCH_HAS_RESET_CONTROLLER
|
||||
select RESET_CONTROLLER
|
||||
+ select PINCTRL
|
||||
+ select PINCTRL_RT2880
|
||||
|
||||
config SGI_IP22
|
||||
bool "SGI IP22 (Indy/Indigo2)"
|
||||
--- a/drivers/pinctrl/Kconfig
|
||||
+++ b/drivers/pinctrl/Kconfig
|
||||
@@ -114,6 +114,11 @@ config PINCTRL_LPC18XX
|
||||
help
|
||||
Pinctrl driver for NXP LPC18xx/43xx System Control Unit (SCU).
|
||||
|
||||
+config PINCTRL_RT2880
|
||||
+ bool
|
||||
+ depends on RALINK
|
||||
+ select PINMUX
|
||||
+
|
||||
config PINCTRL_FALCON
|
||||
bool
|
||||
depends on SOC_FALCON
|
||||
--- a/drivers/pinctrl/Makefile
|
||||
+++ b/drivers/pinctrl/Makefile
|
||||
@@ -20,6 +20,7 @@ obj-$(CONFIG_PINCTRL_MESON) += meson/
|
||||
obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
|
||||
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
|
||||
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
|
||||
+obj-$(CONFIG_PINCTRL_RT2880) += pinctrl-rt2880.o
|
||||
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
|
||||
obj-$(CONFIG_PINCTRL_SIRF) += sirf/
|
||||
obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/pinctrl/pinctrl-rt2880.c
|
||||
@@ -0,0 +1,472 @@
|
||||
+/*
|
||||
+ * linux/drivers/pinctrl/pinctrl-rt2880.c
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * publishhed by the Free Software Foundation.
|
||||
+ *
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/device.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/pinctrl/pinctrl.h>
|
||||
+#include <linux/pinctrl/pinconf.h>
|
||||
+#include <linux/pinctrl/pinmux.h>
|
||||
+#include <linux/pinctrl/consumer.h>
|
||||
+#include <linux/pinctrl/machine.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+#include <asm/mach-ralink/pinmux.h>
|
||||
+#include <asm/mach-ralink/mt7620.h>
|
||||
+
|
||||
+#include "core.h"
|
||||
+
|
||||
+#define SYSC_REG_GPIO_MODE 0x60
|
||||
+#define SYSC_REG_GPIO_MODE2 0x64
|
||||
+
|
||||
+struct rt2880_priv {
|
||||
+ struct device *dev;
|
||||
+
|
||||
+ struct pinctrl_pin_desc *pads;
|
||||
+ struct pinctrl_desc *desc;
|
||||
+
|
||||
+ struct rt2880_pmx_func **func;
|
||||
+ int func_count;
|
||||
+
|
||||
+ struct rt2880_pmx_group *groups;
|
||||
+ const char **group_names;
|
||||
+ int group_count;
|
||||
+
|
||||
+ uint8_t *gpio;
|
||||
+ int max_pins;
|
||||
+};
|
||||
+
|
||||
+static int rt2880_get_group_count(struct pinctrl_dev *pctrldev)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ return p->group_count;
|
||||
+}
|
||||
+
|
||||
+static const char *rt2880_get_group_name(struct pinctrl_dev *pctrldev,
|
||||
+ unsigned group)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ if (group >= p->group_count)
|
||||
+ return NULL;
|
||||
+
|
||||
+ return p->group_names[group];
|
||||
+}
|
||||
+
|
||||
+static int rt2880_get_group_pins(struct pinctrl_dev *pctrldev,
|
||||
+ unsigned group,
|
||||
+ const unsigned **pins,
|
||||
+ unsigned *num_pins)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ if (group >= p->group_count)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *pins = p->groups[group].func[0].pins;
|
||||
+ *num_pins = p->groups[group].func[0].pin_count;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rt2880_pinctrl_dt_free_map(struct pinctrl_dev *pctrldev,
|
||||
+ struct pinctrl_map *map, unsigned num_maps)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < num_maps; i++)
|
||||
+ if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN ||
|
||||
+ map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
|
||||
+ kfree(map[i].data.configs.configs);
|
||||
+ kfree(map);
|
||||
+}
|
||||
+
|
||||
+static void rt2880_pinctrl_pin_dbg_show(struct pinctrl_dev *pctrldev,
|
||||
+ struct seq_file *s,
|
||||
+ unsigned offset)
|
||||
+{
|
||||
+ seq_printf(s, "ralink pio");
|
||||
+}
|
||||
+
|
||||
+static void rt2880_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctrldev,
|
||||
+ struct device_node *np,
|
||||
+ struct pinctrl_map **map)
|
||||
+{
|
||||
+ const char *function;
|
||||
+ int func = of_property_read_string(np, "ralink,function", &function);
|
||||
+ int grps = of_property_count_strings(np, "ralink,group");
|
||||
+ int i;
|
||||
+
|
||||
+ if (func || !grps)
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < grps; i++) {
|
||||
+ const char *group;
|
||||
+
|
||||
+ of_property_read_string_index(np, "ralink,group", i, &group);
|
||||
+
|
||||
+ (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
|
||||
+ (*map)->name = function;
|
||||
+ (*map)->data.mux.group = group;
|
||||
+ (*map)->data.mux.function = function;
|
||||
+ (*map)++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev,
|
||||
+ struct device_node *np_config,
|
||||
+ struct pinctrl_map **map,
|
||||
+ unsigned *num_maps)
|
||||
+{
|
||||
+ int max_maps = 0;
|
||||
+ struct pinctrl_map *tmp;
|
||||
+ struct device_node *np;
|
||||
+
|
||||
+ for_each_child_of_node(np_config, np) {
|
||||
+ int ret = of_property_count_strings(np, "ralink,group");
|
||||
+
|
||||
+ if (ret >= 0)
|
||||
+ max_maps += ret;
|
||||
+ }
|
||||
+
|
||||
+ if (!max_maps)
|
||||
+ return max_maps;
|
||||
+
|
||||
+ *map = kzalloc(max_maps * sizeof(struct pinctrl_map), GFP_KERNEL);
|
||||
+ if (!*map)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ tmp = *map;
|
||||
+
|
||||
+ for_each_child_of_node(np_config, np)
|
||||
+ rt2880_pinctrl_dt_subnode_to_map(pctrldev, np, &tmp);
|
||||
+ *num_maps = max_maps;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct pinctrl_ops rt2880_pctrl_ops = {
|
||||
+ .get_groups_count = rt2880_get_group_count,
|
||||
+ .get_group_name = rt2880_get_group_name,
|
||||
+ .get_group_pins = rt2880_get_group_pins,
|
||||
+ .pin_dbg_show = rt2880_pinctrl_pin_dbg_show,
|
||||
+ .dt_node_to_map = rt2880_pinctrl_dt_node_to_map,
|
||||
+ .dt_free_map = rt2880_pinctrl_dt_free_map,
|
||||
+};
|
||||
+
|
||||
+static int rt2880_pmx_func_count(struct pinctrl_dev *pctrldev)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ return p->func_count;
|
||||
+}
|
||||
+
|
||||
+static const char *rt2880_pmx_func_name(struct pinctrl_dev *pctrldev,
|
||||
+ unsigned func)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ return p->func[func]->name;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_pmx_group_get_groups(struct pinctrl_dev *pctrldev,
|
||||
+ unsigned func,
|
||||
+ const char * const **groups,
|
||||
+ unsigned * const num_groups)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ if (p->func[func]->group_count == 1)
|
||||
+ *groups = &p->group_names[p->func[func]->groups[0]];
|
||||
+ else
|
||||
+ *groups = p->group_names;
|
||||
+
|
||||
+ *num_groups = p->func[func]->group_count;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_pmx_group_enable(struct pinctrl_dev *pctrldev,
|
||||
+ unsigned func,
|
||||
+ unsigned group)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+ u32 mode = 0;
|
||||
+ u32 reg = SYSC_REG_GPIO_MODE;
|
||||
+ int i;
|
||||
+ int shift;
|
||||
+
|
||||
+ /* dont allow double use */
|
||||
+ if (p->groups[group].enabled) {
|
||||
+ dev_err(p->dev, "%s is already enabled\n", p->groups[group].name);
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+
|
||||
+ p->groups[group].enabled = 1;
|
||||
+ p->func[func]->enabled = 1;
|
||||
+
|
||||
+ shift = p->groups[group].shift;
|
||||
+ if (shift >= 32) {
|
||||
+ shift -= 32;
|
||||
+ reg = SYSC_REG_GPIO_MODE2;
|
||||
+ }
|
||||
+ mode = rt_sysc_r32(reg);
|
||||
+ mode &= ~(p->groups[group].mask << shift);
|
||||
+
|
||||
+ /* mark the pins as gpio */
|
||||
+ for (i = 0; i < p->groups[group].func[0].pin_count; i++)
|
||||
+ p->gpio[p->groups[group].func[0].pins[i]] = 1;
|
||||
+
|
||||
+ /* function 0 is gpio and needs special handling */
|
||||
+ if (func == 0) {
|
||||
+ mode |= p->groups[group].gpio << shift;
|
||||
+ } else {
|
||||
+ for (i = 0; i < p->func[func]->pin_count; i++)
|
||||
+ p->gpio[p->func[func]->pins[i]] = 0;
|
||||
+ mode |= p->func[func]->value << shift;
|
||||
+ }
|
||||
+ rt_sysc_w32(mode, reg);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_pmx_group_gpio_request_enable(struct pinctrl_dev *pctrldev,
|
||||
+ struct pinctrl_gpio_range *range,
|
||||
+ unsigned pin)
|
||||
+{
|
||||
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
|
||||
+
|
||||
+ if (!p->gpio[pin]) {
|
||||
+ dev_err(p->dev, "pin %d is not set to gpio mux\n", pin);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct pinmux_ops rt2880_pmx_group_ops = {
|
||||
+ .get_functions_count = rt2880_pmx_func_count,
|
||||
+ .get_function_name = rt2880_pmx_func_name,
|
||||
+ .get_function_groups = rt2880_pmx_group_get_groups,
|
||||
+ .set_mux = rt2880_pmx_group_enable,
|
||||
+ .gpio_request_enable = rt2880_pmx_group_gpio_request_enable,
|
||||
+};
|
||||
+
|
||||
+static struct pinctrl_desc rt2880_pctrl_desc = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .name = "rt2880-pinmux",
|
||||
+ .pctlops = &rt2880_pctrl_ops,
|
||||
+ .pmxops = &rt2880_pmx_group_ops,
|
||||
+};
|
||||
+
|
||||
+static struct rt2880_pmx_func gpio_func = {
|
||||
+ .name = "gpio",
|
||||
+};
|
||||
+
|
||||
+static int rt2880_pinmux_index(struct rt2880_priv *p)
|
||||
+{
|
||||
+ struct rt2880_pmx_func **f;
|
||||
+ struct rt2880_pmx_group *mux = p->groups;
|
||||
+ int i, j, c = 0;
|
||||
+
|
||||
+ /* count the mux functions */
|
||||
+ while (mux->name) {
|
||||
+ p->group_count++;
|
||||
+ mux++;
|
||||
+ }
|
||||
+
|
||||
+ /* allocate the group names array needed by the gpio function */
|
||||
+ p->group_names = devm_kzalloc(p->dev, sizeof(char *) * p->group_count, GFP_KERNEL);
|
||||
+ if (!p->group_names)
|
||||
+ return -1;
|
||||
+
|
||||
+ for (i = 0; i < p->group_count; i++) {
|
||||
+ p->group_names[i] = p->groups[i].name;
|
||||
+ p->func_count += p->groups[i].func_count;
|
||||
+ }
|
||||
+
|
||||
+ /* we have a dummy function[0] for gpio */
|
||||
+ p->func_count++;
|
||||
+
|
||||
+ /* allocate our function and group mapping index buffers */
|
||||
+ f = p->func = devm_kzalloc(p->dev, sizeof(struct rt2880_pmx_func) * p->func_count, GFP_KERNEL);
|
||||
+ gpio_func.groups = devm_kzalloc(p->dev, sizeof(int) * p->group_count, GFP_KERNEL);
|
||||
+ if (!f || !gpio_func.groups)
|
||||
+ return -1;
|
||||
+
|
||||
+ /* add a backpointer to the function so it knows its group */
|
||||
+ gpio_func.group_count = p->group_count;
|
||||
+ for (i = 0; i < gpio_func.group_count; i++)
|
||||
+ gpio_func.groups[i] = i;
|
||||
+
|
||||
+ f[c] = &gpio_func;
|
||||
+ c++;
|
||||
+
|
||||
+ /* add remaining functions */
|
||||
+ for (i = 0; i < p->group_count; i++) {
|
||||
+ for (j = 0; j < p->groups[i].func_count; j++) {
|
||||
+ f[c] = &p->groups[i].func[j];
|
||||
+ f[c]->groups = devm_kzalloc(p->dev, sizeof(int), GFP_KERNEL);
|
||||
+ f[c]->groups[0] = i;
|
||||
+ f[c]->group_count = 1;
|
||||
+ c++;
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_pinmux_pins(struct rt2880_priv *p)
|
||||
+{
|
||||
+ int i, j;
|
||||
+
|
||||
+ /* loop over the functions and initialize the pins array. also work out the highest pin used */
|
||||
+ for (i = 0; i < p->func_count; i++) {
|
||||
+ int pin;
|
||||
+
|
||||
+ if (!p->func[i]->pin_count)
|
||||
+ continue;
|
||||
+
|
||||
+ p->func[i]->pins = devm_kzalloc(p->dev, sizeof(int) * p->func[i]->pin_count, GFP_KERNEL);
|
||||
+ for (j = 0; j < p->func[i]->pin_count; j++)
|
||||
+ p->func[i]->pins[j] = p->func[i]->pin_first + j;
|
||||
+
|
||||
+ pin = p->func[i]->pin_first + p->func[i]->pin_count;
|
||||
+ if (pin > p->max_pins)
|
||||
+ p->max_pins = pin;
|
||||
+ }
|
||||
+
|
||||
+ /* the buffer that tells us which pins are gpio */
|
||||
+ p->gpio = devm_kzalloc(p->dev,sizeof(uint8_t) * p->max_pins,
|
||||
+ GFP_KERNEL);
|
||||
+ /* the pads needed to tell pinctrl about our pins */
|
||||
+ p->pads = devm_kzalloc(p->dev,
|
||||
+ sizeof(struct pinctrl_pin_desc) * p->max_pins,
|
||||
+ GFP_KERNEL);
|
||||
+ if (!p->pads || !p->gpio ) {
|
||||
+ dev_err(p->dev, "Failed to allocate gpio data\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ memset(p->gpio, 1, sizeof(uint8_t) * p->max_pins);
|
||||
+ for (i = 0; i < p->func_count; i++) {
|
||||
+ if (!p->func[i]->pin_count)
|
||||
+ continue;
|
||||
+
|
||||
+ for (j = 0; j < p->func[i]->pin_count; j++)
|
||||
+ p->gpio[p->func[i]->pins[j]] = 0;
|
||||
+ }
|
||||
+
|
||||
+ /* pin 0 is always a gpio */
|
||||
+ p->gpio[0] = 1;
|
||||
+
|
||||
+ /* set the pads */
|
||||
+ for (i = 0; i < p->max_pins; i++) {
|
||||
+ /* strlen("ioXY") + 1 = 5 */
|
||||
+ char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL);
|
||||
+
|
||||
+ if (!name) {
|
||||
+ dev_err(p->dev, "Failed to allocate pad name\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ snprintf(name, 5, "io%d", i);
|
||||
+ p->pads[i].number = i;
|
||||
+ p->pads[i].name = name;
|
||||
+ }
|
||||
+ p->desc->pins = p->pads;
|
||||
+ p->desc->npins = p->max_pins;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_pinmux_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rt2880_priv *p;
|
||||
+ struct pinctrl_dev *dev;
|
||||
+ struct device_node *np;
|
||||
+
|
||||
+ if (!rt2880_pinmux_data)
|
||||
+ return -ENOSYS;
|
||||
+
|
||||
+ /* setup the private data */
|
||||
+ p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL);
|
||||
+ if (!p)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ p->dev = &pdev->dev;
|
||||
+ p->desc = &rt2880_pctrl_desc;
|
||||
+ p->groups = rt2880_pinmux_data;
|
||||
+ platform_set_drvdata(pdev, p);
|
||||
+
|
||||
+ /* init the device */
|
||||
+ if (rt2880_pinmux_index(p)) {
|
||||
+ dev_err(&pdev->dev, "failed to load index\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (rt2880_pinmux_pins(p)) {
|
||||
+ dev_err(&pdev->dev, "failed to load pins\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ dev = pinctrl_register(p->desc, &pdev->dev, p);
|
||||
+ if (IS_ERR(dev))
|
||||
+ return PTR_ERR(dev);
|
||||
+
|
||||
+ /* finalize by adding gpio ranges for enables gpio controllers */
|
||||
+ for_each_compatible_node(np, NULL, "ralink,rt2880-gpio") {
|
||||
+ const __be32 *ngpio, *gpiobase;
|
||||
+ struct pinctrl_gpio_range *range;
|
||||
+ char *name;
|
||||
+
|
||||
+ if (!of_device_is_available(np))
|
||||
+ continue;
|
||||
+
|
||||
+ ngpio = of_get_property(np, "ralink,num-gpios", NULL);
|
||||
+ gpiobase = of_get_property(np, "ralink,gpio-base", NULL);
|
||||
+ if (!ngpio || !gpiobase) {
|
||||
+ dev_err(&pdev->dev, "failed to load chip info\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ range = devm_kzalloc(p->dev, sizeof(struct pinctrl_gpio_range) + 4, GFP_KERNEL);
|
||||
+ range->name = name = (char *) &range[1];
|
||||
+ sprintf(name, "pio");
|
||||
+ range->npins = __be32_to_cpu(*ngpio);
|
||||
+ range->base = __be32_to_cpu(*gpiobase);
|
||||
+ range->pin_base = range->base;
|
||||
+ pinctrl_add_gpio_range(dev, range);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id rt2880_pinmux_match[] = {
|
||||
+ { .compatible = "ralink,rt2880-pinmux" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, rt2880_pinmux_match);
|
||||
+
|
||||
+static struct platform_driver rt2880_pinmux_driver = {
|
||||
+ .probe = rt2880_pinmux_probe,
|
||||
+ .driver = {
|
||||
+ .name = "rt2880-pinmux",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = rt2880_pinmux_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+int __init rt2880_pinmux_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&rt2880_pinmux_driver);
|
||||
+}
|
||||
+
|
||||
+core_initcall_sync(rt2880_pinmux_init);
|
|
@ -0,0 +1,59 @@
|
|||
From d410e5478c622c01fcf31427533df5f433df9146 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 28 Jul 2013 19:45:30 +0200
|
||||
Subject: [PATCH 26/53] DT: Add documentation for gpio-ralink
|
||||
|
||||
Describe gpio-ralink binding.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Cc: linux-mips@linux-mips.org
|
||||
Cc: devicetree@vger.kernel.org
|
||||
Cc: linux-gpio@vger.kernel.org
|
||||
---
|
||||
.../devicetree/bindings/gpio/gpio-ralink.txt | 40 ++++++++++++++++++++
|
||||
1 file changed, 40 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/gpio/gpio-ralink.txt
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/gpio/gpio-ralink.txt
|
||||
@@ -0,0 +1,40 @@
|
||||
+Ralink SoC GPIO controller bindings
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible:
|
||||
+ - "ralink,rt2880-gpio" for Ralink controllers
|
||||
+- #gpio-cells : Should be two.
|
||||
+ - first cell is the pin number
|
||||
+ - second cell is used to specify optional parameters (unused)
|
||||
+- gpio-controller : Marks the device node as a GPIO controller
|
||||
+- reg : Physical base address and length of the controller's registers
|
||||
+- interrupt-parent: phandle to the INTC device node
|
||||
+- interrupts : Specify the INTC interrupt number
|
||||
+- ralink,num-gpios : Specify the number of GPIOs
|
||||
+- ralink,register-map : The register layout depends on the GPIO bank and actual
|
||||
+ SoC type. Register offsets need to be in this order.
|
||||
+ [ INT, EDGE, RENA, FENA, DATA, DIR, POL, SET, RESET, TOGGLE ]
|
||||
+
|
||||
+Optional properties:
|
||||
+- ralink,gpio-base : Specify the GPIO chips base number
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+ gpio0: gpio@600 {
|
||||
+ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio";
|
||||
+
|
||||
+ #gpio-cells = <2>;
|
||||
+ gpio-controller;
|
||||
+
|
||||
+ reg = <0x600 0x34>;
|
||||
+
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <6>;
|
||||
+
|
||||
+ ralink,gpio-base = <0>;
|
||||
+ ralink,num-gpios = <24>;
|
||||
+ ralink,register-map = [ 00 04 08 0c
|
||||
+ 20 24 28 2c
|
||||
+ 30 34 ];
|
||||
+
|
||||
+ };
|
|
@ -0,0 +1,430 @@
|
|||
From 69fdd2c4f937796b934e89c33acde9d082e27bfd Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 4 Aug 2014 20:36:29 +0200
|
||||
Subject: [PATCH 27/53] GPIO: MIPS: ralink: add gpio driver for ralink SoC
|
||||
|
||||
Add gpio driver for Ralink SoC. This driver makes the gpio core on
|
||||
RT2880, RT305x, rt3352, rt3662, rt3883, rt5350 and mt7620 work.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Cc: linux-mips@linux-mips.org
|
||||
Cc: linux-gpio@vger.kernel.org
|
||||
---
|
||||
arch/mips/include/asm/mach-ralink/gpio.h | 24 ++
|
||||
drivers/gpio/Kconfig | 6 +
|
||||
drivers/gpio/Makefile | 1 +
|
||||
drivers/gpio/gpio-ralink.c | 355 ++++++++++++++++++++++++++++++
|
||||
4 files changed, 386 insertions(+)
|
||||
create mode 100644 arch/mips/include/asm/mach-ralink/gpio.h
|
||||
create mode 100644 drivers/gpio/gpio-ralink.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/arch/mips/include/asm/mach-ralink/gpio.h
|
||||
@@ -0,0 +1,24 @@
|
||||
+/*
|
||||
+ * Ralink SoC GPIO API support
|
||||
+ *
|
||||
+ * Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#ifndef __ASM_MACH_RALINK_GPIO_H
|
||||
+#define __ASM_MACH_RALINK_GPIO_H
|
||||
+
|
||||
+#define ARCH_NR_GPIOS 128
|
||||
+#include <asm-generic/gpio.h>
|
||||
+
|
||||
+#define gpio_get_value __gpio_get_value
|
||||
+#define gpio_set_value __gpio_set_value
|
||||
+#define gpio_cansleep __gpio_cansleep
|
||||
+#define gpio_to_irq __gpio_to_irq
|
||||
+
|
||||
+#endif /* __ASM_MACH_RALINK_GPIO_H */
|
||||
--- a/drivers/gpio/Kconfig
|
||||
+++ b/drivers/gpio/Kconfig
|
||||
@@ -351,6 +351,12 @@ config GPIO_SAMSUNG
|
||||
Legacy GPIO support. Use only for platforms without support for
|
||||
pinctrl.
|
||||
|
||||
+config GPIO_RALINK
|
||||
+ bool "Ralink GPIO Support"
|
||||
+ depends on RALINK
|
||||
+ help
|
||||
+ Say yes here to support the Ralink SoC GPIO device
|
||||
+
|
||||
config GPIO_SPEAR_SPICS
|
||||
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
|
||||
depends on PLAT_SPEAR
|
||||
--- a/drivers/gpio/Makefile
|
||||
+++ b/drivers/gpio/Makefile
|
||||
@@ -76,6 +76,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf85
|
||||
obj-$(CONFIG_GPIO_PCH) += gpio-pch.o
|
||||
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
|
||||
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
|
||||
+obj-$(CONFIG_GPIO_RALINK) += gpio-ralink.o
|
||||
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
|
||||
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
|
||||
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/gpio/gpio-ralink.c
|
||||
@@ -0,0 +1,355 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/irqdomain.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+
|
||||
+enum ralink_gpio_reg {
|
||||
+ GPIO_REG_INT = 0,
|
||||
+ GPIO_REG_EDGE,
|
||||
+ GPIO_REG_RENA,
|
||||
+ GPIO_REG_FENA,
|
||||
+ GPIO_REG_DATA,
|
||||
+ GPIO_REG_DIR,
|
||||
+ GPIO_REG_POL,
|
||||
+ GPIO_REG_SET,
|
||||
+ GPIO_REG_RESET,
|
||||
+ GPIO_REG_TOGGLE,
|
||||
+ GPIO_REG_MAX
|
||||
+};
|
||||
+
|
||||
+struct ralink_gpio_chip {
|
||||
+ struct gpio_chip chip;
|
||||
+ u8 regs[GPIO_REG_MAX];
|
||||
+
|
||||
+ spinlock_t lock;
|
||||
+ void __iomem *membase;
|
||||
+ struct irq_domain *domain;
|
||||
+ int irq;
|
||||
+
|
||||
+ u32 rising;
|
||||
+ u32 falling;
|
||||
+};
|
||||
+
|
||||
+#define MAP_MAX 4
|
||||
+static struct irq_domain *irq_map[MAP_MAX];
|
||||
+static int irq_map_count;
|
||||
+static atomic_t irq_refcount = ATOMIC_INIT(0);
|
||||
+
|
||||
+static inline struct ralink_gpio_chip *to_ralink_gpio(struct gpio_chip *chip)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg;
|
||||
+
|
||||
+ rg = container_of(chip, struct ralink_gpio_chip, chip);
|
||||
+
|
||||
+ return rg;
|
||||
+}
|
||||
+
|
||||
+static inline void rt_gpio_w32(struct ralink_gpio_chip *rg, u8 reg, u32 val)
|
||||
+{
|
||||
+ iowrite32(val, rg->membase + rg->regs[reg]);
|
||||
+}
|
||||
+
|
||||
+static inline u32 rt_gpio_r32(struct ralink_gpio_chip *rg, u8 reg)
|
||||
+{
|
||||
+ return ioread32(rg->membase + rg->regs[reg]);
|
||||
+}
|
||||
+
|
||||
+static void ralink_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
|
||||
+
|
||||
+ rt_gpio_w32(rg, (value) ? GPIO_REG_SET : GPIO_REG_RESET, BIT(offset));
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
|
||||
+
|
||||
+ return !!(rt_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset));
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
|
||||
+ unsigned long flags;
|
||||
+ u32 t;
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ t = rt_gpio_r32(rg, GPIO_REG_DIR);
|
||||
+ t &= ~BIT(offset);
|
||||
+ rt_gpio_w32(rg, GPIO_REG_DIR, t);
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_direction_output(struct gpio_chip *chip,
|
||||
+ unsigned offset, int value)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
|
||||
+ unsigned long flags;
|
||||
+ u32 t;
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ ralink_gpio_set(chip, offset, value);
|
||||
+ t = rt_gpio_r32(rg, GPIO_REG_DIR);
|
||||
+ t |= BIT(offset);
|
||||
+ rt_gpio_w32(rg, GPIO_REG_DIR, t);
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip);
|
||||
+
|
||||
+ if (rg->irq < 1)
|
||||
+ return -1;
|
||||
+
|
||||
+ return irq_create_mapping(rg->domain, pin);
|
||||
+}
|
||||
+
|
||||
+static void ralink_gpio_irq_handler(struct irq_desc *desc)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < irq_map_count; i++) {
|
||||
+ struct irq_domain *domain = irq_map[i];
|
||||
+ struct ralink_gpio_chip *rg;
|
||||
+ unsigned long pending;
|
||||
+ int bit;
|
||||
+
|
||||
+ rg = (struct ralink_gpio_chip *) domain->host_data;
|
||||
+ pending = rt_gpio_r32(rg, GPIO_REG_INT);
|
||||
+
|
||||
+ for_each_set_bit(bit, &pending, rg->chip.ngpio) {
|
||||
+ u32 map = irq_find_mapping(domain, bit);
|
||||
+ generic_handle_irq(map);
|
||||
+ rt_gpio_w32(rg, GPIO_REG_INT, BIT(bit));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ralink_gpio_irq_unmask(struct irq_data *d)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg;
|
||||
+ unsigned long flags;
|
||||
+ u32 rise, fall;
|
||||
+
|
||||
+ rg = (struct ralink_gpio_chip *) d->domain->host_data;
|
||||
+ rise = rt_gpio_r32(rg, GPIO_REG_RENA);
|
||||
+ fall = rt_gpio_r32(rg, GPIO_REG_FENA);
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ rt_gpio_w32(rg, GPIO_REG_RENA, rise | (BIT(d->hwirq) & rg->rising));
|
||||
+ rt_gpio_w32(rg, GPIO_REG_FENA, fall | (BIT(d->hwirq) & rg->falling));
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static void ralink_gpio_irq_mask(struct irq_data *d)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg;
|
||||
+ unsigned long flags;
|
||||
+ u32 rise, fall;
|
||||
+
|
||||
+ rg = (struct ralink_gpio_chip *) d->domain->host_data;
|
||||
+ rise = rt_gpio_r32(rg, GPIO_REG_RENA);
|
||||
+ fall = rt_gpio_r32(rg, GPIO_REG_FENA);
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ rt_gpio_w32(rg, GPIO_REG_FENA, fall & ~BIT(d->hwirq));
|
||||
+ rt_gpio_w32(rg, GPIO_REG_RENA, rise & ~BIT(d->hwirq));
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_irq_type(struct irq_data *d, unsigned int type)
|
||||
+{
|
||||
+ struct ralink_gpio_chip *rg;
|
||||
+ u32 mask = BIT(d->hwirq);
|
||||
+
|
||||
+ rg = (struct ralink_gpio_chip *) d->domain->host_data;
|
||||
+
|
||||
+ if (type == IRQ_TYPE_PROBE) {
|
||||
+ if ((rg->rising | rg->falling) & mask)
|
||||
+ return 0;
|
||||
+
|
||||
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
|
||||
+ }
|
||||
+
|
||||
+ if (type & IRQ_TYPE_EDGE_RISING)
|
||||
+ rg->rising |= mask;
|
||||
+ else
|
||||
+ rg->rising &= ~mask;
|
||||
+
|
||||
+ if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
+ rg->falling |= mask;
|
||||
+ else
|
||||
+ rg->falling &= ~mask;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct irq_chip ralink_gpio_irq_chip = {
|
||||
+ .name = "GPIO",
|
||||
+ .irq_unmask = ralink_gpio_irq_unmask,
|
||||
+ .irq_mask = ralink_gpio_irq_mask,
|
||||
+ .irq_mask_ack = ralink_gpio_irq_mask,
|
||||
+ .irq_set_type = ralink_gpio_irq_type,
|
||||
+};
|
||||
+
|
||||
+static int gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
+{
|
||||
+ irq_set_chip_and_handler(irq, &ralink_gpio_irq_chip, handle_level_irq);
|
||||
+ irq_set_handler_data(irq, d);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct irq_domain_ops irq_domain_ops = {
|
||||
+ .xlate = irq_domain_xlate_onecell,
|
||||
+ .map = gpio_map,
|
||||
+};
|
||||
+
|
||||
+static void ralink_gpio_irq_init(struct device_node *np,
|
||||
+ struct ralink_gpio_chip *rg)
|
||||
+{
|
||||
+ if (irq_map_count >= MAP_MAX)
|
||||
+ return;
|
||||
+
|
||||
+ rg->irq = irq_of_parse_and_map(np, 0);
|
||||
+ if (!rg->irq)
|
||||
+ return;
|
||||
+
|
||||
+ rg->domain = irq_domain_add_linear(np, rg->chip.ngpio,
|
||||
+ &irq_domain_ops, rg);
|
||||
+ if (!rg->domain) {
|
||||
+ dev_err(rg->chip.dev, "irq_domain_add_linear failed\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ irq_map[irq_map_count++] = rg->domain;
|
||||
+
|
||||
+ rt_gpio_w32(rg, GPIO_REG_RENA, 0x0);
|
||||
+ rt_gpio_w32(rg, GPIO_REG_FENA, 0x0);
|
||||
+
|
||||
+ if (!atomic_read(&irq_refcount))
|
||||
+ irq_set_chained_handler(rg->irq, ralink_gpio_irq_handler);
|
||||
+ atomic_inc(&irq_refcount);
|
||||
+
|
||||
+ dev_info(rg->chip.dev, "registering %d irq handlers\n", rg->chip.ngpio);
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ int gpio = chip->base + offset;
|
||||
+
|
||||
+ return pinctrl_request_gpio(gpio);
|
||||
+}
|
||||
+
|
||||
+static void ralink_gpio_free(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ int gpio = chip->base + offset;
|
||||
+
|
||||
+ pinctrl_free_gpio(gpio);
|
||||
+}
|
||||
+
|
||||
+static int ralink_gpio_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ struct ralink_gpio_chip *rg;
|
||||
+ const __be32 *ngpio, *gpiobase;
|
||||
+
|
||||
+ if (!res) {
|
||||
+ dev_err(&pdev->dev, "failed to find resource\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ rg = devm_kzalloc(&pdev->dev,
|
||||
+ sizeof(struct ralink_gpio_chip), GFP_KERNEL);
|
||||
+ if (!rg)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ rg->membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (!rg->membase) {
|
||||
+ dev_err(&pdev->dev, "cannot remap I/O memory region\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ if (of_property_read_u8_array(np, "ralink,register-map",
|
||||
+ rg->regs, GPIO_REG_MAX)) {
|
||||
+ dev_err(&pdev->dev, "failed to read register definition\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ ngpio = of_get_property(np, "ralink,num-gpios", NULL);
|
||||
+ if (!ngpio) {
|
||||
+ dev_err(&pdev->dev, "failed to read number of pins\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ gpiobase = of_get_property(np, "ralink,gpio-base", NULL);
|
||||
+ if (gpiobase)
|
||||
+ rg->chip.base = be32_to_cpu(*gpiobase);
|
||||
+ else
|
||||
+ rg->chip.base = -1;
|
||||
+
|
||||
+ spin_lock_init(&rg->lock);
|
||||
+
|
||||
+ rg->chip.dev = &pdev->dev;
|
||||
+ rg->chip.label = dev_name(&pdev->dev);
|
||||
+ rg->chip.of_node = np;
|
||||
+ rg->chip.ngpio = be32_to_cpu(*ngpio);
|
||||
+ rg->chip.direction_input = ralink_gpio_direction_input;
|
||||
+ rg->chip.direction_output = ralink_gpio_direction_output;
|
||||
+ rg->chip.get = ralink_gpio_get;
|
||||
+ rg->chip.set = ralink_gpio_set;
|
||||
+ rg->chip.request = ralink_gpio_request;
|
||||
+ rg->chip.to_irq = ralink_gpio_to_irq;
|
||||
+ rg->chip.free = ralink_gpio_free;
|
||||
+
|
||||
+ /* set polarity to low for all lines */
|
||||
+ rt_gpio_w32(rg, GPIO_REG_POL, 0);
|
||||
+
|
||||
+ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio);
|
||||
+
|
||||
+ ralink_gpio_irq_init(np, rg);
|
||||
+
|
||||
+ return gpiochip_add(&rg->chip);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id ralink_gpio_match[] = {
|
||||
+ { .compatible = "ralink,rt2880-gpio" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, ralink_gpio_match);
|
||||
+
|
||||
+static struct platform_driver ralink_gpio_driver = {
|
||||
+ .probe = ralink_gpio_probe,
|
||||
+ .driver = {
|
||||
+ .name = "rt2880_gpio",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = ralink_gpio_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init ralink_gpio_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&ralink_gpio_driver);
|
||||
+}
|
||||
+
|
||||
+subsys_initcall(ralink_gpio_init);
|
|
@ -0,0 +1,405 @@
|
|||
From 61ac7d9b4228de8c332900902c2b93189b042eab Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 11:00:32 +0100
|
||||
Subject: [PATCH 28/53] GPIO: ralink: add mt7621 gpio controller
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/Kconfig | 3 +
|
||||
drivers/gpio/Kconfig | 6 +
|
||||
drivers/gpio/Makefile | 1 +
|
||||
drivers/gpio/gpio-mt7621.c | 354 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
4 files changed, 364 insertions(+)
|
||||
create mode 100644 drivers/gpio/gpio-mt7621.c
|
||||
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -587,6 +587,9 @@ config RALINK
|
||||
select RESET_CONTROLLER
|
||||
select PINCTRL
|
||||
select PINCTRL_RT2880
|
||||
+ select ARCH_HAS_RESET_CONTROLLER
|
||||
+ select RESET_CONTROLLER
|
||||
+ select ARCH_REQUIRE_GPIOLIB
|
||||
|
||||
config SGI_IP22
|
||||
bool "SGI IP22 (Indy/Indigo2)"
|
||||
--- a/drivers/gpio/Kconfig
|
||||
+++ b/drivers/gpio/Kconfig
|
||||
@@ -260,6 +260,12 @@ config GPIO_MB86S7X
|
||||
help
|
||||
Say yes here to support the GPIO controller in Fujitsu MB86S70 SoCs.
|
||||
|
||||
+config GPIO_MT7621
|
||||
+ bool "Mediatek GPIO Support"
|
||||
+ depends on SOC_MT7620 || SOC_MT7621
|
||||
+ help
|
||||
+ Say yes here to support the Mediatek SoC GPIO device
|
||||
+
|
||||
config GPIO_MM_LANTIQ
|
||||
bool "Lantiq Memory mapped GPIOs"
|
||||
depends on LANTIQ && SOC_XWAY
|
||||
--- a/drivers/gpio/Makefile
|
||||
+++ b/drivers/gpio/Makefile
|
||||
@@ -120,3 +120,4 @@ obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa
|
||||
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
||||
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
||||
obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
|
||||
+obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/gpio/gpio-mt7621.c
|
||||
@@ -0,0 +1,354 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/irqdomain.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#define MTK_MAX_BANK 3
|
||||
+#define MTK_BANK_WIDTH 32
|
||||
+
|
||||
+enum mediatek_gpio_reg {
|
||||
+ GPIO_REG_CTRL = 0,
|
||||
+ GPIO_REG_POL,
|
||||
+ GPIO_REG_DATA,
|
||||
+ GPIO_REG_DSET,
|
||||
+ GPIO_REG_DCLR,
|
||||
+ GPIO_REG_REDGE,
|
||||
+ GPIO_REG_FEDGE,
|
||||
+ GPIO_REG_HLVL,
|
||||
+ GPIO_REG_LLVL,
|
||||
+ GPIO_REG_STAT,
|
||||
+ GPIO_REG_EDGE,
|
||||
+};
|
||||
+
|
||||
+static void __iomem *mediatek_gpio_membase;
|
||||
+static int mediatek_gpio_irq;
|
||||
+static struct irq_domain *mediatek_gpio_irq_domain;
|
||||
+static atomic_t irq_refcount = ATOMIC_INIT(0);
|
||||
+
|
||||
+struct mtk_gc {
|
||||
+ struct gpio_chip chip;
|
||||
+ spinlock_t lock;
|
||||
+ int bank;
|
||||
+ u32 rising;
|
||||
+ u32 falling;
|
||||
+} *gc_map[MTK_MAX_BANK];
|
||||
+
|
||||
+static inline struct mtk_gc
|
||||
+*to_mediatek_gpio(struct gpio_chip *chip)
|
||||
+{
|
||||
+ struct mtk_gc *mgc;
|
||||
+
|
||||
+ mgc = container_of(chip, struct mtk_gc, chip);
|
||||
+
|
||||
+ return mgc;
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+mtk_gpio_w32(struct mtk_gc *rg, u8 reg, u32 val)
|
||||
+{
|
||||
+ iowrite32(val, mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
|
||||
+}
|
||||
+
|
||||
+static inline u32
|
||||
+mtk_gpio_r32(struct mtk_gc *rg, u8 reg)
|
||||
+{
|
||||
+ return ioread32(mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mediatek_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
+{
|
||||
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
|
||||
+
|
||||
+ mtk_gpio_w32(rg, (value) ? GPIO_REG_DSET : GPIO_REG_DCLR, BIT(offset));
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
|
||||
+
|
||||
+ return !!(mtk_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset));
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
|
||||
+ unsigned long flags;
|
||||
+ u32 t;
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
|
||||
+ t &= ~BIT(offset);
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_direction_output(struct gpio_chip *chip,
|
||||
+ unsigned offset, int value)
|
||||
+{
|
||||
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
|
||||
+ unsigned long flags;
|
||||
+ u32 t;
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
|
||||
+ t |= BIT(offset);
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
|
||||
+ mediatek_gpio_set(chip, offset, value);
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
||||
+{
|
||||
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
|
||||
+ unsigned long flags;
|
||||
+ u32 t;
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+
|
||||
+ if (t & BIT(offset))
|
||||
+ return 0;
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
|
||||
+{
|
||||
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
|
||||
+
|
||||
+ return irq_create_mapping(mediatek_gpio_irq_domain, pin + (rg->bank * MTK_BANK_WIDTH));
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_bank_probe(struct platform_device *pdev, struct device_node *bank)
|
||||
+{
|
||||
+ const __be32 *id = of_get_property(bank, "reg", NULL);
|
||||
+ struct mtk_gc *rg = devm_kzalloc(&pdev->dev,
|
||||
+ sizeof(struct mtk_gc), GFP_KERNEL);
|
||||
+
|
||||
+ if (!rg || !id || be32_to_cpu(*id) > MTK_MAX_BANK)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ gc_map[be32_to_cpu(*id)] = rg;
|
||||
+
|
||||
+ memset(rg, 0, sizeof(struct mtk_gc));
|
||||
+
|
||||
+ spin_lock_init(&rg->lock);
|
||||
+
|
||||
+ rg->chip.dev = &pdev->dev;
|
||||
+ rg->chip.label = dev_name(&pdev->dev);
|
||||
+ rg->chip.of_node = bank;
|
||||
+ rg->chip.base = MTK_BANK_WIDTH * be32_to_cpu(*id);
|
||||
+ rg->chip.ngpio = MTK_BANK_WIDTH;
|
||||
+ rg->chip.direction_input = mediatek_gpio_direction_input;
|
||||
+ rg->chip.direction_output = mediatek_gpio_direction_output;
|
||||
+ rg->chip.get_direction = mediatek_gpio_get_direction;
|
||||
+ rg->chip.get = mediatek_gpio_get;
|
||||
+ rg->chip.set = mediatek_gpio_set;
|
||||
+ if (mediatek_gpio_irq_domain)
|
||||
+ rg->chip.to_irq = mediatek_gpio_to_irq;
|
||||
+ rg->bank = be32_to_cpu(*id);
|
||||
+
|
||||
+ /* set polarity to low for all gpios */
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_POL, 0);
|
||||
+
|
||||
+ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio);
|
||||
+
|
||||
+ return gpiochip_add(&rg->chip);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mediatek_gpio_irq_handler(struct irq_desc *desc)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < MTK_MAX_BANK; i++) {
|
||||
+ struct mtk_gc *rg = gc_map[i];
|
||||
+ unsigned long pending;
|
||||
+ int bit;
|
||||
+
|
||||
+ if (!rg)
|
||||
+ continue;
|
||||
+
|
||||
+ pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
|
||||
+
|
||||
+ for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
|
||||
+ u32 map = irq_find_mapping(mediatek_gpio_irq_domain, (MTK_BANK_WIDTH * i) + bit);
|
||||
+
|
||||
+ generic_handle_irq(map);
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mediatek_gpio_irq_unmask(struct irq_data *d)
|
||||
+{
|
||||
+ int pin = d->hwirq;
|
||||
+ int bank = pin / 32;
|
||||
+ struct mtk_gc *rg = gc_map[bank];
|
||||
+ unsigned long flags;
|
||||
+ u32 rise, fall;
|
||||
+
|
||||
+ if (!rg)
|
||||
+ return;
|
||||
+
|
||||
+ rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
|
||||
+ fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(d->hwirq) & rg->rising));
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(d->hwirq) & rg->falling));
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mediatek_gpio_irq_mask(struct irq_data *d)
|
||||
+{
|
||||
+ int pin = d->hwirq;
|
||||
+ int bank = pin / 32;
|
||||
+ struct mtk_gc *rg = gc_map[bank];
|
||||
+ unsigned long flags;
|
||||
+ u32 rise, fall;
|
||||
+
|
||||
+ if (!rg)
|
||||
+ return;
|
||||
+
|
||||
+ rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
|
||||
+ fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
|
||||
+
|
||||
+ spin_lock_irqsave(&rg->lock, flags);
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(d->hwirq));
|
||||
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(d->hwirq));
|
||||
+ spin_unlock_irqrestore(&rg->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
|
||||
+{
|
||||
+ int pin = d->hwirq;
|
||||
+ int bank = pin / 32;
|
||||
+ struct mtk_gc *rg = gc_map[bank];
|
||||
+ u32 mask = BIT(d->hwirq);
|
||||
+
|
||||
+ if (!rg)
|
||||
+ return -1;
|
||||
+
|
||||
+ if (type == IRQ_TYPE_PROBE) {
|
||||
+ if ((rg->rising | rg->falling) & mask)
|
||||
+ return 0;
|
||||
+
|
||||
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
|
||||
+ }
|
||||
+
|
||||
+ if (type & IRQ_TYPE_EDGE_RISING)
|
||||
+ rg->rising |= mask;
|
||||
+ else
|
||||
+ rg->rising &= ~mask;
|
||||
+
|
||||
+ if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
+ rg->falling |= mask;
|
||||
+ else
|
||||
+ rg->falling &= ~mask;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct irq_chip mediatek_gpio_irq_chip = {
|
||||
+ .name = "GPIO",
|
||||
+ .irq_unmask = mediatek_gpio_irq_unmask,
|
||||
+ .irq_mask = mediatek_gpio_irq_mask,
|
||||
+ .irq_mask_ack = mediatek_gpio_irq_mask,
|
||||
+ .irq_set_type = mediatek_gpio_irq_type,
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
|
||||
+{
|
||||
+ irq_set_chip_and_handler(irq, &mediatek_gpio_irq_chip, handle_level_irq);
|
||||
+ irq_set_handler_data(irq, d);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct irq_domain_ops irq_domain_ops = {
|
||||
+ .xlate = irq_domain_xlate_onecell,
|
||||
+ .map = mediatek_gpio_gpio_map,
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+mediatek_gpio_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *bank, *np = pdev->dev.of_node;
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+
|
||||
+ mediatek_gpio_membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(mediatek_gpio_membase))
|
||||
+ return PTR_ERR(mediatek_gpio_membase);
|
||||
+
|
||||
+ mediatek_gpio_irq = irq_of_parse_and_map(np, 0);
|
||||
+ if (mediatek_gpio_irq) {
|
||||
+ mediatek_gpio_irq_domain = irq_domain_add_linear(np,
|
||||
+ MTK_MAX_BANK * MTK_BANK_WIDTH,
|
||||
+ &irq_domain_ops, NULL);
|
||||
+ if (!mediatek_gpio_irq_domain)
|
||||
+ dev_err(&pdev->dev, "irq_domain_add_linear failed\n");
|
||||
+ }
|
||||
+
|
||||
+ for_each_child_of_node(np, bank)
|
||||
+ if (of_device_is_compatible(bank, "mtk,mt7621-gpio-bank"))
|
||||
+ mediatek_gpio_bank_probe(pdev, bank);
|
||||
+
|
||||
+ if (mediatek_gpio_irq_domain)
|
||||
+ irq_set_chained_handler(mediatek_gpio_irq, mediatek_gpio_irq_handler);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mediatek_gpio_match[] = {
|
||||
+ { .compatible = "mtk,mt7621-gpio" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
|
||||
+
|
||||
+static struct platform_driver mediatek_gpio_driver = {
|
||||
+ .probe = mediatek_gpio_probe,
|
||||
+ .driver = {
|
||||
+ .name = "mt7621_gpio",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mediatek_gpio_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init
|
||||
+mediatek_gpio_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&mediatek_gpio_driver);
|
||||
+}
|
||||
+
|
||||
+subsys_initcall(mediatek_gpio_init);
|
|
@ -0,0 +1,293 @@
|
|||
From b00b5eafa7e8d059bd0ce844e66f648916953270 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 3 Jan 2016 19:11:22 +0100
|
||||
Subject: [PATCH 2/3] phy: ralink-usb: add driver for Mediatek/Ralink
|
||||
|
||||
Add a driver to setup the USB phy on Mediatek/Ralink SoCs.
|
||||
The driver is trivial and only sets up power and host mode.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
.../devicetree/bindings/phy/ralink-usb-phy.txt | 17 ++
|
||||
drivers/phy/Kconfig | 8 +
|
||||
drivers/phy/Makefile | 1 +
|
||||
drivers/phy/phy-ralink-usb.c | 171 ++++++++++++++++++++
|
||||
4 files changed, 197 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/phy/ralink-usb-phy.txt
|
||||
create mode 100644 drivers/phy/phy-ralink-usb.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/phy/ralink-usb-phy.txt
|
||||
@@ -0,0 +1,17 @@
|
||||
+Mediatek/Ralink USB PHY
|
||||
+
|
||||
+Required properties:
|
||||
+ - compatible: ralink,rt3352-usbphy or mediatek,mt7620-usbphy
|
||||
+ - #phy-cells: should be 0
|
||||
+ - resets: the two reset controllers for host and device
|
||||
+ - reset-names: the names of the 2 reset controllers
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+usbphy: phy {
|
||||
+ compatible = "mediatek,mt7620-usbphy";
|
||||
+ #phy-cells = <0>;
|
||||
+
|
||||
+ resets = <&rstctrl 22 &rstctrl 25>;
|
||||
+ reset-names = "host", "device";
|
||||
+};
|
||||
--- a/drivers/phy/Kconfig
|
||||
+++ b/drivers/phy/Kconfig
|
||||
@@ -341,6 +341,14 @@ config PHY_XGENE
|
||||
help
|
||||
This option enables support for APM X-Gene SoC multi-purpose PHY.
|
||||
|
||||
+config PHY_RALINK_USB
|
||||
+ tristate "Ralink USB PHY driver"
|
||||
+ select GENERIC_PHY
|
||||
+ depends on RALINK
|
||||
+ help
|
||||
+ This option enables support for the Ralink USB PHY found inside
|
||||
+ RT3352 and MT7620.
|
||||
+
|
||||
config PHY_STIH407_USB
|
||||
tristate "STMicroelectronics USB2 picoPHY driver for STiH407 family"
|
||||
depends on RESET_CONTROLLER
|
||||
--- a/drivers/phy/Makefile
|
||||
+++ b/drivers/phy/Makefile
|
||||
@@ -48,3 +48,4 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1
|
||||
obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
|
||||
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
|
||||
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
|
||||
+obj-$(CONFIG_PHY_RALINK_USB) += phy-ralink-usb.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/phy/phy-ralink-usb.c
|
||||
@@ -0,0 +1,228 @@
|
||||
+/*
|
||||
+ * Allwinner ralink USB phy driver
|
||||
+ *
|
||||
+ * Copyright (C) 2016 John Crispin <blogic@openwrt.org>
|
||||
+ *
|
||||
+ * Based on code from
|
||||
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; either version 2 of the License, or
|
||||
+ * (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mutex.h>
|
||||
+#include <linux/phy/phy.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#define RT_SYSC_REG_SYSCFG1 0x014
|
||||
+#define RT_SYSC_REG_CLKCFG1 0x030
|
||||
+#define RT_SYSC_REG_USB_PHY_CFG 0x05c
|
||||
+
|
||||
+#define OFS_U2_PHY_AC0 0x00
|
||||
+#define OFS_U2_PHY_AC1 0x04
|
||||
+#define OFS_U2_PHY_AC2 0x08
|
||||
+#define OFS_U2_PHY_ACR0 0x10
|
||||
+#define OFS_U2_PHY_ACR1 0x14
|
||||
+#define OFS_U2_PHY_ACR2 0x18
|
||||
+#define OFS_U2_PHY_ACR3 0x1C
|
||||
+#define OFS_U2_PHY_ACR4 0x20
|
||||
+#define OFS_U2_PHY_AMON0 0x24
|
||||
+#define OFS_U2_PHY_DCR0 0x60
|
||||
+#define OFS_U2_PHY_DCR1 0x64
|
||||
+#define OFS_U2_PHY_DTM0 0x68
|
||||
+#define OFS_U2_PHY_DTM1 0x6C
|
||||
+
|
||||
+#define RT_RSTCTRL_UDEV BIT(25)
|
||||
+#define RT_RSTCTRL_UHST BIT(22)
|
||||
+#define RT_SYSCFG1_USB0_HOST_MODE BIT(10)
|
||||
+
|
||||
+#define MT7620_CLKCFG1_UPHY0_CLK_EN BIT(25)
|
||||
+#define MT7620_CLKCFG1_UPHY1_CLK_EN BIT(22)
|
||||
+#define RT_CLKCFG1_UPHY1_CLK_EN BIT(20)
|
||||
+#define RT_CLKCFG1_UPHY0_CLK_EN BIT(18)
|
||||
+
|
||||
+#define USB_PHY_UTMI_8B60M BIT(1)
|
||||
+#define UDEV_WAKEUP BIT(0)
|
||||
+
|
||||
+struct ralink_usb_phy {
|
||||
+ struct reset_control *rstdev;
|
||||
+ struct reset_control *rsthost;
|
||||
+ u32 clk;
|
||||
+ struct phy *phy;
|
||||
+ void __iomem *base;
|
||||
+};
|
||||
+
|
||||
+static void u2_phy_w32(struct ralink_usb_phy *phy, u32 val, u32 reg)
|
||||
+{
|
||||
+ iowrite32(val, phy->base + reg);
|
||||
+}
|
||||
+
|
||||
+static u32 u2_phy_r32(struct ralink_usb_phy *phy, u32 reg)
|
||||
+{
|
||||
+ return ioread32(phy->base + reg);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+u2_phy_init(struct ralink_usb_phy *phy)
|
||||
+{
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_AC2);
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_ACR0);
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_DCR0);
|
||||
+
|
||||
+ u2_phy_w32(phy, 0x00ffff02, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_w32(phy, 0x00555502, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_w32(phy, 0x00aaaa02, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_w32(phy, 0x00000402, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_r32(phy, OFS_U2_PHY_DCR0);
|
||||
+ u2_phy_w32(phy, 0x0048086a, OFS_U2_PHY_AC0);
|
||||
+ u2_phy_w32(phy, 0x4400001c, OFS_U2_PHY_AC1);
|
||||
+ u2_phy_w32(phy, 0xc0200000, OFS_U2_PHY_ACR3);
|
||||
+ u2_phy_w32(phy, 0x02000000, OFS_U2_PHY_DTM0);
|
||||
+}
|
||||
+
|
||||
+static int ralink_usb_phy_power_on(struct phy *_phy)
|
||||
+{
|
||||
+ struct ralink_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
+ u32 t;
|
||||
+
|
||||
+ /* enable the phy */
|
||||
+ rt_sysc_m32(0, phy->clk, RT_SYSC_REG_CLKCFG1);
|
||||
+
|
||||
+ /* setup host mode */
|
||||
+ rt_sysc_m32(0, RT_SYSCFG1_USB0_HOST_MODE, RT_SYSC_REG_SYSCFG1);
|
||||
+
|
||||
+ /* deassert the reset lines */
|
||||
+ reset_control_deassert(phy->rsthost);
|
||||
+ reset_control_deassert(phy->rstdev);
|
||||
+
|
||||
+ /*
|
||||
+ * The SDK kernel had a delay of 100ms. however on device
|
||||
+ * testing showed that 10ms is enough
|
||||
+ */
|
||||
+ mdelay(10);
|
||||
+
|
||||
+ if (!IS_ERR(phy->base))
|
||||
+ u2_phy_init(phy);
|
||||
+
|
||||
+ /* print some status info */
|
||||
+ t = rt_sysc_r32(RT_SYSC_REG_USB_PHY_CFG);
|
||||
+ dev_info(&phy->phy->dev, "remote usb device wakeup %s\n",
|
||||
+ (t & UDEV_WAKEUP) ? ("enabled") : ("disabled"));
|
||||
+ if (t & USB_PHY_UTMI_8B60M)
|
||||
+ dev_info(&phy->phy->dev, "UTMI 8bit 60MHz\n");
|
||||
+ else
|
||||
+ dev_info(&phy->phy->dev, "UTMI 16bit 30MHz\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int ralink_usb_phy_power_off(struct phy *_phy)
|
||||
+{
|
||||
+ struct ralink_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
+
|
||||
+ /* assert the reset lines */
|
||||
+ reset_control_assert(phy->rstdev);
|
||||
+ reset_control_assert(phy->rsthost);
|
||||
+
|
||||
+ /* disable the phy */
|
||||
+ rt_sysc_m32(phy->clk, 0, RT_SYSC_REG_CLKCFG1);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct phy_ops ralink_usb_phy_ops = {
|
||||
+ .power_on = ralink_usb_phy_power_on,
|
||||
+ .power_off = ralink_usb_phy_power_off,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id ralink_usb_phy_of_match[] = {
|
||||
+ {
|
||||
+ .compatible = "ralink,rt3352-usbphy",
|
||||
+ .data = (void *) (RT_CLKCFG1_UPHY1_CLK_EN |
|
||||
+ RT_CLKCFG1_UPHY0_CLK_EN)
|
||||
+ },
|
||||
+ {
|
||||
+ .compatible = "mediatek,mt7620-usbphy",
|
||||
+ .data = (void *) (MT7620_CLKCFG1_UPHY1_CLK_EN |
|
||||
+ MT7620_CLKCFG1_UPHY0_CLK_EN) },
|
||||
+ { },
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, ralink_usb_phy_of_match);
|
||||
+
|
||||
+static int ralink_usb_phy_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res;
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct phy_provider *phy_provider;
|
||||
+ const struct of_device_id *match;
|
||||
+ struct ralink_usb_phy *phy;
|
||||
+
|
||||
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
+ if (!phy)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ match = of_match_device(ralink_usb_phy_of_match, &pdev->dev);
|
||||
+ if (!match)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ phy->clk = (int) match->data;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ phy->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+
|
||||
+ phy->rsthost = devm_reset_control_get(&pdev->dev, "host");
|
||||
+ if (IS_ERR(phy->rsthost)) {
|
||||
+ dev_err(dev, "host reset is missing\n");
|
||||
+ return PTR_ERR(phy->rsthost);
|
||||
+ }
|
||||
+
|
||||
+ phy->rstdev = devm_reset_control_get(&pdev->dev, "device");
|
||||
+ if (IS_ERR(phy->rstdev)) {
|
||||
+ dev_err(dev, "device reset is missing\n");
|
||||
+ return PTR_ERR(phy->rstdev);
|
||||
+ }
|
||||
+
|
||||
+ phy->phy = devm_phy_create(dev, NULL, &ralink_usb_phy_ops);
|
||||
+ if (IS_ERR(phy->phy)) {
|
||||
+ dev_err(dev, "failed to create PHY\n");
|
||||
+ return PTR_ERR(phy->phy);
|
||||
+ }
|
||||
+ phy_set_drvdata(phy->phy, phy);
|
||||
+
|
||||
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
+
|
||||
+ return PTR_ERR_OR_ZERO(phy_provider);
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver ralink_usb_phy_driver = {
|
||||
+ .probe = ralink_usb_phy_probe,
|
||||
+ .driver = {
|
||||
+ .of_match_table = ralink_usb_phy_of_match,
|
||||
+ .name = "ralink-usb-phy",
|
||||
+ }
|
||||
+};
|
||||
+module_platform_driver(ralink_usb_phy_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Ralink USB phy driver");
|
||||
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
|
||||
+MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,246 @@
|
|||
From 975e76214cd2516eb6cfff4c3eec581872645e88 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Thu, 19 Sep 2013 01:50:59 +0200
|
||||
Subject: [PATCH 31/53] uvc: add iPassion iP2970 support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/media/usb/uvc/uvc_driver.c | 12 +++
|
||||
drivers/media/usb/uvc/uvc_status.c | 2 +
|
||||
drivers/media/usb/uvc/uvc_video.c | 147 ++++++++++++++++++++++++++++++++++++
|
||||
drivers/media/usb/uvc/uvcvideo.h | 5 +-
|
||||
4 files changed, 165 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/media/usb/uvc/uvc_driver.c
|
||||
+++ b/drivers/media/usb/uvc/uvc_driver.c
|
||||
@@ -2539,6 +2539,18 @@ static struct usb_device_id uvc_ids[] =
|
||||
.bInterfaceSubClass = 1,
|
||||
.bInterfaceProtocol = 0,
|
||||
.driver_info = UVC_QUIRK_FORCE_Y8 },
|
||||
+ /* iPassion iP2970 */
|
||||
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
|
||||
+ | USB_DEVICE_ID_MATCH_INT_INFO,
|
||||
+ .idVendor = 0x1B3B,
|
||||
+ .idProduct = 0x2970,
|
||||
+ .bInterfaceClass = USB_CLASS_VIDEO,
|
||||
+ .bInterfaceSubClass = 1,
|
||||
+ .bInterfaceProtocol = 0,
|
||||
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
|
||||
+ | UVC_QUIRK_STREAM_NO_FID
|
||||
+ | UVC_QUIRK_MOTION
|
||||
+ | UVC_QUIRK_SINGLE_ISO },
|
||||
/* Generic USB Video Class */
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
|
||||
{}
|
||||
--- a/drivers/media/usb/uvc/uvc_status.c
|
||||
+++ b/drivers/media/usb/uvc/uvc_status.c
|
||||
@@ -139,6 +139,7 @@ static void uvc_status_complete(struct u
|
||||
switch (dev->status[0] & 0x0f) {
|
||||
case UVC_STATUS_TYPE_CONTROL:
|
||||
uvc_event_control(dev, dev->status, len);
|
||||
+ dev->motion = 1;
|
||||
break;
|
||||
|
||||
case UVC_STATUS_TYPE_STREAMING:
|
||||
@@ -182,6 +183,7 @@ int uvc_status_init(struct uvc_device *d
|
||||
}
|
||||
|
||||
pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
|
||||
+ dev->motion = 0;
|
||||
|
||||
/* For high-speed interrupt endpoints, the bInterval value is used as
|
||||
* an exponent of two. Some developers forgot about it.
|
||||
--- a/drivers/media/usb/uvc/uvc_video.c
|
||||
+++ b/drivers/media/usb/uvc/uvc_video.c
|
||||
@@ -21,6 +21,11 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <asm/unaligned.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <linux/kobject.h>
|
||||
+#include <linux/netlink.h>
|
||||
+#include <linux/kobject.h>
|
||||
+#include <linux/workqueue.h>
|
||||
|
||||
#include <media/v4l2-common.h>
|
||||
|
||||
@@ -1092,9 +1097,149 @@ static void uvc_video_decode_data(struct
|
||||
}
|
||||
}
|
||||
|
||||
+struct bh_priv {
|
||||
+ unsigned long seen;
|
||||
+};
|
||||
+
|
||||
+struct bh_event {
|
||||
+ const char *name;
|
||||
+ struct sk_buff *skb;
|
||||
+ struct work_struct work;
|
||||
+};
|
||||
+
|
||||
+#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, "webcam", ##args )
|
||||
+#define BH_DBG(fmt, args...) do {} while (0)
|
||||
+#define BH_SKB_SIZE 2048
|
||||
+
|
||||
+extern u64 uevent_next_seqnum(void);
|
||||
+static int seen = 0;
|
||||
+
|
||||
+static int bh_event_add_var(struct bh_event *event, int argv,
|
||||
+ const char *format, ...)
|
||||
+{
|
||||
+ static char buf[128];
|
||||
+ char *s;
|
||||
+ va_list args;
|
||||
+ int len;
|
||||
+
|
||||
+ if (argv)
|
||||
+ return 0;
|
||||
+
|
||||
+ va_start(args, format);
|
||||
+ len = vsnprintf(buf, sizeof(buf), format, args);
|
||||
+ va_end(args);
|
||||
+
|
||||
+ if (len >= sizeof(buf)) {
|
||||
+ BH_ERR("buffer size too small\n");
|
||||
+ WARN_ON(1);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ s = skb_put(event->skb, len + 1);
|
||||
+ strcpy(s, buf);
|
||||
+
|
||||
+ BH_DBG("added variable '%s'\n", s);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int motion_hotplug_fill_event(struct bh_event *event)
|
||||
+{
|
||||
+ int s = jiffies;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!seen)
|
||||
+ seen = jiffies;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "HOME=%s", "/");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "PATH=%s",
|
||||
+ "/sbin:/bin:/usr/sbin:/usr/bin");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "SUBSYSTEM=usb");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "ACTION=motion");
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "SEEN=%d", s - seen);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ seen = s;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void motion_hotplug_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct bh_event *event = container_of(work, struct bh_event, work);
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);
|
||||
+ if (!event->skb)
|
||||
+ goto out_free_event;
|
||||
+
|
||||
+ ret = bh_event_add_var(event, 0, "%s@", "add");
|
||||
+ if (ret)
|
||||
+ goto out_free_skb;
|
||||
+
|
||||
+ ret = motion_hotplug_fill_event(event);
|
||||
+ if (ret)
|
||||
+ goto out_free_skb;
|
||||
+
|
||||
+ NETLINK_CB(event->skb).dst_group = 1;
|
||||
+ broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);
|
||||
+
|
||||
+out_free_skb:
|
||||
+ if (ret) {
|
||||
+ BH_ERR("work error %d\n", ret);
|
||||
+ kfree_skb(event->skb);
|
||||
+ }
|
||||
+out_free_event:
|
||||
+ kfree(event);
|
||||
+}
|
||||
+
|
||||
+static int motion_hotplug_create_event(void)
|
||||
+{
|
||||
+ struct bh_event *event;
|
||||
+
|
||||
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
|
||||
+ if (!event)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ event->name = "motion";
|
||||
+
|
||||
+ INIT_WORK(&event->work, (void *)(void *)motion_hotplug_work);
|
||||
+ schedule_work(&event->work);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#define MOTION_FLAG_OFFSET 4
|
||||
static void uvc_video_decode_end(struct uvc_streaming *stream,
|
||||
struct uvc_buffer *buf, const __u8 *data, int len)
|
||||
{
|
||||
+ if ((stream->dev->quirks & UVC_QUIRK_MOTION) &&
|
||||
+ (data[len - 2] == 0xff) && (data[len - 1] == 0xd9)) {
|
||||
+ u8 *mem;
|
||||
+ buf->state = UVC_BUF_STATE_READY;
|
||||
+ mem = (u8 *) (buf->mem + MOTION_FLAG_OFFSET);
|
||||
+ if ( stream->dev->motion ) {
|
||||
+ stream->dev->motion = 0;
|
||||
+ motion_hotplug_create_event();
|
||||
+ } else {
|
||||
+ *mem &= 0x7f;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* Mark the buffer as done if the EOF marker is set. */
|
||||
if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
|
||||
uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
|
||||
@@ -1507,6 +1652,8 @@ static int uvc_init_video_isoc(struct uv
|
||||
if (npackets == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
+ if (stream->dev->quirks & UVC_QUIRK_SINGLE_ISO)
|
||||
+ npackets = 1;
|
||||
size = npackets * psize;
|
||||
|
||||
for (i = 0; i < UVC_URBS; ++i) {
|
||||
--- a/drivers/media/usb/uvc/uvcvideo.h
|
||||
+++ b/drivers/media/usb/uvc/uvcvideo.h
|
||||
@@ -152,7 +152,9 @@
|
||||
#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200
|
||||
#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400
|
||||
#define UVC_QUIRK_FORCE_Y8 0x00000800
|
||||
-
|
||||
+#define UVC_QUIRK_MOTION 0x00001000
|
||||
+#define UVC_QUIRK_SINGLE_ISO 0x00002000
|
||||
+
|
||||
/* Format flags */
|
||||
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
|
||||
#define UVC_FMT_FLAG_STREAM 0x00000002
|
||||
@@ -550,6 +552,7 @@ struct uvc_device {
|
||||
__u8 *status;
|
||||
struct input_dev *input;
|
||||
char input_phys[64];
|
||||
+ int motion;
|
||||
};
|
||||
|
||||
enum uvc_handle_state {
|
|
@ -0,0 +1,29 @@
|
|||
From a758e0870c6d1e4b0272f6e7f9efa9face5534bb Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:49:07 +0100
|
||||
Subject: [PATCH 32/53] USB: dwc2: add device_reset()
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/usb/dwc2/hcd.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/usb/dwc2/hcd.c
|
||||
+++ b/drivers/usb/dwc2/hcd.c
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/usb.h>
|
||||
+#include <linux/reset.h>
|
||||
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/ch11.h>
|
||||
@@ -3002,6 +3003,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hso
|
||||
|
||||
retval = -ENOMEM;
|
||||
|
||||
+ device_reset(hsotg->dev);
|
||||
+
|
||||
hcfg = dwc2_readl(hsotg->regs + HCFG);
|
||||
dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg);
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,53 @@
|
|||
From 0b6eb1e68290243d439ee330ea8d0b239a5aec69 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:38:50 +0100
|
||||
Subject: [PATCH 34/53] NET: multi phy support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/phy/phy.c | 9 ++++++---
|
||||
include/linux/phy.h | 1 +
|
||||
2 files changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/phy.c
|
||||
+++ b/drivers/net/phy/phy.c
|
||||
@@ -888,7 +888,8 @@ void phy_state_machine(struct work_struc
|
||||
/* If the link is down, give up on negotiation for now */
|
||||
if (!phydev->link) {
|
||||
phydev->state = PHY_NOLINK;
|
||||
- netif_carrier_off(phydev->attached_dev);
|
||||
+ if (!phydev->no_auto_carrier_off)
|
||||
+ netif_carrier_off(phydev->attached_dev);
|
||||
phydev->adjust_link(phydev->attached_dev);
|
||||
break;
|
||||
}
|
||||
@@ -971,7 +972,8 @@ void phy_state_machine(struct work_struc
|
||||
netif_carrier_on(phydev->attached_dev);
|
||||
} else {
|
||||
phydev->state = PHY_NOLINK;
|
||||
- netif_carrier_off(phydev->attached_dev);
|
||||
+ if (!phydev->no_auto_carrier_off)
|
||||
+ netif_carrier_off(phydev->attached_dev);
|
||||
}
|
||||
|
||||
phydev->adjust_link(phydev->attached_dev);
|
||||
@@ -983,7 +985,8 @@ void phy_state_machine(struct work_struc
|
||||
case PHY_HALTED:
|
||||
if (phydev->link) {
|
||||
phydev->link = 0;
|
||||
- netif_carrier_off(phydev->attached_dev);
|
||||
+ if (!phydev->no_auto_carrier_off)
|
||||
+ netif_carrier_off(phydev->attached_dev);
|
||||
phydev->adjust_link(phydev->attached_dev);
|
||||
do_suspend = true;
|
||||
}
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -377,6 +377,7 @@ struct phy_device {
|
||||
bool is_pseudo_fixed_link;
|
||||
bool has_fixups;
|
||||
bool suspended;
|
||||
+ bool no_auto_carrier_off;
|
||||
|
||||
enum phy_state state;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
From 8e72a3a1be8f6328bd7ef491332ba541547b6086 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 15 Jul 2013 00:38:51 +0200
|
||||
Subject: [PATCH 36/53] mtd: fix cfi cmdset 0002 erase status check
|
||||
|
||||
---
|
||||
drivers/mtd/chips/cfi_cmdset_0002.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
|
||||
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
|
||||
@@ -2291,7 +2291,7 @@ static int __xipram do_erase_chip(struct
|
||||
chip->erase_suspended = 0;
|
||||
}
|
||||
|
||||
- if (chip_ready(map, adr))
|
||||
+ if (chip_good(map, adr, map_word_ff(map)))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeo)) {
|
||||
@@ -2380,7 +2380,7 @@ static int __xipram do_erase_oneblock(st
|
||||
chip->erase_suspended = 0;
|
||||
}
|
||||
|
||||
- if (chip_ready(map, adr)) {
|
||||
+ if (chip_good(map, adr, map_word_ff(map))) {
|
||||
xip_enable(map, chip, adr);
|
||||
break;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
From ee9081b2726a5ca8cde5497afdc5425e21ff8f8b Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 15 Jul 2013 00:39:21 +0200
|
||||
Subject: [PATCH 37/53] mtd: cfi cmdset 0002 force word write
|
||||
|
||||
---
|
||||
drivers/mtd/chips/cfi_cmdset_0002.c | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
|
||||
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
|
||||
@@ -40,7 +40,7 @@
|
||||
#include <linux/mtd/xip.h>
|
||||
|
||||
#define AMD_BOOTLOC_BUG
|
||||
-#define FORCE_WORD_WRITE 0
|
||||
+#define FORCE_WORD_WRITE 1
|
||||
|
||||
#define MAX_WORD_RETRIES 3
|
||||
|
||||
@@ -51,7 +51,9 @@
|
||||
|
||||
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
+#if !FORCE_WORD_WRITE
|
||||
static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
+#endif
|
||||
static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
|
||||
static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
|
||||
static void cfi_amdstd_sync (struct mtd_info *);
|
||||
@@ -202,6 +204,7 @@ static void fixup_amd_bootblock(struct m
|
||||
}
|
||||
#endif
|
||||
|
||||
+#if !FORCE_WORD_WRITE
|
||||
static void fixup_use_write_buffers(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
@@ -211,6 +214,7 @@ static void fixup_use_write_buffers(stru
|
||||
mtd->_write = cfi_amdstd_write_buffers;
|
||||
}
|
||||
}
|
||||
+#endif /* !FORCE_WORD_WRITE */
|
||||
|
||||
/* Atmel chips don't use the same PRI format as AMD chips */
|
||||
static void fixup_convert_atmel_pri(struct mtd_info *mtd)
|
||||
@@ -1789,6 +1793,7 @@ static int cfi_amdstd_write_words(struct
|
||||
/*
|
||||
* FIXME: interleaved mode not tested, and probably not supported!
|
||||
*/
|
||||
+#if !FORCE_WORD_WRITE
|
||||
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, const u_char *buf,
|
||||
int len)
|
||||
@@ -1917,7 +1922,6 @@ static int __xipram do_write_buffer(stru
|
||||
return ret;
|
||||
}
|
||||
|
||||
-
|
||||
static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
@@ -1992,6 +1996,7 @@ static int cfi_amdstd_write_buffers(stru
|
||||
|
||||
return 0;
|
||||
}
|
||||
+#endif /* !FORCE_WORD_WRITE */
|
||||
|
||||
/*
|
||||
* Wait for the flash chip to become ready to write data
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,157 @@
|
|||
From 61e17c2f864698033f4661e1fc7ba63d4d982491 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:21:55 +0100
|
||||
Subject: [PATCH 40/53] nand: add mtk-nand hack/hook
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/mtd/nand/mtk_nand.c | 34 ++++++++++++++++++++++++++++++----
|
||||
drivers/mtd/nand/nand_base.c | 9 ++++++++-
|
||||
drivers/mtd/nand/nand_device_list.h | 2 ++
|
||||
include/linux/mtd/nand.h | 4 ++++
|
||||
4 files changed, 44 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/nand/mtk_nand.c
|
||||
+++ b/drivers/mtd/nand/mtk_nand.c
|
||||
@@ -110,6 +110,10 @@ int part_num = NUM_PARTITIONS;
|
||||
int manu_id;
|
||||
int dev_id;
|
||||
|
||||
+/* this constant was taken from linux/nand/nand.h v 3.14
|
||||
+ * in later versions it seems it was removed in order to save a bit of space
|
||||
+ */
|
||||
+#define NAND_MAX_OOBSIZE 774
|
||||
static u8 local_oob_buf[NAND_MAX_OOBSIZE];
|
||||
|
||||
static u8 nand_badblock_offset = 0;
|
||||
@@ -348,7 +352,7 @@ mtk_nand_check_bch_error(struct mtd_info
|
||||
if (0xF == u4ErrNum) {
|
||||
mtd->ecc_stats.failed++;
|
||||
bRet = false;
|
||||
- //printk(KERN_ERR"UnCorrectable at PageAddr=%d\n", u4PageAddr);
|
||||
+ printk(KERN_ERR"mtk_nand: UnCorrectable at PageAddr=%d\n", u4PageAddr);
|
||||
} else {
|
||||
for (i = 0; i < ((u4ErrNum + 1) >> 1); ++i) {
|
||||
au4ErrBitLoc[i] = DRV_Reg32(ECC_DECEL0_REG32 + i);
|
||||
@@ -1422,7 +1426,7 @@ mtk_nand_erase_hw(struct mtd_info *mtd,
|
||||
{
|
||||
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
||||
|
||||
- chip->erase_cmd(mtd, page);
|
||||
+ chip->erase(mtd, page);
|
||||
|
||||
return chip->waitfunc(mtd, chip);
|
||||
}
|
||||
@@ -2094,8 +2098,8 @@ mtk_nand_probe(struct platform_device *p
|
||||
nand_chip->write_page = mtk_nand_write_page;
|
||||
nand_chip->ecc.write_oob = mtk_nand_write_oob;
|
||||
nand_chip->block_markbad = mtk_nand_block_markbad; // need to add nand_get_device()/nand_release_device().
|
||||
- // nand_chip->erase = mtk_nand_erase;
|
||||
- // nand_chip->read_page = mtk_nand_read_page;
|
||||
+ nand_chip->erase_mtk = mtk_nand_erase;
|
||||
+ nand_chip->read_page = mtk_nand_read_page;
|
||||
nand_chip->ecc.read_oob = mtk_nand_read_oob;
|
||||
nand_chip->block_bad = mtk_nand_block_bad;
|
||||
|
||||
@@ -2175,6 +2179,21 @@ mtk_nand_probe(struct platform_device *p
|
||||
nand_chip->pagemask = (nand_chip->chipsize >> nand_chip->page_shift) - 1;
|
||||
nand_chip->phys_erase_shift = ffs(mtd->erasesize) - 1;
|
||||
nand_chip->chip_shift = ffs(nand_chip->chipsize) - 1;//0x1C;//ffs(nand_chip->chipsize) - 1;
|
||||
+
|
||||
+ /* allocate buffers or call select_chip here or a bit earlier*/
|
||||
+ {
|
||||
+ struct nand_buffers *nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + mtd->oobsize * 3, GFP_KERNEL);
|
||||
+ if (!nbuf) {
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ nbuf->ecccalc = (uint8_t *)(nbuf + 1);
|
||||
+ nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
|
||||
+ nbuf->databuf = nbuf->ecccode + mtd->oobsize;
|
||||
+
|
||||
+ nand_chip->buffers = nbuf;
|
||||
+ nand_chip->options |= NAND_OWN_BUFFERS;
|
||||
+ }
|
||||
+
|
||||
nand_chip->oob_poi = nand_chip->buffers->databuf + mtd->writesize;
|
||||
nand_chip->badblockpos = 0;
|
||||
|
||||
@@ -2251,6 +2270,9 @@ out:
|
||||
MSG(INIT, "[NFI] mtk_nand_probe fail, err = %d!\n", err);
|
||||
nand_release(mtd);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
+ if ( NULL != nand_chip->buffers) {
|
||||
+ kfree(nand_chip->buffers);
|
||||
+ }
|
||||
kfree(host);
|
||||
nand_disable_clock();
|
||||
return err;
|
||||
@@ -2261,8 +2283,12 @@ mtk_nand_remove(struct platform_device *
|
||||
{
|
||||
struct mtk_nand_host *host = platform_get_drvdata(pdev);
|
||||
struct mtd_info *mtd = &host->mtd;
|
||||
+ struct nand_chip *nand_chip = &host->nand_chip;
|
||||
|
||||
nand_release(mtd);
|
||||
+ if ( NULL != nand_chip->buffers) {
|
||||
+ kfree(nand_chip->buffers);
|
||||
+ }
|
||||
kfree(host);
|
||||
nand_disable_clock();
|
||||
|
||||
--- a/drivers/mtd/nand/nand_base.c
|
||||
+++ b/drivers/mtd/nand/nand_base.c
|
||||
@@ -1727,6 +1727,9 @@ static int nand_do_read_ops(struct mtd_i
|
||||
__func__, buf);
|
||||
|
||||
read_retry:
|
||||
+#ifdef CONFIG_MTK_MTD_NAND
|
||||
+ ret = chip->read_page(mtd, chip, bufpoi, page);
|
||||
+#else
|
||||
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
|
||||
|
||||
/*
|
||||
@@ -1745,6 +1748,7 @@ read_retry:
|
||||
else
|
||||
ret = chip->ecc.read_page(mtd, chip, bufpoi,
|
||||
oob_required, page);
|
||||
+#endif /* CONFIG_MTK_MTD_NAND */
|
||||
if (ret < 0) {
|
||||
if (use_bufpoi)
|
||||
/* Invalidate page cache */
|
||||
@@ -2932,8 +2936,11 @@ int nand_erase_nand(struct mtd_info *mtd
|
||||
if (page <= chip->pagebuf && chip->pagebuf <
|
||||
(page + pages_per_block))
|
||||
chip->pagebuf = -1;
|
||||
-
|
||||
+#ifdef CONFIG_MTK_MTD_NAND
|
||||
+ status = chip->erase_mtk(mtd, page & chip->pagemask);
|
||||
+#else
|
||||
status = chip->erase(mtd, page & chip->pagemask);
|
||||
+#endif /* CONFIG_MTK_MTD_NAND */
|
||||
|
||||
/*
|
||||
* See if operation failed and additional status checks are
|
||||
--- a/drivers/mtd/nand/nand_device_list.h
|
||||
+++ b/drivers/mtd/nand/nand_device_list.h
|
||||
@@ -43,6 +43,8 @@ static const flashdev_info gen_FlashTabl
|
||||
{0xADBC, 0x905554, 5, 16, 512, 128, 2048, 64, 0x10801011, "H9DA4GH4JJAMC", 0},
|
||||
{0x01F1, 0x801D01, 4, 8, 128, 128, 2048, 64, 0x30C77fff, "S34ML01G100TF", 0},
|
||||
{0x92F1, 0x8095FF, 4, 8, 128, 128, 2048, 64, 0x30C77fff, "F59L1G81A", 0},
|
||||
+ {0xC8DA, 0x909544, 5, 8, 256, 128, 2048, 64, 0x30C77fff, "F59L2G81A", 0},
|
||||
+ {0xC8DC, 0x909554, 5, 8, 512, 128, 2048, 64, 0x30C77fff, "F59L4G81A", 0},
|
||||
{0xECD3, 0x519558, 5, 8, 1024, 128, 2048, 64, 0x44333, "K9K8G8000", 0},
|
||||
{0xC2F1, 0x801DC2, 4, 8, 128, 128, 2048, 64, 0x30C77fff, "MX30LF1G08AA", 0},
|
||||
{0x98D3, 0x902676, 5, 8, 1024, 256, 4096, 224, 0x00C25332, "TC58NVG3S0F", 0},
|
||||
--- a/include/linux/mtd/nand.h
|
||||
+++ b/include/linux/mtd/nand.h
|
||||
@@ -665,6 +665,10 @@ struct nand_chip {
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw);
|
||||
+#ifdef CONFIG_MTK_MTD_NAND
|
||||
+ int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, u8 *buf, int page);
|
||||
+ int (*erase_mtk)(struct mtd_info *mtd, int page);
|
||||
+#endif /* CONFIG_MTK_MTD_NAND */
|
||||
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
|
@ -0,0 +1,44 @@
|
|||
From da6015e7f19d749f135f7ac55c4ec47b06faa868 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Fri, 9 Aug 2013 20:12:59 +0200
|
||||
Subject: [PATCH 41/53] DT: Add documentation for spi-rt2880
|
||||
|
||||
Describe the SPI master found on the MIPS based Ralink RT2880 SoC.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
.../devicetree/bindings/spi/spi-rt2880.txt | 28 ++++++++++++++++++++
|
||||
1 file changed, 28 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/spi/spi-rt2880.txt
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/spi/spi-rt2880.txt
|
||||
@@ -0,0 +1,28 @@
|
||||
+Ralink SoC RT2880 SPI master controller.
|
||||
+
|
||||
+This SPI controller is found on most wireless SoCs made by ralink.
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible : "ralink,rt2880-spi"
|
||||
+- reg : The register base for the controller.
|
||||
+- #address-cells : <1>, as required by generic SPI binding.
|
||||
+- #size-cells : <0>, also as required by generic SPI binding.
|
||||
+
|
||||
+Child nodes as per the generic SPI binding.
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+ spi@b00 {
|
||||
+ compatible = "ralink,rt2880-spi";
|
||||
+ reg = <0xb00 0x100>;
|
||||
+
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ m25p80@0 {
|
||||
+ compatible = "m25p80";
|
||||
+ reg = <0>;
|
||||
+ spi-max-frequency = <10000000>;
|
||||
+ };
|
||||
+ };
|
||||
+
|
|
@ -0,0 +1,574 @@
|
|||
From 683af4ebb91a1600df1946ac4769d916b8a1be65 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 11:15:12 +0100
|
||||
Subject: [PATCH 42/53] SPI: ralink: add Ralink SoC spi driver
|
||||
|
||||
Add the driver needed to make SPI work on Ralink SoC.
|
||||
|
||||
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
||||
Acked-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/spi/Kconfig | 6 +
|
||||
drivers/spi/Makefile | 1 +
|
||||
drivers/spi/spi-rt2880.c | 530 ++++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 537 insertions(+)
|
||||
create mode 100644 drivers/spi/spi-rt2880.c
|
||||
|
||||
--- a/drivers/spi/Kconfig
|
||||
+++ b/drivers/spi/Kconfig
|
||||
@@ -477,6 +477,12 @@ config SPI_QUP
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called spi_qup.
|
||||
|
||||
+config SPI_RT2880
|
||||
+ tristate "Ralink RT288x SPI Controller"
|
||||
+ depends on RALINK
|
||||
+ help
|
||||
+ This selects a driver for the Ralink RT288x/RT305x SPI Controller.
|
||||
+
|
||||
config SPI_S3C24XX
|
||||
tristate "Samsung S3C24XX series SPI"
|
||||
depends on ARCH_S3C24XX
|
||||
--- a/drivers/spi/Makefile
|
||||
+++ b/drivers/spi/Makefile
|
||||
@@ -70,6 +70,7 @@ obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
|
||||
obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
|
||||
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
|
||||
+obj-$(CONFIG_SPI_RT2880) += spi-rt2880.o
|
||||
obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
|
||||
spi-s3c24xx-hw-y := spi-s3c24xx.o
|
||||
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/spi/spi-rt2880.c
|
||||
@@ -0,0 +1,530 @@
|
||||
+/*
|
||||
+ * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver
|
||||
+ *
|
||||
+ * Copyright (C) 2011 Sergiy <piratfm@gmail.com>
|
||||
+ * Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ *
|
||||
+ * Some parts are based on spi-orion.c:
|
||||
+ * Author: Shadi Ammouri <shadi@marvell.com>
|
||||
+ * Copyright (C) 2007-2008 Marvell Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/spi/spi.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/gpio.h>
|
||||
+
|
||||
+#define DRIVER_NAME "spi-rt2880"
|
||||
+
|
||||
+#define RAMIPS_SPI_STAT 0x00
|
||||
+#define RAMIPS_SPI_CFG 0x10
|
||||
+#define RAMIPS_SPI_CTL 0x14
|
||||
+#define RAMIPS_SPI_DATA 0x20
|
||||
+#define RAMIPS_SPI_ADDR 0x24
|
||||
+#define RAMIPS_SPI_BS 0x28
|
||||
+#define RAMIPS_SPI_USER 0x2C
|
||||
+#define RAMIPS_SPI_TXFIFO 0x30
|
||||
+#define RAMIPS_SPI_RXFIFO 0x34
|
||||
+#define RAMIPS_SPI_FIFO_STAT 0x38
|
||||
+#define RAMIPS_SPI_MODE 0x3C
|
||||
+#define RAMIPS_SPI_DEV_OFFSET 0x40
|
||||
+#define RAMIPS_SPI_DMA 0x80
|
||||
+#define RAMIPS_SPI_DMASTAT 0x84
|
||||
+#define RAMIPS_SPI_ARBITER 0xF0
|
||||
+
|
||||
+/* SPISTAT register bit field */
|
||||
+#define SPISTAT_BUSY BIT(0)
|
||||
+
|
||||
+/* SPICFG register bit field */
|
||||
+#define SPICFG_ADDRMODE BIT(12)
|
||||
+#define SPICFG_RXENVDIS BIT(11)
|
||||
+#define SPICFG_RXCAP BIT(10)
|
||||
+#define SPICFG_SPIENMODE BIT(9)
|
||||
+#define SPICFG_MSBFIRST BIT(8)
|
||||
+#define SPICFG_SPICLKPOL BIT(6)
|
||||
+#define SPICFG_RXCLKEDGE_FALLING BIT(5)
|
||||
+#define SPICFG_TXCLKEDGE_FALLING BIT(4)
|
||||
+#define SPICFG_HIZSPI BIT(3)
|
||||
+#define SPICFG_SPICLK_PRESCALE_MASK 0x7
|
||||
+#define SPICFG_SPICLK_DIV2 0
|
||||
+#define SPICFG_SPICLK_DIV4 1
|
||||
+#define SPICFG_SPICLK_DIV8 2
|
||||
+#define SPICFG_SPICLK_DIV16 3
|
||||
+#define SPICFG_SPICLK_DIV32 4
|
||||
+#define SPICFG_SPICLK_DIV64 5
|
||||
+#define SPICFG_SPICLK_DIV128 6
|
||||
+#define SPICFG_SPICLK_DISABLE 7
|
||||
+
|
||||
+/* SPICTL register bit field */
|
||||
+#define SPICTL_START BIT(4)
|
||||
+#define SPICTL_HIZSDO BIT(3)
|
||||
+#define SPICTL_STARTWR BIT(2)
|
||||
+#define SPICTL_STARTRD BIT(1)
|
||||
+#define SPICTL_SPIENA BIT(0)
|
||||
+
|
||||
+/* SPIUSER register bit field */
|
||||
+#define SPIUSER_USERMODE BIT(21)
|
||||
+#define SPIUSER_INSTR_PHASE BIT(20)
|
||||
+#define SPIUSER_ADDR_PHASE_MASK 0x7
|
||||
+#define SPIUSER_ADDR_PHASE_OFFSET 17
|
||||
+#define SPIUSER_MODE_PHASE BIT(16)
|
||||
+#define SPIUSER_DUMMY_PHASE_MASK 0x3
|
||||
+#define SPIUSER_DUMMY_PHASE_OFFSET 14
|
||||
+#define SPIUSER_DATA_PHASE_MASK 0x3
|
||||
+#define SPIUSER_DATA_PHASE_OFFSET 12
|
||||
+#define SPIUSER_DATA_READ (BIT(0) << SPIUSER_DATA_PHASE_OFFSET)
|
||||
+#define SPIUSER_DATA_WRITE (BIT(1) << SPIUSER_DATA_PHASE_OFFSET)
|
||||
+#define SPIUSER_ADDR_TYPE_OFFSET 9
|
||||
+#define SPIUSER_MODE_TYPE_OFFSET 6
|
||||
+#define SPIUSER_DUMMY_TYPE_OFFSET 3
|
||||
+#define SPIUSER_DATA_TYPE_OFFSET 0
|
||||
+#define SPIUSER_TRANSFER_MASK 0x7
|
||||
+#define SPIUSER_TRANSFER_SINGLE BIT(0)
|
||||
+#define SPIUSER_TRANSFER_DUAL BIT(1)
|
||||
+#define SPIUSER_TRANSFER_QUAD BIT(2)
|
||||
+
|
||||
+#define SPIUSER_TRANSFER_TYPE(type) ( \
|
||||
+ (type << SPIUSER_ADDR_TYPE_OFFSET) | \
|
||||
+ (type << SPIUSER_MODE_TYPE_OFFSET) | \
|
||||
+ (type << SPIUSER_DUMMY_TYPE_OFFSET) | \
|
||||
+ (type << SPIUSER_DATA_TYPE_OFFSET) \
|
||||
+)
|
||||
+
|
||||
+/* SPIFIFOSTAT register bit field */
|
||||
+#define SPIFIFOSTAT_TXEMPTY BIT(19)
|
||||
+#define SPIFIFOSTAT_RXEMPTY BIT(18)
|
||||
+#define SPIFIFOSTAT_TXFULL BIT(17)
|
||||
+#define SPIFIFOSTAT_RXFULL BIT(16)
|
||||
+#define SPIFIFOSTAT_FIFO_MASK 0xff
|
||||
+#define SPIFIFOSTAT_TX_OFFSET 8
|
||||
+#define SPIFIFOSTAT_RX_OFFSET 0
|
||||
+
|
||||
+#define SPI_FIFO_DEPTH 16
|
||||
+
|
||||
+/* SPIMODE register bit field */
|
||||
+#define SPIMODE_MODE_OFFSET 24
|
||||
+#define SPIMODE_DUMMY_OFFSET 0
|
||||
+
|
||||
+/* SPIARB register bit field */
|
||||
+#define SPICTL_ARB_EN BIT(31)
|
||||
+#define SPICTL_CSCTL1 BIT(16)
|
||||
+#define SPI1_POR BIT(1)
|
||||
+#define SPI0_POR BIT(0)
|
||||
+
|
||||
+#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \
|
||||
+ SPI_CS_HIGH)
|
||||
+
|
||||
+static atomic_t hw_reset_count = ATOMIC_INIT(0);
|
||||
+
|
||||
+struct rt2880_spi {
|
||||
+ struct spi_master *master;
|
||||
+ void __iomem *base;
|
||||
+ u32 speed;
|
||||
+ u16 wait_loops;
|
||||
+ u16 mode;
|
||||
+ struct clk *clk;
|
||||
+};
|
||||
+
|
||||
+static inline struct rt2880_spi *spidev_to_rt2880_spi(struct spi_device *spi)
|
||||
+{
|
||||
+ return spi_master_get_devdata(spi->master);
|
||||
+}
|
||||
+
|
||||
+static inline u32 rt2880_spi_read(struct rt2880_spi *rs, u32 reg)
|
||||
+{
|
||||
+ return ioread32(rs->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void rt2880_spi_write(struct rt2880_spi *rs, u32 reg,
|
||||
+ const u32 val)
|
||||
+{
|
||||
+ iowrite32(val, rs->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void rt2880_spi_setbits(struct rt2880_spi *rs, u32 reg, u32 mask)
|
||||
+{
|
||||
+ void __iomem *addr = rs->base + reg;
|
||||
+
|
||||
+ iowrite32((ioread32(addr) | mask), addr);
|
||||
+}
|
||||
+
|
||||
+static inline void rt2880_spi_clrbits(struct rt2880_spi *rs, u32 reg, u32 mask)
|
||||
+{
|
||||
+ void __iomem *addr = rs->base + reg;
|
||||
+
|
||||
+ iowrite32((ioread32(addr) & ~mask), addr);
|
||||
+}
|
||||
+
|
||||
+static u32 rt2880_spi_baudrate_get(struct spi_device *spi, unsigned int speed)
|
||||
+{
|
||||
+ struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
|
||||
+ u32 rate;
|
||||
+ u32 prescale;
|
||||
+
|
||||
+ /*
|
||||
+ * the supported rates are: 2, 4, 8, ... 128
|
||||
+ * round up as we look for equal or less speed
|
||||
+ */
|
||||
+ rate = DIV_ROUND_UP(clk_get_rate(rs->clk), speed);
|
||||
+ rate = roundup_pow_of_two(rate);
|
||||
+
|
||||
+ /* Convert the rate to SPI clock divisor value. */
|
||||
+ prescale = ilog2(rate / 2);
|
||||
+
|
||||
+ /* some tolerance. double and add 100 */
|
||||
+ rs->wait_loops = (8 * HZ * loops_per_jiffy) /
|
||||
+ (clk_get_rate(rs->clk) / rate);
|
||||
+ rs->wait_loops = (rs->wait_loops << 1) + 100;
|
||||
+ rs->speed = speed;
|
||||
+
|
||||
+ dev_dbg(&spi->dev, "speed: %lu/%u, rate: %u, prescal: %u, loops: %hu\n",
|
||||
+ clk_get_rate(rs->clk) / rate, speed, rate, prescale,
|
||||
+ rs->wait_loops);
|
||||
+
|
||||
+ return prescale;
|
||||
+}
|
||||
+
|
||||
+static u32 get_arbiter_offset(struct spi_master *master)
|
||||
+{
|
||||
+ u32 offset;
|
||||
+
|
||||
+ offset = RAMIPS_SPI_ARBITER;
|
||||
+ if (master->bus_num == 1)
|
||||
+ offset -= RAMIPS_SPI_DEV_OFFSET;
|
||||
+
|
||||
+ return offset;
|
||||
+}
|
||||
+
|
||||
+static void rt2880_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
+{
|
||||
+ struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
|
||||
+
|
||||
+ if (enable)
|
||||
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
|
||||
+ else
|
||||
+ rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
|
||||
+}
|
||||
+
|
||||
+static int rt2880_spi_wait_ready(struct rt2880_spi *rs, int len)
|
||||
+{
|
||||
+ int loop = rs->wait_loops * len;
|
||||
+
|
||||
+ while ((rt2880_spi_read(rs, RAMIPS_SPI_STAT) & SPISTAT_BUSY) && --loop)
|
||||
+ cpu_relax();
|
||||
+
|
||||
+ if (loop)
|
||||
+ return 0;
|
||||
+
|
||||
+ return -ETIMEDOUT;
|
||||
+}
|
||||
+
|
||||
+static void rt2880_dump_reg(struct spi_master *master)
|
||||
+{
|
||||
+ struct rt2880_spi *rs = spi_master_get_devdata(master);
|
||||
+
|
||||
+ dev_dbg(&master->dev, "stat: %08x, cfg: %08x, ctl: %08x, " \
|
||||
+ "data: %08x, arb: %08x\n",
|
||||
+ rt2880_spi_read(rs, RAMIPS_SPI_STAT),
|
||||
+ rt2880_spi_read(rs, RAMIPS_SPI_CFG),
|
||||
+ rt2880_spi_read(rs, RAMIPS_SPI_CTL),
|
||||
+ rt2880_spi_read(rs, RAMIPS_SPI_DATA),
|
||||
+ rt2880_spi_read(rs, get_arbiter_offset(master)));
|
||||
+}
|
||||
+
|
||||
+static int rt2880_spi_transfer_one(struct spi_master *master,
|
||||
+ struct spi_device *spi, struct spi_transfer *xfer)
|
||||
+{
|
||||
+ struct rt2880_spi *rs = spi_master_get_devdata(master);
|
||||
+ unsigned len;
|
||||
+ const u8 *tx = xfer->tx_buf;
|
||||
+ u8 *rx = xfer->rx_buf;
|
||||
+ int err = 0;
|
||||
+
|
||||
+ /* change clock speed */
|
||||
+ if (unlikely(rs->speed != xfer->speed_hz)) {
|
||||
+ u32 reg;
|
||||
+ reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
|
||||
+ reg &= ~SPICFG_SPICLK_PRESCALE_MASK;
|
||||
+ reg |= rt2880_spi_baudrate_get(spi, xfer->speed_hz);
|
||||
+ rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
|
||||
+ }
|
||||
+
|
||||
+ if (tx) {
|
||||
+ len = xfer->len;
|
||||
+ while (len-- > 0) {
|
||||
+ rt2880_spi_write(rs, RAMIPS_SPI_DATA, *tx++);
|
||||
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR);
|
||||
+ err = rt2880_spi_wait_ready(rs, 1);
|
||||
+ if (err) {
|
||||
+ dev_err(&spi->dev, "TX failed, err=%d\n", err);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (rx) {
|
||||
+ len = xfer->len;
|
||||
+ while (len-- > 0) {
|
||||
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD);
|
||||
+ err = rt2880_spi_wait_ready(rs, 1);
|
||||
+ if (err) {
|
||||
+ dev_err(&spi->dev, "RX failed, err=%d\n", err);
|
||||
+ goto out;
|
||||
+ }
|
||||
+ *rx++ = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+/* copy from spi.c */
|
||||
+static void spi_set_cs(struct spi_device *spi, bool enable)
|
||||
+{
|
||||
+ if (spi->mode & SPI_CS_HIGH)
|
||||
+ enable = !enable;
|
||||
+
|
||||
+ if (spi->cs_gpio >= 0)
|
||||
+ gpio_set_value(spi->cs_gpio, !enable);
|
||||
+ else if (spi->master->set_cs)
|
||||
+ spi->master->set_cs(spi, !enable);
|
||||
+}
|
||||
+
|
||||
+static int rt2880_spi_setup(struct spi_device *spi)
|
||||
+{
|
||||
+ struct spi_master *master = spi->master;
|
||||
+ struct rt2880_spi *rs = spi_master_get_devdata(master);
|
||||
+ u32 reg, old_reg, arbit_off;
|
||||
+
|
||||
+ if ((spi->max_speed_hz > master->max_speed_hz) ||
|
||||
+ (spi->max_speed_hz < master->min_speed_hz)) {
|
||||
+ dev_err(&spi->dev, "invalide requested speed %d Hz\n",
|
||||
+ spi->max_speed_hz);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (!(master->bits_per_word_mask &
|
||||
+ BIT(spi->bits_per_word - 1))) {
|
||||
+ dev_err(&spi->dev, "invalide bits_per_word %d\n",
|
||||
+ spi->bits_per_word);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* the hardware seems can't work on mode0 force it to mode3 */
|
||||
+ if ((spi->mode & (SPI_CPOL | SPI_CPHA)) == SPI_MODE_0) {
|
||||
+ dev_warn(&spi->dev, "force spi mode3\n");
|
||||
+ spi->mode |= SPI_MODE_3;
|
||||
+ }
|
||||
+
|
||||
+ /* chip polarity */
|
||||
+ arbit_off = get_arbiter_offset(master);
|
||||
+ reg = old_reg = rt2880_spi_read(rs, arbit_off);
|
||||
+ if (spi->mode & SPI_CS_HIGH) {
|
||||
+ switch (master->bus_num) {
|
||||
+ case 1:
|
||||
+ reg |= SPI1_POR;
|
||||
+ break;
|
||||
+ default:
|
||||
+ reg |= SPI0_POR;
|
||||
+ break;
|
||||
+ }
|
||||
+ } else {
|
||||
+ switch (master->bus_num) {
|
||||
+ case 1:
|
||||
+ reg &= ~SPI1_POR;
|
||||
+ break;
|
||||
+ default:
|
||||
+ reg &= ~SPI0_POR;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* enable spi1 */
|
||||
+ if (master->bus_num == 1)
|
||||
+ reg |= SPICTL_ARB_EN;
|
||||
+
|
||||
+ if (reg != old_reg)
|
||||
+ rt2880_spi_write(rs, arbit_off, reg);
|
||||
+
|
||||
+ /* deselected the spi device */
|
||||
+ spi_set_cs(spi, false);
|
||||
+
|
||||
+ rt2880_dump_reg(master);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_spi_prepare_message(struct spi_master *master,
|
||||
+ struct spi_message *msg)
|
||||
+{
|
||||
+ struct rt2880_spi *rs = spi_master_get_devdata(master);
|
||||
+ struct spi_device *spi = msg->spi;
|
||||
+ u32 reg;
|
||||
+
|
||||
+ if ((rs->mode == spi->mode) && (rs->speed == spi->max_speed_hz))
|
||||
+ return 0;
|
||||
+
|
||||
+#if 0
|
||||
+ /* set spido to tri-state */
|
||||
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO);
|
||||
+#endif
|
||||
+
|
||||
+ reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
|
||||
+
|
||||
+ reg &= ~(SPICFG_MSBFIRST | SPICFG_SPICLKPOL |
|
||||
+ SPICFG_RXCLKEDGE_FALLING |
|
||||
+ SPICFG_TXCLKEDGE_FALLING |
|
||||
+ SPICFG_SPICLK_PRESCALE_MASK);
|
||||
+
|
||||
+ /* MSB */
|
||||
+ if (!(spi->mode & SPI_LSB_FIRST))
|
||||
+ reg |= SPICFG_MSBFIRST;
|
||||
+
|
||||
+ /* spi mode */
|
||||
+ switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {
|
||||
+ case SPI_MODE_0:
|
||||
+ reg |= SPICFG_TXCLKEDGE_FALLING;
|
||||
+ break;
|
||||
+ case SPI_MODE_1:
|
||||
+ reg |= SPICFG_RXCLKEDGE_FALLING;
|
||||
+ break;
|
||||
+ case SPI_MODE_2:
|
||||
+ reg |= SPICFG_SPICLKPOL | SPICFG_RXCLKEDGE_FALLING;
|
||||
+ break;
|
||||
+ case SPI_MODE_3:
|
||||
+ reg |= SPICFG_SPICLKPOL | SPICFG_TXCLKEDGE_FALLING;
|
||||
+ break;
|
||||
+ }
|
||||
+ rs->mode = spi->mode;
|
||||
+
|
||||
+#if 0
|
||||
+ /* set spiclk and spiena to tri-state */
|
||||
+ reg |= SPICFG_HIZSPI;
|
||||
+#endif
|
||||
+
|
||||
+ /* clock divide */
|
||||
+ reg |= rt2880_spi_baudrate_get(spi, spi->max_speed_hz);
|
||||
+
|
||||
+ rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_spi_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct spi_master *master;
|
||||
+ struct rt2880_spi *rs;
|
||||
+ void __iomem *base;
|
||||
+ struct resource *r;
|
||||
+ struct clk *clk;
|
||||
+ int ret;
|
||||
+
|
||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ base = devm_ioremap_resource(&pdev->dev, r);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ clk = devm_clk_get(&pdev->dev, NULL);
|
||||
+ if (IS_ERR(clk)) {
|
||||
+ dev_err(&pdev->dev, "unable to get SYS clock\n");
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ ret = clk_prepare_enable(clk);
|
||||
+ if (ret)
|
||||
+ goto err_clk;
|
||||
+
|
||||
+ master = spi_alloc_master(&pdev->dev, sizeof(*rs));
|
||||
+ if (master == NULL) {
|
||||
+ dev_dbg(&pdev->dev, "master allocation failed\n");
|
||||
+ ret = -ENOMEM;
|
||||
+ goto err_clk;
|
||||
+ }
|
||||
+
|
||||
+ master->dev.of_node = pdev->dev.of_node;
|
||||
+ master->mode_bits = RT2880_SPI_MODE_BITS;
|
||||
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
+ master->min_speed_hz = clk_get_rate(clk) / 128;
|
||||
+ master->max_speed_hz = clk_get_rate(clk) / 2;
|
||||
+ master->flags = SPI_MASTER_HALF_DUPLEX;
|
||||
+ master->setup = rt2880_spi_setup;
|
||||
+ master->prepare_message = rt2880_spi_prepare_message;
|
||||
+ master->set_cs = rt2880_spi_set_cs;
|
||||
+ master->transfer_one = rt2880_spi_transfer_one,
|
||||
+
|
||||
+ dev_set_drvdata(&pdev->dev, master);
|
||||
+
|
||||
+ rs = spi_master_get_devdata(master);
|
||||
+ rs->master = master;
|
||||
+ rs->base = base;
|
||||
+ rs->clk = clk;
|
||||
+
|
||||
+ if (atomic_inc_return(&hw_reset_count) == 1)
|
||||
+ device_reset(&pdev->dev);
|
||||
+
|
||||
+ ret = devm_spi_register_master(&pdev->dev, master);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&pdev->dev, "devm_spi_register_master error.\n");
|
||||
+ goto err_master;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+
|
||||
+err_master:
|
||||
+ spi_master_put(master);
|
||||
+ kfree(master);
|
||||
+err_clk:
|
||||
+ clk_disable_unprepare(clk);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rt2880_spi_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct spi_master *master;
|
||||
+ struct rt2880_spi *rs;
|
||||
+
|
||||
+ master = dev_get_drvdata(&pdev->dev);
|
||||
+ rs = spi_master_get_devdata(master);
|
||||
+
|
||||
+ clk_disable_unprepare(rs->clk);
|
||||
+ atomic_dec(&hw_reset_count);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
+
|
||||
+static const struct of_device_id rt2880_spi_match[] = {
|
||||
+ { .compatible = "ralink,rt2880-spi" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, rt2880_spi_match);
|
||||
+
|
||||
+static struct platform_driver rt2880_spi_driver = {
|
||||
+ .driver = {
|
||||
+ .name = DRIVER_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = rt2880_spi_match,
|
||||
+ },
|
||||
+ .probe = rt2880_spi_probe,
|
||||
+ .remove = rt2880_spi_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(rt2880_spi_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("Ralink SPI driver");
|
||||
+MODULE_AUTHOR("Sergiy <piratfm@gmail.com>");
|
||||
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
|
||||
+MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,521 @@
|
|||
From 87a5fcd57c577cd94b5b080deb98885077c13a42 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:49:07 +0100
|
||||
Subject: [PATCH 43/53] spi: add mt7621 support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/spi/Kconfig | 6 +
|
||||
drivers/spi/Makefile | 1 +
|
||||
drivers/spi/spi-mt7621.c | 480 ++++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 487 insertions(+)
|
||||
create mode 100644 drivers/spi/spi-mt7621.c
|
||||
|
||||
--- a/drivers/spi/Kconfig
|
||||
+++ b/drivers/spi/Kconfig
|
||||
@@ -483,6 +483,12 @@ config SPI_RT2880
|
||||
help
|
||||
This selects a driver for the Ralink RT288x/RT305x SPI Controller.
|
||||
|
||||
+config SPI_MT7621
|
||||
+ tristate "MediaTek MT7621 SPI Controller"
|
||||
+ depends on RALINK
|
||||
+ help
|
||||
+ This selects a driver for the MediaTek MT7621 SPI Controller.
|
||||
+
|
||||
config SPI_S3C24XX
|
||||
tristate "Samsung S3C24XX series SPI"
|
||||
depends on ARCH_S3C24XX
|
||||
--- a/drivers/spi/Makefile
|
||||
+++ b/drivers/spi/Makefile
|
||||
@@ -51,6 +51,7 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mp
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||
obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
|
||||
+obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o
|
||||
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
|
||||
obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
|
||||
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/spi/spi-mt7621.c
|
||||
@@ -0,0 +1,480 @@
|
||||
+/*
|
||||
+ * spi-mt7621.c -- MediaTek MT7621 SPI controller driver
|
||||
+ *
|
||||
+ * Copyright (C) 2011 Sergiy <piratfm@gmail.com>
|
||||
+ * Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2014-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ *
|
||||
+ * Some parts are based on spi-orion.c:
|
||||
+ * Author: Shadi Ammouri <shadi@marvell.com>
|
||||
+ * Copyright (C) 2007-2008 Marvell Ltd.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/spi/spi.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/swab.h>
|
||||
+
|
||||
+#include <ralink_regs.h>
|
||||
+
|
||||
+#define SPI_BPW_MASK(bits) BIT((bits) - 1)
|
||||
+
|
||||
+#define DRIVER_NAME "spi-mt7621"
|
||||
+/* in usec */
|
||||
+#define RALINK_SPI_WAIT_MAX_LOOP 2000
|
||||
+
|
||||
+/* SPISTAT register bit field */
|
||||
+#define SPISTAT_BUSY BIT(0)
|
||||
+
|
||||
+#define MT7621_SPI_TRANS 0x00
|
||||
+#define SPITRANS_BUSY BIT(16)
|
||||
+
|
||||
+#define MT7621_SPI_OPCODE 0x04
|
||||
+#define MT7621_SPI_DATA0 0x08
|
||||
+#define MT7621_SPI_DATA4 0x18
|
||||
+#define SPI_CTL_TX_RX_CNT_MASK 0xff
|
||||
+#define SPI_CTL_START BIT(8)
|
||||
+
|
||||
+#define MT7621_SPI_POLAR 0x38
|
||||
+#define MT7621_SPI_MASTER 0x28
|
||||
+#define MT7621_SPI_MOREBUF 0x2c
|
||||
+#define MT7621_SPI_SPACE 0x3c
|
||||
+
|
||||
+#define MT7621_CPHA BIT(5)
|
||||
+#define MT7621_CPOL BIT(4)
|
||||
+#define MT7621_LSB_FIRST BIT(3)
|
||||
+
|
||||
+#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH)
|
||||
+
|
||||
+struct mt7621_spi;
|
||||
+
|
||||
+struct mt7621_spi {
|
||||
+ struct spi_master *master;
|
||||
+ void __iomem *base;
|
||||
+ unsigned int sys_freq;
|
||||
+ unsigned int speed;
|
||||
+ struct clk *clk;
|
||||
+ spinlock_t lock;
|
||||
+
|
||||
+ struct mt7621_spi_ops *ops;
|
||||
+};
|
||||
+
|
||||
+static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi)
|
||||
+{
|
||||
+ return spi_master_get_devdata(spi->master);
|
||||
+}
|
||||
+
|
||||
+static inline u32 mt7621_spi_read(struct mt7621_spi *rs, u32 reg)
|
||||
+{
|
||||
+ return ioread32(rs->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val)
|
||||
+{
|
||||
+ iowrite32(val, rs->base + reg);
|
||||
+}
|
||||
+
|
||||
+static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex)
|
||||
+{
|
||||
+ u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
|
||||
+
|
||||
+ master |= 7 << 29;
|
||||
+ master |= 1 << 2;
|
||||
+ if (duplex)
|
||||
+ master |= 1 << 10;
|
||||
+ else
|
||||
+ master &= ~(1 << 10);
|
||||
+
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
|
||||
+}
|
||||
+
|
||||
+static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
|
||||
+{
|
||||
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
|
||||
+ int cs = spi->chip_select;
|
||||
+ u32 polar = 0;
|
||||
+
|
||||
+ mt7621_spi_reset(rs, cs);
|
||||
+ if (enable)
|
||||
+ polar = BIT(cs);
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
|
||||
+}
|
||||
+
|
||||
+static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed)
|
||||
+{
|
||||
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
|
||||
+ u32 rate;
|
||||
+ u32 reg;
|
||||
+
|
||||
+ dev_dbg(&spi->dev, "speed:%u\n", speed);
|
||||
+
|
||||
+ rate = DIV_ROUND_UP(rs->sys_freq, speed);
|
||||
+ dev_dbg(&spi->dev, "rate-1:%u\n", rate);
|
||||
+
|
||||
+ if (rate > 4097)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (rate < 2)
|
||||
+ rate = 2;
|
||||
+
|
||||
+ reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
|
||||
+ reg &= ~(0xfff << 16);
|
||||
+ reg |= (rate - 2) << 16;
|
||||
+ rs->speed = speed;
|
||||
+
|
||||
+ reg &= ~MT7621_LSB_FIRST;
|
||||
+ if (spi->mode & SPI_LSB_FIRST)
|
||||
+ reg |= MT7621_LSB_FIRST;
|
||||
+
|
||||
+ reg &= ~(MT7621_CPHA | MT7621_CPOL);
|
||||
+ switch(spi->mode & (SPI_CPOL | SPI_CPHA)) {
|
||||
+ case SPI_MODE_0:
|
||||
+ break;
|
||||
+ case SPI_MODE_1:
|
||||
+ reg |= MT7621_CPHA;
|
||||
+ break;
|
||||
+ case SPI_MODE_2:
|
||||
+ reg |= MT7621_CPOL;
|
||||
+ break;
|
||||
+ case SPI_MODE_3:
|
||||
+ reg |= MT7621_CPOL | MT7621_CPHA;
|
||||
+ break;
|
||||
+ }
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
|
||||
+{
|
||||
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
|
||||
+ u32 status;
|
||||
+
|
||||
+ status = mt7621_spi_read(rs, MT7621_SPI_TRANS);
|
||||
+ if ((status & SPITRANS_BUSY) == 0) {
|
||||
+ return 0;
|
||||
+ }
|
||||
+ cpu_relax();
|
||||
+ udelay(1);
|
||||
+ }
|
||||
+
|
||||
+ return -ETIMEDOUT;
|
||||
+}
|
||||
+
|
||||
+static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
|
||||
+ struct spi_message *m)
|
||||
+{
|
||||
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
|
||||
+ struct spi_device *spi = m->spi;
|
||||
+ unsigned int speed = spi->max_speed_hz;
|
||||
+ struct spi_transfer *t = NULL;
|
||||
+ int status = 0;
|
||||
+ int i, len = 0;
|
||||
+ int rx_len = 0;
|
||||
+ u32 data[9] = { 0 };
|
||||
+ u32 val;
|
||||
+
|
||||
+ mt7621_spi_wait_till_ready(spi);
|
||||
+
|
||||
+ list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
+ const u8 *buf = t->tx_buf;
|
||||
+
|
||||
+ if (t->rx_buf)
|
||||
+ rx_len += t->len;
|
||||
+
|
||||
+ if (!buf)
|
||||
+ continue;
|
||||
+
|
||||
+ if (WARN_ON(len + t->len > 36)) {
|
||||
+ status = -EIO;
|
||||
+ goto msg_done;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < t->len; i++, len++)
|
||||
+ data[len / 4] |= buf[i] << (8 * (len & 3));
|
||||
+ }
|
||||
+
|
||||
+ if (WARN_ON(rx_len > 32)) {
|
||||
+ status = -EIO;
|
||||
+ goto msg_done;
|
||||
+ }
|
||||
+
|
||||
+ if (mt7621_spi_prepare(spi, speed)) {
|
||||
+ status = -EIO;
|
||||
+ goto msg_done;
|
||||
+ }
|
||||
+ data[0] = swab32(data[0]);
|
||||
+ if (len < 4)
|
||||
+ data[0] >>= (4 - len) * 8;
|
||||
+
|
||||
+ for (i = 0; i < len; i += 4)
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_OPCODE + i, data[i / 4]);
|
||||
+
|
||||
+ val = (min_t(int, len, 4) * 8) << 24;
|
||||
+ if (len > 4)
|
||||
+ val |= (len - 4) * 8;
|
||||
+ val |= (rx_len * 8) << 12;
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
|
||||
+
|
||||
+ mt7621_spi_set_cs(spi, 1);
|
||||
+
|
||||
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
|
||||
+ val |= SPI_CTL_START;
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
|
||||
+
|
||||
+ mt7621_spi_wait_till_ready(spi);
|
||||
+
|
||||
+ mt7621_spi_set_cs(spi, 0);
|
||||
+
|
||||
+ for (i = 0; i < rx_len; i += 4)
|
||||
+ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
|
||||
+
|
||||
+ m->actual_length = len + rx_len;
|
||||
+
|
||||
+ len = 0;
|
||||
+ list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
+ u8 *buf = t->rx_buf;
|
||||
+
|
||||
+ if (!buf)
|
||||
+ continue;
|
||||
+
|
||||
+ for (i = 0; i < t->len; i++, len++)
|
||||
+ buf[i] = data[len / 4] >> (8 * (len & 3));
|
||||
+ }
|
||||
+
|
||||
+msg_done:
|
||||
+ m->status = status;
|
||||
+ spi_finalize_current_message(master);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
|
||||
+ struct spi_message *m)
|
||||
+{
|
||||
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
|
||||
+ struct spi_device *spi = m->spi;
|
||||
+ unsigned int speed = spi->max_speed_hz;
|
||||
+ struct spi_transfer *t = NULL;
|
||||
+ int status = 0;
|
||||
+ int i, len = 0;
|
||||
+ int rx_len = 0;
|
||||
+ u32 data[9] = { 0 };
|
||||
+ u32 val = 0;
|
||||
+
|
||||
+ mt7621_spi_wait_till_ready(spi);
|
||||
+
|
||||
+ list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
+ const u8 *buf = t->tx_buf;
|
||||
+
|
||||
+ if (t->rx_buf)
|
||||
+ rx_len += t->len;
|
||||
+
|
||||
+ if (!buf)
|
||||
+ continue;
|
||||
+
|
||||
+ if (WARN_ON(len + t->len > 16)) {
|
||||
+ status = -EIO;
|
||||
+ goto msg_done;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < t->len; i++, len++)
|
||||
+ data[len / 4] |= buf[i] << (8 * (len & 3));
|
||||
+ if (speed > t->speed_hz)
|
||||
+ speed = t->speed_hz;
|
||||
+ }
|
||||
+
|
||||
+ if (WARN_ON(rx_len > 16)) {
|
||||
+ status = -EIO;
|
||||
+ goto msg_done;
|
||||
+ }
|
||||
+
|
||||
+ if (mt7621_spi_prepare(spi, speed)) {
|
||||
+ status = -EIO;
|
||||
+ goto msg_done;
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < len; i += 4)
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, data[i / 4]);
|
||||
+
|
||||
+ val |= len * 8;
|
||||
+ val |= (rx_len * 8) << 12;
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
|
||||
+
|
||||
+ mt7621_spi_set_cs(spi, 1);
|
||||
+
|
||||
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
|
||||
+ val |= SPI_CTL_START;
|
||||
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
|
||||
+
|
||||
+ mt7621_spi_wait_till_ready(spi);
|
||||
+
|
||||
+ mt7621_spi_set_cs(spi, 0);
|
||||
+
|
||||
+ for (i = 0; i < rx_len; i += 4)
|
||||
+ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
|
||||
+
|
||||
+ m->actual_length = rx_len;
|
||||
+
|
||||
+ len = 0;
|
||||
+ list_for_each_entry(t, &m->transfers, transfer_list) {
|
||||
+ u8 *buf = t->rx_buf;
|
||||
+
|
||||
+ if (!buf)
|
||||
+ continue;
|
||||
+
|
||||
+ for (i = 0; i < t->len; i++, len++)
|
||||
+ buf[i] = data[len / 4] >> (8 * (len & 3));
|
||||
+ }
|
||||
+
|
||||
+msg_done:
|
||||
+ m->status = status;
|
||||
+ spi_finalize_current_message(master);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7621_spi_transfer_one_message(struct spi_master *master,
|
||||
+ struct spi_message *m)
|
||||
+{
|
||||
+ struct spi_device *spi = m->spi;
|
||||
+ int cs = spi->chip_select;
|
||||
+
|
||||
+ if (cs)
|
||||
+ return mt7621_spi_transfer_full_duplex(master, m);
|
||||
+ return mt7621_spi_transfer_half_duplex(master, m);
|
||||
+}
|
||||
+
|
||||
+static int mt7621_spi_setup(struct spi_device *spi)
|
||||
+{
|
||||
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
|
||||
+
|
||||
+ if ((spi->max_speed_hz == 0) ||
|
||||
+ (spi->max_speed_hz > (rs->sys_freq / 2)))
|
||||
+ spi->max_speed_hz = (rs->sys_freq / 2);
|
||||
+
|
||||
+ if (spi->max_speed_hz < (rs->sys_freq / 4097)) {
|
||||
+ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
|
||||
+ spi->max_speed_hz);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mt7621_spi_match[] = {
|
||||
+ { .compatible = "ralink,mt7621-spi" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mt7621_spi_match);
|
||||
+
|
||||
+static int mt7621_spi_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ const struct of_device_id *match;
|
||||
+ struct spi_master *master;
|
||||
+ struct mt7621_spi *rs;
|
||||
+ unsigned long flags;
|
||||
+ void __iomem *base;
|
||||
+ struct resource *r;
|
||||
+ int status = 0;
|
||||
+ struct clk *clk;
|
||||
+ struct mt7621_spi_ops *ops;
|
||||
+
|
||||
+ match = of_match_device(mt7621_spi_match, &pdev->dev);
|
||||
+ if (!match)
|
||||
+ return -EINVAL;
|
||||
+ ops = (struct mt7621_spi_ops *)match->data;
|
||||
+
|
||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ base = devm_ioremap_resource(&pdev->dev, r);
|
||||
+ if (IS_ERR(base))
|
||||
+ return PTR_ERR(base);
|
||||
+
|
||||
+ clk = devm_clk_get(&pdev->dev, NULL);
|
||||
+ if (IS_ERR(clk)) {
|
||||
+ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
|
||||
+ status);
|
||||
+ return PTR_ERR(clk);
|
||||
+ }
|
||||
+
|
||||
+ status = clk_prepare_enable(clk);
|
||||
+ if (status)
|
||||
+ return status;
|
||||
+
|
||||
+ master = spi_alloc_master(&pdev->dev, sizeof(*rs));
|
||||
+ if (master == NULL) {
|
||||
+ dev_info(&pdev->dev, "master allocation failed\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ master->mode_bits = RT2880_SPI_MODE_BITS;
|
||||
+
|
||||
+ master->setup = mt7621_spi_setup;
|
||||
+ master->transfer_one_message = mt7621_spi_transfer_one_message;
|
||||
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
+ master->dev.of_node = pdev->dev.of_node;
|
||||
+ master->num_chipselect = 2;
|
||||
+
|
||||
+ dev_set_drvdata(&pdev->dev, master);
|
||||
+
|
||||
+ rs = spi_master_get_devdata(master);
|
||||
+ rs->base = base;
|
||||
+ rs->clk = clk;
|
||||
+ rs->master = master;
|
||||
+ rs->sys_freq = clk_get_rate(rs->clk);
|
||||
+ rs->ops = ops;
|
||||
+ dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
|
||||
+ spin_lock_irqsave(&rs->lock, flags);
|
||||
+
|
||||
+ device_reset(&pdev->dev);
|
||||
+
|
||||
+ mt7621_spi_reset(rs, 0);
|
||||
+
|
||||
+ return spi_register_master(master);
|
||||
+}
|
||||
+
|
||||
+static int mt7621_spi_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct spi_master *master;
|
||||
+ struct mt7621_spi *rs;
|
||||
+
|
||||
+ master = dev_get_drvdata(&pdev->dev);
|
||||
+ rs = spi_master_get_devdata(master);
|
||||
+
|
||||
+ clk_disable(rs->clk);
|
||||
+ spi_unregister_master(master);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
+
|
||||
+static struct platform_driver mt7621_spi_driver = {
|
||||
+ .driver = {
|
||||
+ .name = DRIVER_NAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mt7621_spi_match,
|
||||
+ },
|
||||
+ .probe = mt7621_spi_probe,
|
||||
+ .remove = mt7621_spi_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(mt7621_spi_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("MT7621 SPI driver");
|
||||
+MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>");
|
||||
+MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,398 @@
|
|||
From 723b8beaabf3c3c4b1ce69480141f1e926f3f3b2 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:52:56 +0100
|
||||
Subject: [PATCH 44/53] i2c: MIPS: adds ralink I2C driver
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
.../devicetree/bindings/i2c/i2c-ralink.txt | 27 ++
|
||||
drivers/i2c/busses/Kconfig | 4 +
|
||||
drivers/i2c/busses/Makefile | 1 +
|
||||
drivers/i2c/busses/i2c-ralink.c | 327 ++++++++++++++++++++
|
||||
4 files changed, 359 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/i2c/i2c-ralink.txt
|
||||
create mode 100644 drivers/i2c/busses/i2c-ralink.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt
|
||||
@@ -0,0 +1,27 @@
|
||||
+I2C for Ralink platforms
|
||||
+
|
||||
+Required properties :
|
||||
+- compatible : Must be "link,rt3052-i2c"
|
||||
+- reg: physical base address of the controller and length of memory mapped
|
||||
+ region.
|
||||
+- #address-cells = <1>;
|
||||
+- #size-cells = <0>;
|
||||
+
|
||||
+Optional properties:
|
||||
+- Child nodes conforming to i2c bus binding
|
||||
+
|
||||
+Example :
|
||||
+
|
||||
+palmbus@10000000 {
|
||||
+ i2c@900 {
|
||||
+ compatible = "link,rt3052-i2c";
|
||||
+ reg = <0x900 0x100>;
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ hwmon@4b {
|
||||
+ compatible = "national,lm92";
|
||||
+ reg = <0x4b>;
|
||||
+ };
|
||||
+ };
|
||||
+};
|
||||
--- a/drivers/i2c/busses/Kconfig
|
||||
+++ b/drivers/i2c/busses/Kconfig
|
||||
@@ -806,6 +806,10 @@ config I2C_RK3X
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called i2c-rk3x.
|
||||
|
||||
+config I2C_RALINK
|
||||
+ tristate "Ralink I2C Controller"
|
||||
+ select OF_I2C
|
||||
+
|
||||
config HAVE_S3C2410_I2C
|
||||
bool
|
||||
help
|
||||
--- a/drivers/i2c/busses/Makefile
|
||||
+++ b/drivers/i2c/busses/Makefile
|
||||
@@ -75,6 +75,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
|
||||
obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
|
||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
||||
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
|
||||
+obj-$(CONFIG_I2C_RALINK) += i2c-ralink.o
|
||||
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
||||
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
||||
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/i2c/busses/i2c-ralink.c
|
||||
@@ -0,0 +1,327 @@
|
||||
+/*
|
||||
+ * drivers/i2c/busses/i2c-ralink.c
|
||||
+ *
|
||||
+ * Copyright (C) 2013 Steven Liu <steven_liu@mediatek.com>
|
||||
+ *
|
||||
+ * Improve driver for i2cdetect from i2c-tools to detect i2c devices on the bus.
|
||||
+ * (C) 2014 Sittisak <sittisaks@hotmail.com>
|
||||
+ *
|
||||
+ * This software is licensed under the terms of the GNU General Public
|
||||
+ * License version 2, as published by the Free Software Foundation, and
|
||||
+ * may be copied, distributed, and modified under those terms.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/err.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#define REG_CONFIG_REG 0x00
|
||||
+#define REG_CLKDIV_REG 0x04
|
||||
+#define REG_DEVADDR_REG 0x08
|
||||
+#define REG_ADDR_REG 0x0C
|
||||
+#define REG_DATAOUT_REG 0x10
|
||||
+#define REG_DATAIN_REG 0x14
|
||||
+#define REG_STATUS_REG 0x18
|
||||
+#define REG_STARTXFR_REG 0x1C
|
||||
+#define REG_BYTECNT_REG 0x20
|
||||
+#define REG_SM0CFG2 0x28
|
||||
+#define REG_SM0CTL0 0x40
|
||||
+
|
||||
+#define I2C_STARTERR BIT(4)
|
||||
+#define I2C_ACKERR BIT(3)
|
||||
+#define I2C_DATARDY BIT(2)
|
||||
+#define I2C_SDOEMPTY BIT(1)
|
||||
+#define I2C_BUSY BIT(0)
|
||||
+
|
||||
+#define I2C_DEVADLEN_7 (6 << 2)
|
||||
+#define I2C_ADDRDIS BIT(1)
|
||||
+
|
||||
+#define CLKDIV_VALUE 200 // clock rate is 40M, 40M / (200*2) = 100k (standard i2c bus rate).
|
||||
+//#define CLKDIV_VALUE 50 // clock rate is 40M, 40M / (50*2) = 400k (fast i2c bus rate).
|
||||
+
|
||||
+#define READ_CMD 0x01
|
||||
+#define WRITE_CMD 0x00
|
||||
+#define READ_BLOCK 64
|
||||
+
|
||||
+#define SM0CTL0_OD BIT(31)
|
||||
+#define SM0CTL0_VTRIG BIT(28)
|
||||
+#define SM0CTL0_OUTHI BIT(6)
|
||||
+#define SM0CTL0_STRETCH BIT(1)
|
||||
+#define SM0CTL0_DEFAULT (SM0CTL0_OD | SM0CTL0_VTRIG | SM0CTL0_OUTHI | SM0CTL0_STRETCH)
|
||||
+
|
||||
+/* timeout waiting for I2C devices to respond (clock streching) */
|
||||
+#define RT_I2C_TIMEOUT (msecs_to_jiffies(1000))
|
||||
+
|
||||
+enum {
|
||||
+ I2C_TYPE_RALINK,
|
||||
+ I2C_TYPE_MEDIATEK,
|
||||
+};
|
||||
+
|
||||
+static void __iomem *membase;
|
||||
+static struct i2c_adapter *adapter;
|
||||
+static int hw_type;
|
||||
+
|
||||
+static void rt_i2c_w32(u32 val, unsigned reg)
|
||||
+{
|
||||
+ iowrite32(val, membase + reg);
|
||||
+}
|
||||
+
|
||||
+static u32 rt_i2c_r32(unsigned reg)
|
||||
+{
|
||||
+ return ioread32(membase + reg);
|
||||
+}
|
||||
+
|
||||
+static inline int rt_i2c_get_ack(void)
|
||||
+{
|
||||
+ return (rt_i2c_r32(REG_STATUS_REG) & I2C_ACKERR) ? -EIO : 0;
|
||||
+}
|
||||
+
|
||||
+static inline int rt_i2c_wait_rx_done(void)
|
||||
+{
|
||||
+ unsigned long timeout;
|
||||
+
|
||||
+ timeout = jiffies + RT_I2C_TIMEOUT;
|
||||
+
|
||||
+ do {
|
||||
+ if (time_after(jiffies, timeout))
|
||||
+ return (-ETIMEDOUT);
|
||||
+
|
||||
+ } while (!(rt_i2c_r32(REG_STATUS_REG) & I2C_DATARDY));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int rt_i2c_wait_idle(void)
|
||||
+{
|
||||
+ unsigned long timeout;
|
||||
+
|
||||
+ timeout = jiffies + RT_I2C_TIMEOUT;
|
||||
+
|
||||
+ do {
|
||||
+ if (time_after(jiffies, timeout)) {
|
||||
+ printk("i2c-read line busy\n");
|
||||
+ return 1;
|
||||
+ }
|
||||
+ } while (rt_i2c_r32(REG_STATUS_REG) & I2C_BUSY);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int rt_i2c_wait_tx_done(void)
|
||||
+{
|
||||
+ unsigned long timeout;
|
||||
+
|
||||
+ timeout = jiffies + RT_I2C_TIMEOUT;
|
||||
+
|
||||
+ do {
|
||||
+ if (time_after(jiffies, timeout))
|
||||
+ return (-ETIMEDOUT);
|
||||
+
|
||||
+ } while (!(rt_i2c_r32(REG_STATUS_REG) & I2C_SDOEMPTY));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt_i2c_handle_msg(struct i2c_adapter *a, struct i2c_msg* msg)
|
||||
+{
|
||||
+ int i = 0, j = 0, pos = 0;
|
||||
+ int nblock = msg->len / READ_BLOCK;
|
||||
+ int rem = msg->len % READ_BLOCK;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (msg->flags & I2C_M_TEN) {
|
||||
+ printk("10 bits addr not supported\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (msg->flags & I2C_M_RD) {
|
||||
+ for (i = 0; i < nblock; i++) {
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ return -ETIMEDOUT;
|
||||
+ rt_i2c_w32(READ_BLOCK - 1, REG_BYTECNT_REG);
|
||||
+ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG);
|
||||
+ for (j = 0; j < READ_BLOCK; j++) {
|
||||
+ if (rt_i2c_wait_rx_done() < 0)
|
||||
+ ret = rt_i2c_wait_rx_done();
|
||||
+ if (rt_i2c_get_ack() < 0)
|
||||
+ ret = rt_i2c_get_ack();
|
||||
+ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ return -ETIMEDOUT;
|
||||
+ if (rem) {
|
||||
+ rt_i2c_w32(rem - 1, REG_BYTECNT_REG);
|
||||
+ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG);
|
||||
+ }
|
||||
+ for (i = 0; i < rem; i++) {
|
||||
+ if (rt_i2c_wait_rx_done() < 0)
|
||||
+ ret = rt_i2c_wait_rx_done();
|
||||
+ if (rt_i2c_get_ack() < 0)
|
||||
+ ret = rt_i2c_get_ack();
|
||||
+
|
||||
+ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG);
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ return -ETIMEDOUT;
|
||||
+ rt_i2c_w32(msg->len - 1, REG_BYTECNT_REG);
|
||||
+ for (i = 0; i < msg->len; i++) {
|
||||
+ rt_i2c_w32(msg->buf[i], REG_DATAOUT_REG);
|
||||
+ rt_i2c_w32(WRITE_CMD, REG_STARTXFR_REG);
|
||||
+
|
||||
+ if (rt_i2c_wait_tx_done() < 0)
|
||||
+ ret = rt_i2c_wait_tx_done();
|
||||
+ if (rt_i2c_get_ack() < 0)
|
||||
+ ret = rt_i2c_get_ack();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int rt_i2c_master_xfer(struct i2c_adapter *a, struct i2c_msg *m, int n)
|
||||
+{
|
||||
+ int i = 0;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ return -ETIMEDOUT;
|
||||
+
|
||||
+ device_reset(a->dev.parent);
|
||||
+
|
||||
+ rt_i2c_w32(m->addr, REG_DEVADDR_REG);
|
||||
+ rt_i2c_w32(I2C_DEVADLEN_7 | I2C_ADDRDIS, REG_CONFIG_REG);
|
||||
+ if (hw_type == I2C_TYPE_RALINK) {
|
||||
+ rt_i2c_w32(CLKDIV_VALUE, REG_CLKDIV_REG);
|
||||
+ } else {
|
||||
+ rt_i2c_w32((CLKDIV_VALUE << 16) | SM0CTL0_DEFAULT, REG_SM0CTL0);
|
||||
+ rt_i2c_w32(1, REG_SM0CFG2);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < n && !ret; i++) {
|
||||
+ ret = rt_i2c_handle_msg(a, &m[i]);
|
||||
+
|
||||
+ if (ret < 0) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return n;
|
||||
+}
|
||||
+
|
||||
+static u32 rt_i2c_func(struct i2c_adapter *a)
|
||||
+{
|
||||
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
+}
|
||||
+
|
||||
+static const struct i2c_algorithm rt_i2c_algo = {
|
||||
+ .master_xfer = rt_i2c_master_xfer,
|
||||
+ .functionality = rt_i2c_func,
|
||||
+};
|
||||
+
|
||||
+static const struct of_device_id i2c_rt_dt_ids[] = {
|
||||
+ { .compatible = "ralink,rt2880-i2c", .data = (void *) I2C_TYPE_RALINK },
|
||||
+ { .compatible = "mediatek,mt7628-i2c", .data = (void *) I2C_TYPE_MEDIATEK },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, i2c_rt_dt_ids);
|
||||
+
|
||||
+static int rt_i2c_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ const struct of_device_id *match;
|
||||
+ int ret;
|
||||
+
|
||||
+ match = of_match_device(i2c_rt_dt_ids, &pdev->dev);
|
||||
+ hw_type = (int) match->data;
|
||||
+
|
||||
+ if (!res) {
|
||||
+ dev_err(&pdev->dev, "no memory resource found\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), GFP_KERNEL);
|
||||
+ if (!adapter) {
|
||||
+ dev_err(&pdev->dev, "failed to allocate i2c_adapter\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(membase))
|
||||
+ return PTR_ERR(membase);
|
||||
+
|
||||
+ strlcpy(adapter->name, dev_name(&pdev->dev), sizeof(adapter->name));
|
||||
+ adapter->owner = THIS_MODULE;
|
||||
+ adapter->nr = pdev->id;
|
||||
+ adapter->timeout = HZ;
|
||||
+ adapter->algo = &rt_i2c_algo;
|
||||
+ adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
+ adapter->dev.parent = &pdev->dev;
|
||||
+ adapter->dev.of_node = pdev->dev.of_node;
|
||||
+
|
||||
+ ret = i2c_add_numbered_adapter(adapter);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, adapter);
|
||||
+
|
||||
+ dev_info(&pdev->dev, "loaded\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt_i2c_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver rt_i2c_driver = {
|
||||
+ .probe = rt_i2c_probe,
|
||||
+ .remove = rt_i2c_remove,
|
||||
+ .driver = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .name = "i2c-ralink",
|
||||
+ .of_match_table = i2c_rt_dt_ids,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init i2c_rt_init (void)
|
||||
+{
|
||||
+ return platform_driver_register(&rt_i2c_driver);
|
||||
+}
|
||||
+subsys_initcall(i2c_rt_init);
|
||||
+
|
||||
+static void __exit i2c_rt_exit (void)
|
||||
+{
|
||||
+ platform_driver_unregister(&rt_i2c_driver);
|
||||
+}
|
||||
+
|
||||
+module_exit (i2c_rt_exit);
|
||||
+
|
||||
+MODULE_AUTHOR("Steven Liu <steven_liu@mediatek.com>");
|
||||
+MODULE_DESCRIPTION("Ralink I2c host driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_ALIAS("platform:Ralink-I2C");
|
342
target/linux/ramips/patches-4.4/0045-i2c-add-mt7621-driver.patch
Normal file
342
target/linux/ramips/patches-4.4/0045-i2c-add-mt7621-driver.patch
Normal file
|
@ -0,0 +1,342 @@
|
|||
From d5c54ff3d1db0a4348fa04d8e78f3bf6063e3afc Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:21:27 +0100
|
||||
Subject: [PATCH 45/53] i2c: add mt7621 driver
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/i2c/busses/Kconfig | 4 +
|
||||
drivers/i2c/busses/Makefile | 1 +
|
||||
drivers/i2c/busses/i2c-mt7621.c | 303 +++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 308 insertions(+)
|
||||
create mode 100644 drivers/i2c/busses/i2c-mt7621.c
|
||||
|
||||
--- a/drivers/i2c/busses/Kconfig
|
||||
+++ b/drivers/i2c/busses/Kconfig
|
||||
@@ -810,6 +810,10 @@ config I2C_RALINK
|
||||
tristate "Ralink I2C Controller"
|
||||
select OF_I2C
|
||||
|
||||
+config I2C_MT7621
|
||||
+ tristate "MT7621 I2C Controller"
|
||||
+ select OF_I2C
|
||||
+
|
||||
config HAVE_S3C2410_I2C
|
||||
bool
|
||||
help
|
||||
--- a/drivers/i2c/busses/Makefile
|
||||
+++ b/drivers/i2c/busses/Makefile
|
||||
@@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
|
||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
||||
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
|
||||
obj-$(CONFIG_I2C_RALINK) += i2c-ralink.o
|
||||
+obj-$(CONFIG_I2C_MT7621) += i2c-mt7621.o
|
||||
obj-$(CONFIG_I2C_QUP) += i2c-qup.o
|
||||
obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
|
||||
obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/i2c/busses/i2c-mt7621.c
|
||||
@@ -0,0 +1,303 @@
|
||||
+/*
|
||||
+ * drivers/i2c/busses/i2c-mt7621.c
|
||||
+ *
|
||||
+ * Copyright (C) 2013 Steven Liu <steven_liu@mediatek.com>
|
||||
+ *
|
||||
+ * Improve driver for i2cdetect from i2c-tools to detect i2c devices on the bus.
|
||||
+ * (C) 2014 Sittisak <sittisaks@hotmail.com>
|
||||
+ *
|
||||
+ * This software is licensed under the terms of the GNU General Public
|
||||
+ * License version 2, as published by the Free Software Foundation, and
|
||||
+ * may be copied, distributed, and modified under those terms.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/err.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#define REG_CONFIG_REG 0x00
|
||||
+#define REG_CLKDIV_REG 0x04
|
||||
+#define REG_DEVADDR_REG 0x08
|
||||
+#define REG_ADDR_REG 0x0C
|
||||
+#define REG_DATAOUT_REG 0x10
|
||||
+#define REG_DATAIN_REG 0x14
|
||||
+#define REG_STATUS_REG 0x18
|
||||
+#define REG_STARTXFR_REG 0x1C
|
||||
+#define REG_BYTECNT_REG 0x20
|
||||
+#define REG_SM0_IS_AUTOMODE 0x28
|
||||
+#define REG_SM0CTL0 0x40
|
||||
+
|
||||
+
|
||||
+#define I2C_STARTERR 0x10
|
||||
+#define I2C_ACKERR 0x08
|
||||
+#define I2C_DATARDY 0x04
|
||||
+#define I2C_SDOEMPTY 0x02
|
||||
+#define I2C_BUSY 0x01
|
||||
+
|
||||
+/* I2C_CFG register bit field */
|
||||
+#define I2C_CFG_ADDRLEN_8 (7<<5) /* 8 bits */
|
||||
+#define I2C_CFG_DEVADLEN_7 (6<<2)
|
||||
+#define I2C_CFG_ADDRDIS BIT(1)
|
||||
+#define I2C_CFG_DEVADDIS BIT(0)
|
||||
+
|
||||
+#define I2C_CFG_DEFAULT (I2C_CFG_ADDRLEN_8 | \
|
||||
+ I2C_CFG_DEVADLEN_7 | \
|
||||
+ I2C_CFG_ADDRDIS)
|
||||
+
|
||||
+#define I2C_RETRY 0x1000
|
||||
+
|
||||
+#define CLKDIV_VALUE 333
|
||||
+#define i2c_busy_loop (CLKDIV_VALUE*30)
|
||||
+
|
||||
+#define READ_CMD 0x01
|
||||
+#define WRITE_CMD 0x00
|
||||
+#define READ_BLOCK 16
|
||||
+
|
||||
+#define SM0_ODRAIN BIT(31)
|
||||
+#define SM0_VSYNC_MODE BIT(28)
|
||||
+#define SM0_CLK_DIV (CLKDIV_VALUE << 16)
|
||||
+#define SM0_WAIT_LEVEL BIT(6)
|
||||
+#define SM0_EN BIT(1)
|
||||
+
|
||||
+#define SM0_CFG_DEFUALT (SM0_ODRAIN | SM0_VSYNC_MODE | \
|
||||
+ SM0_CLK_DIV | SM0_WAIT_LEVEL | \
|
||||
+ SM0_EN)
|
||||
+/***********************************************************/
|
||||
+
|
||||
+static void __iomem *membase;
|
||||
+static struct i2c_adapter *adapter;
|
||||
+
|
||||
+static void rt_i2c_w32(u32 val, unsigned reg)
|
||||
+{
|
||||
+ iowrite32(val, membase + reg);
|
||||
+}
|
||||
+
|
||||
+static u32 rt_i2c_r32(unsigned reg)
|
||||
+{
|
||||
+ return ioread32(membase + reg);
|
||||
+}
|
||||
+
|
||||
+static void mt7621_i2c_reset(struct i2c_adapter *a)
|
||||
+{
|
||||
+ device_reset(a->dev.parent);
|
||||
+}
|
||||
+static void mt7621_i2c_enable(struct i2c_msg *msg)
|
||||
+{
|
||||
+ rt_i2c_w32(msg->addr,REG_DEVADDR_REG);
|
||||
+ rt_i2c_w32(0,REG_ADDR_REG);
|
||||
+}
|
||||
+
|
||||
+static void i2c_master_init(struct i2c_adapter *a)
|
||||
+{
|
||||
+ mt7621_i2c_reset(a);
|
||||
+ rt_i2c_w32(I2C_CFG_DEFAULT,REG_CONFIG_REG);
|
||||
+ rt_i2c_w32(SM0_CFG_DEFUALT,REG_SM0CTL0);
|
||||
+ rt_i2c_w32(1,REG_SM0_IS_AUTOMODE);//auto mode
|
||||
+}
|
||||
+
|
||||
+
|
||||
+static inline int rt_i2c_wait_rx_done(void)
|
||||
+{
|
||||
+ int i=0;
|
||||
+ while((!(rt_i2c_r32(REG_STATUS_REG) & I2C_DATARDY)) && (i<i2c_busy_loop))
|
||||
+ i++;
|
||||
+ if(i>=i2c_busy_loop){
|
||||
+ pr_err("err,wait for idle timeout");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int rt_i2c_wait_idle(void)
|
||||
+{
|
||||
+ int i=0;
|
||||
+ while((rt_i2c_r32(REG_STATUS_REG) & I2C_BUSY) && (i<i2c_busy_loop))
|
||||
+ i++;
|
||||
+ if(i>=i2c_busy_loop){
|
||||
+ pr_err("err,wait for idle timeout");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static inline int rt_i2c_wait_tx_done(void)
|
||||
+{
|
||||
+ int i=0;
|
||||
+ while((!(rt_i2c_r32(REG_STATUS_REG) & I2C_SDOEMPTY)) && (i<i2c_busy_loop))
|
||||
+ i++;
|
||||
+ if(i>=i2c_busy_loop){
|
||||
+ pr_err("err,wait for idle timeout");
|
||||
+ return -ETIMEDOUT;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt_i2c_handle_msg(struct i2c_adapter *a, struct i2c_msg* msg)
|
||||
+{
|
||||
+ int i = 0, j = 0, pos = 0;
|
||||
+ int nblock = msg->len / READ_BLOCK;
|
||||
+ int rem = msg->len % READ_BLOCK;
|
||||
+
|
||||
+ if (msg->flags & I2C_M_TEN) {
|
||||
+ printk("10 bits addr not supported\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (msg->flags & I2C_M_RD) {
|
||||
+ for (i = 0; i < nblock; i++) {
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ goto err_timeout;
|
||||
+ rt_i2c_w32(READ_BLOCK - 1, REG_BYTECNT_REG);
|
||||
+ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG);
|
||||
+ for (j = 0; j < READ_BLOCK; j++) {
|
||||
+ if (rt_i2c_wait_rx_done())
|
||||
+ goto err_timeout;
|
||||
+ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ goto err_timeout;
|
||||
+ rt_i2c_w32(rem - 1, REG_BYTECNT_REG);
|
||||
+ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG);
|
||||
+
|
||||
+ for (i = 0; i < rem; i++) {
|
||||
+ if (rt_i2c_wait_rx_done())
|
||||
+ goto err_timeout;
|
||||
+ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG);
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (rt_i2c_wait_idle())
|
||||
+ goto err_timeout;
|
||||
+ rt_i2c_w32(msg->len - 1, REG_BYTECNT_REG);
|
||||
+ for (i = 0; i < msg->len; i++) {
|
||||
+ rt_i2c_w32(msg->buf[i], REG_DATAOUT_REG);
|
||||
+ if(i == 0)
|
||||
+ rt_i2c_w32(WRITE_CMD, REG_STARTXFR_REG);
|
||||
+
|
||||
+ if (rt_i2c_wait_tx_done())
|
||||
+ goto err_timeout;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+err_timeout:
|
||||
+ return -ETIMEDOUT;
|
||||
+}
|
||||
+
|
||||
+static int rt_i2c_master_xfer(struct i2c_adapter *a, struct i2c_msg *m, int n)
|
||||
+{
|
||||
+ int i = 0;
|
||||
+ int ret = 0;
|
||||
+ i2c_master_init(a);
|
||||
+ mt7621_i2c_enable(m);
|
||||
+
|
||||
+ for (i = 0; i != n && ret==0; i++) {
|
||||
+ ret = rt_i2c_handle_msg(a, &m[i]);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+ return i;
|
||||
+}
|
||||
+
|
||||
+static u32 rt_i2c_func(struct i2c_adapter *a)
|
||||
+{
|
||||
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
+}
|
||||
+
|
||||
+static const struct i2c_algorithm rt_i2c_algo = {
|
||||
+ .master_xfer = rt_i2c_master_xfer,
|
||||
+ .functionality = rt_i2c_func,
|
||||
+};
|
||||
+
|
||||
+static int rt_i2c_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ int ret;
|
||||
+
|
||||
+ adapter = devm_kzalloc(&pdev->dev,sizeof(struct i2c_adapter), GFP_KERNEL);
|
||||
+ if (!adapter) {
|
||||
+ dev_err(&pdev->dev, "failed to allocate i2c_adapter\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(membase))
|
||||
+ return PTR_ERR(membase);
|
||||
+
|
||||
+ strlcpy(adapter->name, dev_name(&pdev->dev), sizeof(adapter->name));
|
||||
+
|
||||
+ adapter->owner = THIS_MODULE;
|
||||
+ adapter->nr = pdev->id;
|
||||
+ adapter->timeout = HZ;
|
||||
+ adapter->algo = &rt_i2c_algo;
|
||||
+ adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
+ adapter->dev.parent = &pdev->dev;
|
||||
+ adapter->dev.of_node = pdev->dev.of_node;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, adapter);
|
||||
+
|
||||
+ ret = i2c_add_numbered_adapter(adapter);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ dev_info(&pdev->dev,"loaded");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int rt_i2c_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id i2c_rt_dt_ids[] = {
|
||||
+ { .compatible = "ralink,i2c-mt7621", },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, i2c_rt_dt_ids);
|
||||
+
|
||||
+static struct platform_driver rt_i2c_driver = {
|
||||
+ .probe = rt_i2c_probe,
|
||||
+ .remove = rt_i2c_remove,
|
||||
+ .driver = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .name = "i2c-mt7621",
|
||||
+ .of_match_table = i2c_rt_dt_ids,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static int __init i2c_rt_init (void)
|
||||
+{
|
||||
+ return platform_driver_register(&rt_i2c_driver);
|
||||
+}
|
||||
+
|
||||
+static void __exit i2c_rt_exit (void)
|
||||
+{
|
||||
+ platform_driver_unregister(&rt_i2c_driver);
|
||||
+}
|
||||
+module_init (i2c_rt_init);
|
||||
+module_exit (i2c_rt_exit);
|
||||
+
|
||||
+MODULE_AUTHOR("Steven Liu <steven_liu@mediatek.com>");
|
||||
+MODULE_DESCRIPTION("MT7621 I2c host driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_ALIAS("platform:MT7621-I2C");
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,628 @@
|
|||
From f1c4d9e622c800e1f38b3818f933ec7597d1ccfb Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:29:51 +0100
|
||||
Subject: [PATCH 47/53] DMA: ralink: add rt2880 dma engine
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/dma/Kconfig | 6 +
|
||||
drivers/dma/Makefile | 1 +
|
||||
drivers/dma/ralink-gdma.c | 577 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
include/linux/dmaengine.h | 1 +
|
||||
4 files changed, 585 insertions(+)
|
||||
create mode 100644 drivers/dma/ralink-gdma.c
|
||||
|
||||
--- a/drivers/dma/Kconfig
|
||||
+++ b/drivers/dma/Kconfig
|
||||
@@ -40,6 +40,12 @@ config ASYNC_TX_ENABLE_CHANNEL_SWITCH
|
||||
config ARCH_HAS_ASYNC_TX_FIND_CHANNEL
|
||||
bool
|
||||
|
||||
+config DMA_RALINK
|
||||
+ tristate "RALINK DMA support"
|
||||
+ depends on RALINK && SOC_MT7620
|
||||
+ select DMA_ENGINE
|
||||
+ select DMA_VIRTUAL_CHANNELS
|
||||
+
|
||||
config DMA_ENGINE
|
||||
bool
|
||||
|
||||
--- a/drivers/dma/Makefile
|
||||
+++ b/drivers/dma/Makefile
|
||||
@@ -65,5 +65,6 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-
|
||||
obj-$(CONFIG_TI_EDMA) += edma.o
|
||||
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
|
||||
obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
|
||||
+obj-$(CONFIG_DMA_RALINK) += ralink-gdma.o
|
||||
|
||||
obj-y += xilinx/
|
||||
--- /dev/null
|
||||
+++ b/drivers/dma/ralink-gdma.c
|
||||
@@ -0,0 +1,577 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de>
|
||||
+ * GDMA4740 DMAC support
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License as published by the
|
||||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||
+ * option) any later version.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License along
|
||||
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/dmaengine.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/spinlock.h>
|
||||
+#include <linux/irq.h>
|
||||
+#include <linux/of_dma.h>
|
||||
+
|
||||
+#include "virt-dma.h"
|
||||
+
|
||||
+#define GDMA_NR_CHANS 16
|
||||
+
|
||||
+#define GDMA_REG_SRC_ADDR(x) (0x00 + (x) * 0x10)
|
||||
+#define GDMA_REG_DST_ADDR(x) (0x04 + (x) * 0x10)
|
||||
+
|
||||
+#define GDMA_REG_CTRL0(x) (0x08 + (x) * 0x10)
|
||||
+#define GDMA_REG_CTRL0_TX_MASK 0xffff
|
||||
+#define GDMA_REG_CTRL0_TX_SHIFT 16
|
||||
+#define GDMA_REG_CTRL0_CURR_MASK 0xff
|
||||
+#define GDMA_REG_CTRL0_CURR_SHIFT 8
|
||||
+#define GDMA_REG_CTRL0_SRC_ADDR_FIXED BIT(7)
|
||||
+#define GDMA_REG_CTRL0_DST_ADDR_FIXED BIT(6)
|
||||
+#define GDMA_REG_CTRL0_BURST_MASK 0x7
|
||||
+#define GDMA_REG_CTRL0_BURST_SHIFT 3
|
||||
+#define GDMA_REG_CTRL0_DONE_INT BIT(2)
|
||||
+#define GDMA_REG_CTRL0_ENABLE BIT(1)
|
||||
+#define GDMA_REG_CTRL0_HW_MODE 0
|
||||
+
|
||||
+#define GDMA_REG_CTRL1(x) (0x0c + (x) * 0x10)
|
||||
+#define GDMA_REG_CTRL1_SEG_MASK 0xf
|
||||
+#define GDMA_REG_CTRL1_SEG_SHIFT 22
|
||||
+#define GDMA_REG_CTRL1_REQ_MASK 0x3f
|
||||
+#define GDMA_REG_CTRL1_SRC_REQ_SHIFT 16
|
||||
+#define GDMA_REG_CTRL1_DST_REQ_SHIFT 8
|
||||
+#define GDMA_REG_CTRL1_CONTINOUS BIT(14)
|
||||
+#define GDMA_REG_CTRL1_NEXT_MASK 0x1f
|
||||
+#define GDMA_REG_CTRL1_NEXT_SHIFT 3
|
||||
+#define GDMA_REG_CTRL1_COHERENT BIT(2)
|
||||
+#define GDMA_REG_CTRL1_FAIL BIT(1)
|
||||
+#define GDMA_REG_CTRL1_MASK BIT(0)
|
||||
+
|
||||
+#define GDMA_REG_UNMASK_INT 0x200
|
||||
+#define GDMA_REG_DONE_INT 0x204
|
||||
+
|
||||
+#define GDMA_REG_GCT 0x220
|
||||
+#define GDMA_REG_GCT_CHAN_MASK 0x3
|
||||
+#define GDMA_REG_GCT_CHAN_SHIFT 3
|
||||
+#define GDMA_REG_GCT_VER_MASK 0x3
|
||||
+#define GDMA_REG_GCT_VER_SHIFT 1
|
||||
+#define GDMA_REG_GCT_ARBIT_RR BIT(0)
|
||||
+
|
||||
+enum gdma_dma_transfer_size {
|
||||
+ GDMA_TRANSFER_SIZE_4BYTE = 0,
|
||||
+ GDMA_TRANSFER_SIZE_8BYTE = 1,
|
||||
+ GDMA_TRANSFER_SIZE_16BYTE = 2,
|
||||
+ GDMA_TRANSFER_SIZE_32BYTE = 3,
|
||||
+};
|
||||
+
|
||||
+struct gdma_dma_sg {
|
||||
+ dma_addr_t addr;
|
||||
+ unsigned int len;
|
||||
+};
|
||||
+
|
||||
+struct gdma_dma_desc {
|
||||
+ struct virt_dma_desc vdesc;
|
||||
+
|
||||
+ enum dma_transfer_direction direction;
|
||||
+ bool cyclic;
|
||||
+
|
||||
+ unsigned int num_sgs;
|
||||
+ struct gdma_dma_sg sg[];
|
||||
+};
|
||||
+
|
||||
+struct gdma_dmaengine_chan {
|
||||
+ struct virt_dma_chan vchan;
|
||||
+ unsigned int id;
|
||||
+
|
||||
+ dma_addr_t fifo_addr;
|
||||
+ unsigned int transfer_shift;
|
||||
+
|
||||
+ struct gdma_dma_desc *desc;
|
||||
+ unsigned int next_sg;
|
||||
+};
|
||||
+
|
||||
+struct gdma_dma_dev {
|
||||
+ struct dma_device ddev;
|
||||
+ void __iomem *base;
|
||||
+ struct clk *clk;
|
||||
+
|
||||
+ struct gdma_dmaengine_chan chan[GDMA_NR_CHANS];
|
||||
+};
|
||||
+
|
||||
+static struct gdma_dma_dev *gdma_dma_chan_get_dev(
|
||||
+ struct gdma_dmaengine_chan *chan)
|
||||
+{
|
||||
+ return container_of(chan->vchan.chan.device, struct gdma_dma_dev,
|
||||
+ ddev);
|
||||
+}
|
||||
+
|
||||
+static struct gdma_dmaengine_chan *to_gdma_dma_chan(struct dma_chan *c)
|
||||
+{
|
||||
+ return container_of(c, struct gdma_dmaengine_chan, vchan.chan);
|
||||
+}
|
||||
+
|
||||
+static struct gdma_dma_desc *to_gdma_dma_desc(struct virt_dma_desc *vdesc)
|
||||
+{
|
||||
+ return container_of(vdesc, struct gdma_dma_desc, vdesc);
|
||||
+}
|
||||
+
|
||||
+static inline uint32_t gdma_dma_read(struct gdma_dma_dev *dma_dev,
|
||||
+ unsigned int reg)
|
||||
+{
|
||||
+ return readl(dma_dev->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void gdma_dma_write(struct gdma_dma_dev *dma_dev,
|
||||
+ unsigned reg, uint32_t val)
|
||||
+{
|
||||
+ //printk("gdma --> %p = 0x%08X\n", dma_dev->base + reg, val);
|
||||
+ writel(val, dma_dev->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void gdma_dma_write_mask(struct gdma_dma_dev *dma_dev,
|
||||
+ unsigned int reg, uint32_t val, uint32_t mask)
|
||||
+{
|
||||
+ uint32_t tmp;
|
||||
+
|
||||
+ tmp = gdma_dma_read(dma_dev, reg);
|
||||
+ tmp &= ~mask;
|
||||
+ tmp |= val;
|
||||
+ gdma_dma_write(dma_dev, reg, tmp);
|
||||
+}
|
||||
+
|
||||
+static struct gdma_dma_desc *gdma_dma_alloc_desc(unsigned int num_sgs)
|
||||
+{
|
||||
+ return kzalloc(sizeof(struct gdma_dma_desc) +
|
||||
+ sizeof(struct gdma_dma_sg) * num_sgs, GFP_ATOMIC);
|
||||
+}
|
||||
+
|
||||
+static enum gdma_dma_transfer_size gdma_dma_maxburst(u32 maxburst)
|
||||
+{
|
||||
+ if (maxburst <= 7)
|
||||
+ return GDMA_TRANSFER_SIZE_4BYTE;
|
||||
+ else if (maxburst <= 15)
|
||||
+ return GDMA_TRANSFER_SIZE_8BYTE;
|
||||
+ else if (maxburst <= 31)
|
||||
+ return GDMA_TRANSFER_SIZE_16BYTE;
|
||||
+
|
||||
+ return GDMA_TRANSFER_SIZE_32BYTE;
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_slave_config(struct dma_chan *c,
|
||||
+ const struct dma_slave_config *config)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
|
||||
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
|
||||
+ enum gdma_dma_transfer_size transfer_size;
|
||||
+ uint32_t flags;
|
||||
+ uint32_t ctrl0, ctrl1;
|
||||
+
|
||||
+ switch (config->direction) {
|
||||
+ case DMA_MEM_TO_DEV:
|
||||
+ ctrl1 = 32 << GDMA_REG_CTRL1_SRC_REQ_SHIFT;
|
||||
+ ctrl1 |= config->slave_id << GDMA_REG_CTRL1_DST_REQ_SHIFT;
|
||||
+ flags = GDMA_REG_CTRL0_DST_ADDR_FIXED;
|
||||
+ transfer_size = gdma_dma_maxburst(config->dst_maxburst);
|
||||
+ chan->fifo_addr = config->dst_addr;
|
||||
+ break;
|
||||
+
|
||||
+ case DMA_DEV_TO_MEM:
|
||||
+ ctrl1 = config->slave_id << GDMA_REG_CTRL1_SRC_REQ_SHIFT;
|
||||
+ ctrl1 |= 32 << GDMA_REG_CTRL1_DST_REQ_SHIFT;
|
||||
+ flags = GDMA_REG_CTRL0_SRC_ADDR_FIXED;
|
||||
+ transfer_size = gdma_dma_maxburst(config->src_maxburst);
|
||||
+ chan->fifo_addr = config->src_addr;
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ chan->transfer_shift = 1 + transfer_size;
|
||||
+
|
||||
+ ctrl0 = flags | GDMA_REG_CTRL0_HW_MODE;
|
||||
+ ctrl0 |= GDMA_REG_CTRL0_DONE_INT;
|
||||
+
|
||||
+ ctrl1 &= ~(GDMA_REG_CTRL1_NEXT_MASK << GDMA_REG_CTRL1_NEXT_SHIFT);
|
||||
+ ctrl1 |= chan->id << GDMA_REG_CTRL1_NEXT_SHIFT;
|
||||
+ ctrl1 |= GDMA_REG_CTRL1_FAIL;
|
||||
+ ctrl1 &= ~GDMA_REG_CTRL1_CONTINOUS;
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), ctrl0);
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL1(chan->id), ctrl1);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_terminate_all(struct dma_chan *c)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
|
||||
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
|
||||
+ unsigned long flags;
|
||||
+ LIST_HEAD(head);
|
||||
+
|
||||
+ spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
+ gdma_dma_write_mask(dma_dev, GDMA_REG_CTRL0(chan->id), 0,
|
||||
+ GDMA_REG_CTRL0_ENABLE);
|
||||
+ chan->desc = NULL;
|
||||
+ vchan_get_all_descriptors(&chan->vchan, &head);
|
||||
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
+
|
||||
+ vchan_dma_desc_free_list(&chan->vchan, &head);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
|
||||
+ unsigned long arg)
|
||||
+{
|
||||
+ struct dma_slave_config *config = (struct dma_slave_config *)arg;
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case DMA_SLAVE_CONFIG:
|
||||
+ return gdma_dma_slave_config(chan, config);
|
||||
+ case DMA_TERMINATE_ALL:
|
||||
+ return gdma_dma_terminate_all(chan);
|
||||
+ default:
|
||||
+ return -ENOSYS;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_start_transfer(struct gdma_dmaengine_chan *chan)
|
||||
+{
|
||||
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
|
||||
+ dma_addr_t src_addr, dst_addr;
|
||||
+ struct virt_dma_desc *vdesc;
|
||||
+ struct gdma_dma_sg *sg;
|
||||
+
|
||||
+ gdma_dma_write_mask(dma_dev, GDMA_REG_CTRL0(chan->id), 0,
|
||||
+ GDMA_REG_CTRL0_ENABLE);
|
||||
+
|
||||
+ if (!chan->desc) {
|
||||
+ vdesc = vchan_next_desc(&chan->vchan);
|
||||
+ if (!vdesc)
|
||||
+ return 0;
|
||||
+ chan->desc = to_gdma_dma_desc(vdesc);
|
||||
+ chan->next_sg = 0;
|
||||
+ }
|
||||
+
|
||||
+ if (chan->next_sg == chan->desc->num_sgs)
|
||||
+ chan->next_sg = 0;
|
||||
+
|
||||
+ sg = &chan->desc->sg[chan->next_sg];
|
||||
+
|
||||
+ if (chan->desc->direction == DMA_MEM_TO_DEV) {
|
||||
+ src_addr = sg->addr;
|
||||
+ dst_addr = chan->fifo_addr;
|
||||
+ } else {
|
||||
+ src_addr = chan->fifo_addr;
|
||||
+ dst_addr = sg->addr;
|
||||
+ }
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_SRC_ADDR(chan->id), src_addr);
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_DST_ADDR(chan->id), dst_addr);
|
||||
+ gdma_dma_write_mask(dma_dev, GDMA_REG_CTRL0(chan->id),
|
||||
+ (sg->len << GDMA_REG_CTRL0_TX_SHIFT) | GDMA_REG_CTRL0_ENABLE,
|
||||
+ GDMA_REG_CTRL0_TX_MASK << GDMA_REG_CTRL0_TX_SHIFT);
|
||||
+ chan->next_sg++;
|
||||
+ gdma_dma_write_mask(dma_dev, GDMA_REG_CTRL1(chan->id), 0, GDMA_REG_CTRL1_MASK);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void gdma_dma_chan_irq(struct gdma_dmaengine_chan *chan)
|
||||
+{
|
||||
+ spin_lock(&chan->vchan.lock);
|
||||
+ if (chan->desc) {
|
||||
+ if (chan->desc && chan->desc->cyclic) {
|
||||
+ vchan_cyclic_callback(&chan->desc->vdesc);
|
||||
+ } else {
|
||||
+ if (chan->next_sg == chan->desc->num_sgs) {
|
||||
+ chan->desc = NULL;
|
||||
+ vchan_cookie_complete(&chan->desc->vdesc);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ gdma_dma_start_transfer(chan);
|
||||
+ spin_unlock(&chan->vchan.lock);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t gdma_dma_irq(int irq, void *devid)
|
||||
+{
|
||||
+ struct gdma_dma_dev *dma_dev = devid;
|
||||
+ uint32_t unmask, done;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ unmask = gdma_dma_read(dma_dev, GDMA_REG_UNMASK_INT);
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_UNMASK_INT, unmask);
|
||||
+ done = gdma_dma_read(dma_dev, GDMA_REG_DONE_INT);
|
||||
+
|
||||
+ for (i = 0; i < GDMA_NR_CHANS; ++i)
|
||||
+ if (done & BIT(i))
|
||||
+ gdma_dma_chan_irq(&dma_dev->chan[i]);
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_DONE_INT, done);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static void gdma_dma_issue_pending(struct dma_chan *c)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc)
|
||||
+ gdma_dma_start_transfer(chan);
|
||||
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
+}
|
||||
+
|
||||
+static struct dma_async_tx_descriptor *gdma_dma_prep_slave_sg(
|
||||
+ struct dma_chan *c, struct scatterlist *sgl,
|
||||
+ unsigned int sg_len, enum dma_transfer_direction direction,
|
||||
+ unsigned long flags, void *context)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
|
||||
+ struct gdma_dma_desc *desc;
|
||||
+ struct scatterlist *sg;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ desc = gdma_dma_alloc_desc(sg_len);
|
||||
+ if (!desc)
|
||||
+ return NULL;
|
||||
+
|
||||
+ for_each_sg(sgl, sg, sg_len, i) {
|
||||
+ desc->sg[i].addr = sg_dma_address(sg);
|
||||
+ desc->sg[i].len = sg_dma_len(sg);
|
||||
+ }
|
||||
+
|
||||
+ desc->num_sgs = sg_len;
|
||||
+ desc->direction = direction;
|
||||
+ desc->cyclic = false;
|
||||
+
|
||||
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
|
||||
+}
|
||||
+
|
||||
+static struct dma_async_tx_descriptor *gdma_dma_prep_dma_cyclic(
|
||||
+ struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
|
||||
+ size_t period_len, enum dma_transfer_direction direction,
|
||||
+ unsigned long flags, void *context)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
|
||||
+ struct gdma_dma_desc *desc;
|
||||
+ unsigned int num_periods, i;
|
||||
+
|
||||
+ if (buf_len % period_len)
|
||||
+ return NULL;
|
||||
+
|
||||
+ num_periods = buf_len / period_len;
|
||||
+
|
||||
+ desc = gdma_dma_alloc_desc(num_periods);
|
||||
+ if (!desc)
|
||||
+ return NULL;
|
||||
+
|
||||
+ for (i = 0; i < num_periods; i++) {
|
||||
+ desc->sg[i].addr = buf_addr;
|
||||
+ desc->sg[i].len = period_len;
|
||||
+ buf_addr += period_len;
|
||||
+ }
|
||||
+
|
||||
+ desc->num_sgs = num_periods;
|
||||
+ desc->direction = direction;
|
||||
+ desc->cyclic = true;
|
||||
+
|
||||
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
|
||||
+}
|
||||
+
|
||||
+static size_t gdma_dma_desc_residue(struct gdma_dmaengine_chan *chan,
|
||||
+ struct gdma_dma_desc *desc, unsigned int next_sg)
|
||||
+{
|
||||
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
|
||||
+ unsigned int residue, count;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ residue = 0;
|
||||
+
|
||||
+ for (i = next_sg; i < desc->num_sgs; i++)
|
||||
+ residue += desc->sg[i].len;
|
||||
+
|
||||
+ if (next_sg != 0) {
|
||||
+ count = gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id));
|
||||
+ count >>= GDMA_REG_CTRL0_CURR_SHIFT;
|
||||
+ count &= GDMA_REG_CTRL0_CURR_MASK;
|
||||
+ residue += count << chan->transfer_shift;
|
||||
+ }
|
||||
+
|
||||
+ return residue;
|
||||
+}
|
||||
+
|
||||
+static enum dma_status gdma_dma_tx_status(struct dma_chan *c,
|
||||
+ dma_cookie_t cookie, struct dma_tx_state *state)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
|
||||
+ struct virt_dma_desc *vdesc;
|
||||
+ enum dma_status status;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ status = dma_cookie_status(c, cookie, state);
|
||||
+ if (status == DMA_SUCCESS || !state)
|
||||
+ return status;
|
||||
+
|
||||
+ spin_lock_irqsave(&chan->vchan.lock, flags);
|
||||
+ vdesc = vchan_find_desc(&chan->vchan, cookie);
|
||||
+ if (cookie == chan->desc->vdesc.tx.cookie) {
|
||||
+ state->residue = gdma_dma_desc_residue(chan, chan->desc,
|
||||
+ chan->next_sg);
|
||||
+ } else if (vdesc) {
|
||||
+ state->residue = gdma_dma_desc_residue(chan,
|
||||
+ to_gdma_dma_desc(vdesc), 0);
|
||||
+ } else {
|
||||
+ state->residue = 0;
|
||||
+ }
|
||||
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
|
||||
+
|
||||
+ return status;
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_alloc_chan_resources(struct dma_chan *c)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void gdma_dma_free_chan_resources(struct dma_chan *c)
|
||||
+{
|
||||
+ vchan_free_chan_resources(to_virt_chan(c));
|
||||
+}
|
||||
+
|
||||
+static void gdma_dma_desc_free(struct virt_dma_desc *vdesc)
|
||||
+{
|
||||
+ kfree(container_of(vdesc, struct gdma_dma_desc, vdesc));
|
||||
+}
|
||||
+
|
||||
+static struct dma_chan *
|
||||
+of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec,
|
||||
+ struct of_dma *ofdma)
|
||||
+{
|
||||
+ struct gdma_dma_dev *dma_dev = ofdma->of_dma_data;
|
||||
+ unsigned int request = dma_spec->args[0];
|
||||
+
|
||||
+ if (request >= GDMA_NR_CHANS)
|
||||
+ return NULL;
|
||||
+
|
||||
+ return dma_get_slave_channel(&(dma_dev->chan[request].vchan.chan));
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct gdma_dmaengine_chan *chan;
|
||||
+ struct gdma_dma_dev *dma_dev;
|
||||
+ struct dma_device *dd;
|
||||
+ unsigned int i;
|
||||
+ struct resource *res;
|
||||
+ uint32_t gct;
|
||||
+ int ret;
|
||||
+ int irq;
|
||||
+
|
||||
+
|
||||
+ dma_dev = devm_kzalloc(&pdev->dev, sizeof(*dma_dev), GFP_KERNEL);
|
||||
+ if (!dma_dev)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ dd = &dma_dev->ddev;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ dma_dev->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(dma_dev->base))
|
||||
+ return PTR_ERR(dma_dev->base);
|
||||
+
|
||||
+ dma_cap_set(DMA_SLAVE, dd->cap_mask);
|
||||
+ dma_cap_set(DMA_CYCLIC, dd->cap_mask);
|
||||
+ dd->device_alloc_chan_resources = gdma_dma_alloc_chan_resources;
|
||||
+ dd->device_free_chan_resources = gdma_dma_free_chan_resources;
|
||||
+ dd->device_tx_status = gdma_dma_tx_status;
|
||||
+ dd->device_issue_pending = gdma_dma_issue_pending;
|
||||
+ dd->device_prep_slave_sg = gdma_dma_prep_slave_sg;
|
||||
+ dd->device_prep_dma_cyclic = gdma_dma_prep_dma_cyclic;
|
||||
+ dd->device_control = gdma_dma_control;
|
||||
+ dd->dev = &pdev->dev;
|
||||
+ dd->chancnt = GDMA_NR_CHANS;
|
||||
+ INIT_LIST_HEAD(&dd->channels);
|
||||
+
|
||||
+ for (i = 0; i < dd->chancnt; i++) {
|
||||
+ chan = &dma_dev->chan[i];
|
||||
+ chan->id = i;
|
||||
+ chan->vchan.desc_free = gdma_dma_desc_free;
|
||||
+ vchan_init(&chan->vchan, dd);
|
||||
+ }
|
||||
+
|
||||
+ ret = dma_async_device_register(dd);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = of_dma_controller_register(pdev->dev.of_node,
|
||||
+ of_dma_xlate_by_chan_id, dma_dev);
|
||||
+ if (ret)
|
||||
+ goto err_unregister;
|
||||
+
|
||||
+ irq = platform_get_irq(pdev, 0);
|
||||
+ ret = request_irq(irq, gdma_dma_irq, 0, dev_name(&pdev->dev), dma_dev);
|
||||
+ if (ret)
|
||||
+ goto err_unregister;
|
||||
+
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_UNMASK_INT, 0);
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_DONE_INT, BIT(dd->chancnt) - 1);
|
||||
+
|
||||
+ gct = gdma_dma_read(dma_dev, GDMA_REG_GCT);
|
||||
+ dev_info(&pdev->dev, "revision: %d, channels: %d\n",
|
||||
+ (gct >> GDMA_REG_GCT_VER_SHIFT) & GDMA_REG_GCT_VER_MASK,
|
||||
+ 8 << ((gct >> GDMA_REG_GCT_CHAN_SHIFT) & GDMA_REG_GCT_CHAN_MASK));
|
||||
+ platform_set_drvdata(pdev, dma_dev);
|
||||
+
|
||||
+ gdma_dma_write(dma_dev, GDMA_REG_GCT, GDMA_REG_GCT_ARBIT_RR);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+err_unregister:
|
||||
+ dma_async_device_unregister(dd);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int gdma_dma_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct gdma_dma_dev *dma_dev = platform_get_drvdata(pdev);
|
||||
+ int irq = platform_get_irq(pdev, 0);
|
||||
+
|
||||
+ free_irq(irq, dma_dev);
|
||||
+ of_dma_controller_free(pdev->dev.of_node);
|
||||
+ dma_async_device_unregister(&dma_dev->ddev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id gdma_of_match_table[] = {
|
||||
+ { .compatible = "ralink,rt2880-gdma" },
|
||||
+ { },
|
||||
+};
|
||||
+
|
||||
+static struct platform_driver gdma_dma_driver = {
|
||||
+ .probe = gdma_dma_probe,
|
||||
+ .remove = gdma_dma_remove,
|
||||
+ .driver = {
|
||||
+ .name = "gdma-rt2880",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = gdma_of_match_table,
|
||||
+ },
|
||||
+};
|
||||
+module_platform_driver(gdma_dma_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
+MODULE_DESCRIPTION("GDMA4740 DMA driver");
|
||||
+MODULE_LICENSE("GPLv2");
|
||||
--- a/include/linux/dmaengine.h
|
||||
+++ b/include/linux/dmaengine.h
|
||||
@@ -496,6 +496,7 @@ static inline void dma_set_unmap(struct
|
||||
struct dmaengine_unmap_data *
|
||||
dmaengine_get_unmap_data(struct device *dev, int nr, gfp_t flags);
|
||||
void dmaengine_unmap_put(struct dmaengine_unmap_data *unmap);
|
||||
+struct dma_chan *dma_get_slave_channel(struct dma_chan *chan);
|
||||
#else
|
||||
static inline void dma_set_unmap(struct dma_async_tx_descriptor *tx,
|
||||
struct dmaengine_unmap_data *unmap)
|
|
@ -0,0 +1,765 @@
|
|||
From 7f29222b1731e8182ba94a331531dec18865a1e4 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 27 Jul 2014 09:31:47 +0100
|
||||
Subject: [PATCH 48/53] asoc: add mt7620 support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/ralink/of.c | 2 +
|
||||
sound/soc/Kconfig | 1 +
|
||||
sound/soc/Makefile | 1 +
|
||||
sound/soc/ralink/Kconfig | 15 ++
|
||||
sound/soc/ralink/Makefile | 11 +
|
||||
sound/soc/ralink/mt7620-i2s.c | 436 ++++++++++++++++++++++++++++++++++++++
|
||||
sound/soc/ralink/mt7620-wm8960.c | 233 ++++++++++++++++++++
|
||||
7 files changed, 699 insertions(+)
|
||||
create mode 100644 sound/soc/ralink/Kconfig
|
||||
create mode 100644 sound/soc/ralink/Makefile
|
||||
create mode 100644 sound/soc/ralink/mt7620-i2s.c
|
||||
create mode 100644 sound/soc/ralink/mt7620-wm8960.c
|
||||
|
||||
--- a/arch/mips/ralink/of.c
|
||||
+++ b/arch/mips/ralink/of.c
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bootmem.h>
|
||||
+#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
@@ -26,6 +27,7 @@
|
||||
#include "common.h"
|
||||
|
||||
__iomem void *rt_sysc_membase;
|
||||
+EXPORT_SYMBOL(rt_sysc_membase);
|
||||
__iomem void *rt_memc_membase;
|
||||
|
||||
__iomem void *plat_of_remap_node(const char *node)
|
||||
--- a/sound/soc/Kconfig
|
||||
+++ b/sound/soc/Kconfig
|
||||
@@ -56,6 +56,7 @@ source "sound/soc/mxs/Kconfig"
|
||||
source "sound/soc/pxa/Kconfig"
|
||||
source "sound/soc/qcom/Kconfig"
|
||||
source "sound/soc/rockchip/Kconfig"
|
||||
+source "sound/soc/ralink/Kconfig"
|
||||
source "sound/soc/samsung/Kconfig"
|
||||
source "sound/soc/sh/Kconfig"
|
||||
source "sound/soc/sirf/Kconfig"
|
||||
--- a/sound/soc/Makefile
|
||||
+++ b/sound/soc/Makefile
|
||||
@@ -36,6 +36,7 @@ obj-$(CONFIG_SND_SOC) += kirkwood/
|
||||
obj-$(CONFIG_SND_SOC) += pxa/
|
||||
obj-$(CONFIG_SND_SOC) += qcom/
|
||||
obj-$(CONFIG_SND_SOC) += rockchip/
|
||||
+obj-$(CONFIG_SND_SOC) += ralink/
|
||||
obj-$(CONFIG_SND_SOC) += samsung/
|
||||
obj-$(CONFIG_SND_SOC) += sh/
|
||||
obj-$(CONFIG_SND_SOC) += sirf/
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/ralink/Kconfig
|
||||
@@ -0,0 +1,15 @@
|
||||
+config SND_MT7620_SOC_I2S
|
||||
+ depends on SOC_MT7620 && SND_SOC
|
||||
+ select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
+ tristate "SoC Audio (I2S protocol) for Ralink MT7620 SoC"
|
||||
+ help
|
||||
+ Say Y if you want to use I2S protocol and I2S codec on Ingenic MT7620
|
||||
+ based boards.
|
||||
+
|
||||
+config SND_MT7620_SOC_WM8960
|
||||
+ tristate "SoC Audio support for Ralink WM8960"
|
||||
+ select SND_MT7620_SOC_I2S
|
||||
+ select SND_SOC_WM8960
|
||||
+ help
|
||||
+ Say Y if you want to add support for ASoC audio on the Qi LB60 board
|
||||
+ a.k.a Qi Ben NanoNote.
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/ralink/Makefile
|
||||
@@ -0,0 +1,11 @@
|
||||
+#
|
||||
+# Jz4740 Platform Support
|
||||
+#
|
||||
+snd-soc-mt7620-i2s-objs := mt7620-i2s.o
|
||||
+
|
||||
+obj-$(CONFIG_SND_MT7620_SOC_I2S) += snd-soc-mt7620-i2s.o
|
||||
+
|
||||
+# Jz4740 Machine Support
|
||||
+snd-soc-mt7620-wm8960-objs := mt7620-wm8960.o
|
||||
+
|
||||
+obj-$(CONFIG_SND_MT7620_SOC_WM8960) += snd-soc-mt7620-wm8960.o
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/ralink/mt7620-i2s.c
|
||||
@@ -0,0 +1,436 @@
|
||||
+/*
|
||||
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License as published by the
|
||||
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||
+ * option) any later version.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License along
|
||||
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+#include <linux/delay.h>
|
||||
+
|
||||
+#include <linux/dma-mapping.h>
|
||||
+
|
||||
+#include <sound/core.h>
|
||||
+#include <sound/pcm.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/initval.h>
|
||||
+#include <sound/dmaengine_pcm.h>
|
||||
+
|
||||
+#include <ralink_regs.h>
|
||||
+
|
||||
+#define I2S_REG_CFG0 0x00
|
||||
+#define I2S_REG_CFG0_EN BIT(31)
|
||||
+#define I2S_REG_CFG0_DMA_EN BIT(30)
|
||||
+#define I2S_REG_CFG0_BYTE_SWAP BIT(28)
|
||||
+#define I2S_REG_CFG0_TX_EN BIT(24)
|
||||
+#define I2S_REG_CFG0_RX_EN BIT(20)
|
||||
+#define I2S_REG_CFG0_SLAVE BIT(16)
|
||||
+#define I2S_REG_CFG0_RX_THRES 12
|
||||
+#define I2S_REG_CFG0_TX_THRES 4
|
||||
+#define I2S_REG_CFG0_DFT_THRES (4 << I2S_REG_CFG0_RX_THRES) | \
|
||||
+ (4 << I2S_REG_CFG0_TX_THRES)
|
||||
+
|
||||
+#define I2S_REG_INT_STATUS 0x04
|
||||
+#define I2S_REG_INT_EN 0x08
|
||||
+#define I2S_REG_FF_STATUS 0x0c
|
||||
+#define I2S_REG_WREG 0x10
|
||||
+#define I2S_REG_RREG 0x14
|
||||
+#define I2S_REG_CFG1 0x18
|
||||
+
|
||||
+#define I2S_REG_DIVCMP 0x20
|
||||
+#define I2S_REG_DIVINT 0x24
|
||||
+#define I2S_REG_CLK_EN BIT(31)
|
||||
+
|
||||
+struct mt7620_i2s {
|
||||
+ struct resource *mem;
|
||||
+ void __iomem *base;
|
||||
+ dma_addr_t phys_base;
|
||||
+
|
||||
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
|
||||
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
|
||||
+};
|
||||
+
|
||||
+static inline uint32_t mt7620_i2s_read(const struct mt7620_i2s *i2s,
|
||||
+ unsigned int reg)
|
||||
+{
|
||||
+ return readl(i2s->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void mt7620_i2s_write(const struct mt7620_i2s *i2s,
|
||||
+ unsigned int reg, uint32_t value)
|
||||
+{
|
||||
+ //printk("i2s --> %p = 0x%08X\n", i2s->base + reg, value);
|
||||
+ writel(value, i2s->base + reg);
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_startup(struct snd_pcm_substream *substream,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+ uint32_t cfg;
|
||||
+
|
||||
+ if (dai->active)
|
||||
+ return 0;
|
||||
+
|
||||
+ cfg = mt7620_i2s_read(i2s, I2S_REG_CFG0);
|
||||
+ cfg |= I2S_REG_CFG0_EN;
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_CFG0, cfg);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt7620_i2s_shutdown(struct snd_pcm_substream *substream,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+ uint32_t cfg;
|
||||
+
|
||||
+ if (dai->active)
|
||||
+ return;
|
||||
+
|
||||
+ cfg = mt7620_i2s_read(i2s, I2S_REG_CFG0);
|
||||
+ cfg &= ~I2S_REG_CFG0_EN;
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_CFG0, cfg);
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
+ struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+
|
||||
+ uint32_t cfg;
|
||||
+ uint32_t mask;
|
||||
+
|
||||
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
+ mask = I2S_REG_CFG0_TX_EN;
|
||||
+ else
|
||||
+ mask = I2S_REG_CFG0_RX_EN;
|
||||
+
|
||||
+ cfg = mt7620_i2s_read(i2s, I2S_REG_CFG0);
|
||||
+
|
||||
+ switch (cmd) {
|
||||
+ case SNDRV_PCM_TRIGGER_START:
|
||||
+ case SNDRV_PCM_TRIGGER_RESUME:
|
||||
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
+ cfg |= mask;
|
||||
+ break;
|
||||
+ case SNDRV_PCM_TRIGGER_STOP:
|
||||
+ case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
+ cfg &= ~mask;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (cfg & (I2S_REG_CFG0_TX_EN | I2S_REG_CFG0_RX_EN))
|
||||
+ cfg |= I2S_REG_CFG0_DMA_EN;
|
||||
+ else
|
||||
+ cfg &= ~I2S_REG_CFG0_DMA_EN;
|
||||
+
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_CFG0, cfg);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+ uint32_t cfg;
|
||||
+
|
||||
+ cfg = mt7620_i2s_read(i2s, I2S_REG_CFG0);
|
||||
+
|
||||
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
||||
+ case SND_SOC_DAIFMT_CBS_CFS:
|
||||
+ cfg |= I2S_REG_CFG0_SLAVE;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_CBM_CFM:
|
||||
+ cfg &= ~I2S_REG_CFG0_SLAVE;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_CBM_CFS:
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
||||
+ case SND_SOC_DAIFMT_I2S:
|
||||
+ case SND_SOC_DAIFMT_MSB:
|
||||
+ cfg &= ~I2S_REG_CFG0_BYTE_SWAP;
|
||||
+ break;
|
||||
+ case SND_SOC_DAIFMT_LSB:
|
||||
+ cfg |= I2S_REG_CFG0_BYTE_SWAP;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
||||
+ case SND_SOC_DAIFMT_NB_NF:
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_CFG0, cfg);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
+{
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+unsigned long i2sMaster_inclk_int[11] = {
|
||||
+ 78, 56, 52, 39, 28, 26, 19, 14, 13, 9, 6};
|
||||
+unsigned long i2sMaster_inclk_comp[11] = {
|
||||
+ 64, 352, 42, 32, 176, 21, 272, 88, 10, 455, 261};
|
||||
+
|
||||
+
|
||||
+static int mt7620_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
|
||||
+ unsigned int freq, int dir)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+
|
||||
+ printk("Internal REFCLK with fractional division\n");
|
||||
+
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_DIVINT, i2sMaster_inclk_int[7]);
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_DIVCMP,
|
||||
+ i2sMaster_inclk_comp[7] | I2S_REG_CLK_EN);
|
||||
+
|
||||
+/* struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+ struct clk *parent;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ switch (clk_id) {
|
||||
+ case JZ4740_I2S_CLKSRC_EXT:
|
||||
+ parent = clk_get(NULL, "ext");
|
||||
+ clk_set_parent(i2s->clk_i2s, parent);
|
||||
+ break;
|
||||
+ case JZ4740_I2S_CLKSRC_PLL:
|
||||
+ parent = clk_get(NULL, "pll half");
|
||||
+ clk_set_parent(i2s->clk_i2s, parent);
|
||||
+ ret = clk_set_rate(i2s->clk_i2s, freq);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ clk_put(parent);
|
||||
+
|
||||
+ return ret;*/
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt7620_i2c_init_pcm_config(struct mt7620_i2s *i2s)
|
||||
+{
|
||||
+ struct snd_dmaengine_dai_dma_data *dma_data;
|
||||
+
|
||||
+ /* Playback */
|
||||
+ dma_data = &i2s->playback_dma_data;
|
||||
+ dma_data->maxburst = 16;
|
||||
+ dma_data->slave_id = 2; //JZ4740_DMA_TYPE_AIC_TRANSMIT;
|
||||
+ dma_data->addr = i2s->phys_base + I2S_REG_WREG;
|
||||
+
|
||||
+ /* Capture */
|
||||
+ dma_data = &i2s->capture_dma_data;
|
||||
+ dma_data->maxburst = 16;
|
||||
+ dma_data->slave_id = 3; //JZ4740_DMA_TYPE_AIC_RECEIVE;
|
||||
+ dma_data->addr = i2s->phys_base + I2S_REG_RREG;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_dai_probe(struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
+ uint32_t data;
|
||||
+
|
||||
+ mt7620_i2c_init_pcm_config(i2s);
|
||||
+ dai->playback_dma_data = &i2s->playback_dma_data;
|
||||
+ dai->capture_dma_data = &i2s->capture_dma_data;
|
||||
+
|
||||
+ /* set share pins to i2s/gpio mode and i2c mode */
|
||||
+ data = rt_sysc_r32(0x60);
|
||||
+ data &= 0xFFFFFFE2;
|
||||
+ data |= 0x00000018;
|
||||
+ rt_sysc_w32(data, 0x60);
|
||||
+
|
||||
+ printk("Internal REFCLK with fractional division\n");
|
||||
+
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_CFG0, I2S_REG_CFG0_DFT_THRES);
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_CFG1, 0);
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_INT_EN, 0);
|
||||
+
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_DIVINT, i2sMaster_inclk_int[7]);
|
||||
+ mt7620_i2s_write(i2s, I2S_REG_DIVCMP,
|
||||
+ i2sMaster_inclk_comp[7] | I2S_REG_CLK_EN);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_dai_remove(struct snd_soc_dai *dai)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct snd_soc_dai_ops mt7620_i2s_dai_ops = {
|
||||
+ .startup = mt7620_i2s_startup,
|
||||
+ .shutdown = mt7620_i2s_shutdown,
|
||||
+ .trigger = mt7620_i2s_trigger,
|
||||
+ .hw_params = mt7620_i2s_hw_params,
|
||||
+ .set_fmt = mt7620_i2s_set_fmt,
|
||||
+ .set_sysclk = mt7620_i2s_set_sysclk,
|
||||
+};
|
||||
+
|
||||
+#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
|
||||
+ SNDRV_PCM_FMTBIT_S24_LE)
|
||||
+
|
||||
+static struct snd_soc_dai_driver mt7620_i2s_dai = {
|
||||
+ .probe = mt7620_i2s_dai_probe,
|
||||
+ .remove = mt7620_i2s_dai_remove,
|
||||
+ .playback = {
|
||||
+ .channels_min = 1,
|
||||
+ .channels_max = 2,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_48000,
|
||||
+ .formats = JZ4740_I2S_FMTS,
|
||||
+ },
|
||||
+ .capture = {
|
||||
+ .channels_min = 2,
|
||||
+ .channels_max = 2,
|
||||
+ .rates = SNDRV_PCM_RATE_8000_48000,
|
||||
+ .formats = JZ4740_I2S_FMTS,
|
||||
+ },
|
||||
+ .symmetric_rates = 1,
|
||||
+ .ops = &mt7620_i2s_dai_ops,
|
||||
+};
|
||||
+
|
||||
+static const struct snd_pcm_hardware mt7620_pcm_hardware = {
|
||||
+ .info = SNDRV_PCM_INFO_MMAP |
|
||||
+ SNDRV_PCM_INFO_MMAP_VALID |
|
||||
+ SNDRV_PCM_INFO_INTERLEAVED |
|
||||
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
|
||||
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
|
||||
+ .period_bytes_min = PAGE_SIZE,
|
||||
+ .period_bytes_max = 64 * 1024,
|
||||
+ .periods_min = 2,
|
||||
+ .periods_max = 128,
|
||||
+ .buffer_bytes_max = 128 * 1024,
|
||||
+ .fifo_size = 32,
|
||||
+};
|
||||
+
|
||||
+static const struct snd_dmaengine_pcm_config mt7620_dmaengine_pcm_config = {
|
||||
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
+ .pcm_hardware = &mt7620_pcm_hardware,
|
||||
+ .prealloc_buffer_size = 256 * PAGE_SIZE,
|
||||
+};
|
||||
+
|
||||
+static const struct snd_soc_component_driver mt7620_i2s_component = {
|
||||
+ .name = "mt7620-i2s",
|
||||
+};
|
||||
+
|
||||
+static int mt7620_i2s_dev_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s;
|
||||
+ int ret;
|
||||
+
|
||||
+ snd_dmaengine_pcm_register(&pdev->dev,
|
||||
+ &mt7620_dmaengine_pcm_config,
|
||||
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
|
||||
+
|
||||
+ i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
|
||||
+ if (!i2s)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ if (!i2s->mem) {
|
||||
+ ret = -ENOENT;
|
||||
+ goto err_free;
|
||||
+ }
|
||||
+
|
||||
+ i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
|
||||
+ pdev->name);
|
||||
+ if (!i2s->mem) {
|
||||
+ ret = -EBUSY;
|
||||
+ goto err_free;
|
||||
+ }
|
||||
+
|
||||
+ i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
|
||||
+ if (!i2s->base) {
|
||||
+ ret = -EBUSY;
|
||||
+ goto err_release_mem_region;
|
||||
+ }
|
||||
+
|
||||
+ i2s->phys_base = i2s->mem->start;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, i2s);
|
||||
+ ret = snd_soc_register_component(&pdev->dev, &mt7620_i2s_component,
|
||||
+ &mt7620_i2s_dai, 1);
|
||||
+
|
||||
+ if (!ret) {
|
||||
+ dev_err(&pdev->dev, "loaded\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ dev_err(&pdev->dev, "Failed to register DAI\n");
|
||||
+ iounmap(i2s->base);
|
||||
+
|
||||
+err_release_mem_region:
|
||||
+ release_mem_region(i2s->mem->start, resource_size(i2s->mem));
|
||||
+err_free:
|
||||
+ kfree(i2s);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_i2s_dev_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct mt7620_i2s *i2s = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ snd_soc_unregister_component(&pdev->dev);
|
||||
+
|
||||
+ iounmap(i2s->base);
|
||||
+ release_mem_region(i2s->mem->start, resource_size(i2s->mem));
|
||||
+
|
||||
+ kfree(i2s);
|
||||
+
|
||||
+ snd_dmaengine_pcm_unregister(&pdev->dev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mt7620_i2s_match[] = {
|
||||
+ { .compatible = "ralink,mt7620a-i2s" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mt7620_i2s_match);
|
||||
+
|
||||
+static struct platform_driver mt7620_i2s_driver = {
|
||||
+ .probe = mt7620_i2s_dev_probe,
|
||||
+ .remove = mt7620_i2s_dev_remove,
|
||||
+ .driver = {
|
||||
+ .name = "mt7620-i2s",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mt7620_i2s_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(mt7620_i2s_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
|
||||
+MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_ALIAS("platform:mt7620-i2s");
|
||||
--- /dev/null
|
||||
+++ b/sound/soc/ralink/mt7620-wm8960.c
|
||||
@@ -0,0 +1,233 @@
|
||||
+/*
|
||||
+ * Copyright 2013 Freescale Semiconductor, Inc.
|
||||
+ *
|
||||
+ * Based on mt7620-sgtl5000.c
|
||||
+ * Copyright 2012 Freescale Semiconductor, Inc.
|
||||
+ * Copyright 2012 Linaro Ltd.
|
||||
+ *
|
||||
+ * The code contained herein is licensed under the GNU General Public
|
||||
+ * License. You may obtain a copy of the GNU General Public License
|
||||
+ * Version 2 or later at the following locations:
|
||||
+ *
|
||||
+ * http://www.opensource.org/licenses/gpl-license.html
|
||||
+ * http://www.gnu.org/copyleft/gpl.html
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of_platform.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <sound/soc.h>
|
||||
+#include <sound/pcm_params.h>
|
||||
+#include <sound/soc-dapm.h>
|
||||
+#include <linux/pinctrl/consumer.h>
|
||||
+
|
||||
+#include "../codecs/wm8960.h"
|
||||
+
|
||||
+#define DAI_NAME_SIZE 32
|
||||
+
|
||||
+struct mt7620_wm8960_data {
|
||||
+ struct snd_soc_dai_link dai;
|
||||
+ struct snd_soc_card card;
|
||||
+ char codec_dai_name[DAI_NAME_SIZE];
|
||||
+ char platform_name[DAI_NAME_SIZE];
|
||||
+ unsigned int clk_frequency;
|
||||
+};
|
||||
+
|
||||
+struct mt7620_priv {
|
||||
+ struct platform_device *pdev;
|
||||
+};
|
||||
+static struct mt7620_priv card_priv;
|
||||
+
|
||||
+static const struct snd_soc_dapm_widget mt7620_wm8960_dapm_widgets[] = {
|
||||
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
|
||||
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
+ SND_SOC_DAPM_MIC("AMIC", NULL),
|
||||
+ SND_SOC_DAPM_MIC("DMIC", NULL),
|
||||
+};
|
||||
+
|
||||
+static int sample_rate = 44100;
|
||||
+static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
|
||||
+
|
||||
+static int mt7620_hifi_hw_params(struct snd_pcm_substream *substream,
|
||||
+ struct snd_pcm_hw_params *params)
|
||||
+{
|
||||
+ sample_rate = params_rate(params);
|
||||
+ sample_format = params_format(params);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct snd_soc_ops mt7620_hifi_ops = {
|
||||
+ .hw_params = mt7620_hifi_hw_params,
|
||||
+};
|
||||
+
|
||||
+static int mt7620_wm8960_set_bias_level(struct snd_soc_card *card,
|
||||
+ struct snd_soc_dapm_context *dapm,
|
||||
+ enum snd_soc_bias_level level)
|
||||
+{
|
||||
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
+ struct mt7620_priv *priv = &card_priv;
|
||||
+ struct mt7620_wm8960_data *data = snd_soc_card_get_drvdata(card);
|
||||
+ struct device *dev = &priv->pdev->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (dapm->dev != codec_dai->dev)
|
||||
+ return 0;
|
||||
+
|
||||
+ switch (level) {
|
||||
+ case SND_SOC_BIAS_PREPARE:
|
||||
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case SND_SOC_BIAS_STANDBY:
|
||||
+ if (dapm->bias_level == SND_SOC_BIAS_PREPARE) {
|
||||
+ ret = snd_soc_dai_set_sysclk(codec_dai,
|
||||
+ WM8960_SYSCLK_MCLK, data->clk_frequency,
|
||||
+ SND_SOC_CLOCK_IN);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(dev,
|
||||
+ "failed to switch away from FLL: %d\n",
|
||||
+ ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_wm8960_late_probe(struct snd_soc_card *card)
|
||||
+{
|
||||
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
|
||||
+ struct mt7620_priv *priv = &card_priv;
|
||||
+ struct mt7620_wm8960_data *data = snd_soc_card_get_drvdata(card);
|
||||
+ struct device *dev = &priv->pdev->dev;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8960_SYSCLK_MCLK,
|
||||
+ data->clk_frequency, SND_SOC_CLOCK_IN);
|
||||
+ if (ret < 0)
|
||||
+ dev_err(dev, "failed to set sysclk in %s\n", __func__);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_wm8960_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct device_node *i2s_np, *codec_np;
|
||||
+ struct platform_device *i2s_pdev;
|
||||
+ struct mt7620_priv *priv = &card_priv;
|
||||
+ struct i2c_client *codec_dev;
|
||||
+ struct mt7620_wm8960_data *data;
|
||||
+ int ret;
|
||||
+
|
||||
+ priv->pdev = pdev;
|
||||
+
|
||||
+ i2s_np = of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0);
|
||||
+ codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
|
||||
+ if (!i2s_np || !codec_np) {
|
||||
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ i2s_pdev = of_find_device_by_node(i2s_np);
|
||||
+ if (!i2s_pdev) {
|
||||
+ dev_err(&pdev->dev, "failed to find SSI platform device\n");
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ codec_dev = of_find_i2c_device_by_node(codec_np);
|
||||
+ if (!codec_dev || !codec_dev->dev.driver) {
|
||||
+ dev_err(&pdev->dev, "failed to find codec platform device\n");
|
||||
+ ret = -EINVAL;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
+ if (!data) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ data->clk_frequency = 12000000;
|
||||
+ data->dai.name = "HiFi";
|
||||
+ data->dai.stream_name = "HiFi";
|
||||
+ data->dai.codec_dai_name = "wm8960-hifi";
|
||||
+ data->dai.codec_of_node = codec_np;
|
||||
+ data->dai.cpu_dai_name = dev_name(&i2s_pdev->dev);
|
||||
+ data->dai.platform_of_node = i2s_np;
|
||||
+ data->dai.ops = &mt7620_hifi_ops;
|
||||
+ data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
|
||||
+ SND_SOC_DAIFMT_CBM_CFM;
|
||||
+
|
||||
+ data->card.dev = &pdev->dev;
|
||||
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
|
||||
+ if (ret)
|
||||
+ goto fail;
|
||||
+ ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
|
||||
+ if (ret)
|
||||
+ goto fail;
|
||||
+ data->card.num_links = 1;
|
||||
+ data->card.dai_link = &data->dai;
|
||||
+ data->card.dapm_widgets = mt7620_wm8960_dapm_widgets;
|
||||
+ data->card.num_dapm_widgets = ARRAY_SIZE(mt7620_wm8960_dapm_widgets);
|
||||
+
|
||||
+ data->card.late_probe = mt7620_wm8960_late_probe;
|
||||
+ data->card.set_bias_level = mt7620_wm8960_set_bias_level;
|
||||
+
|
||||
+ platform_set_drvdata(pdev, &data->card);
|
||||
+ snd_soc_card_set_drvdata(&data->card, data);
|
||||
+
|
||||
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
|
||||
+ if (ret) {
|
||||
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ of_node_put(i2s_np);
|
||||
+ of_node_put(codec_np);
|
||||
+
|
||||
+ return 0;
|
||||
+fail:
|
||||
+ if (i2s_np)
|
||||
+ of_node_put(i2s_np);
|
||||
+ if (codec_np)
|
||||
+ of_node_put(codec_np);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_wm8960_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mt7620_wm8960_dt_ids[] = {
|
||||
+ { .compatible = "mediatek,mt7620-audio-wm8960", },
|
||||
+ { /* sentinel */ }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mt7620_wm8960_dt_ids);
|
||||
+
|
||||
+static struct platform_driver mt7620_wm8960_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "mt7620-wm8960",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .pm = &snd_soc_pm_ops,
|
||||
+ .of_match_table = mt7620_wm8960_dt_ids,
|
||||
+ },
|
||||
+ .probe = mt7620_wm8960_probe,
|
||||
+ .remove = mt7620_wm8960_remove,
|
||||
+};
|
||||
+module_platform_driver(mt7620_wm8960_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
+MODULE_DESCRIPTION("Freescale i.MX WM8962 ASoC machine driver");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS("platform:mt7620-wm8962");
|
|
@ -0,0 +1,227 @@
|
|||
From 77fe64de72317c0e090d82056e7a6a073f2972b4 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 16 Mar 2014 05:24:42 +0000
|
||||
Subject: [PATCH 49/53] watchdog: add MT7621 support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/watchdog/Kconfig | 7 ++
|
||||
drivers/watchdog/Makefile | 1 +
|
||||
drivers/watchdog/mt7621_wdt.c | 185 +++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 193 insertions(+)
|
||||
create mode 100644 drivers/watchdog/mt7621_wdt.c
|
||||
|
||||
--- a/drivers/watchdog/Kconfig
|
||||
+++ b/drivers/watchdog/Kconfig
|
||||
@@ -1354,6 +1354,13 @@ config RALINK_WDT
|
||||
help
|
||||
Hardware driver for the Ralink SoC Watchdog Timer.
|
||||
|
||||
+config MT7621_WDT
|
||||
+ tristate "Mediatek SoC watchdog"
|
||||
+ select WATCHDOG_CORE
|
||||
+ depends on SOC_MT7620 || SOC_MT7621
|
||||
+ help
|
||||
+ Hardware driver for the Ralink SoC Watchdog Timer.
|
||||
+
|
||||
# PARISC Architecture
|
||||
|
||||
# POWERPC Architecture
|
||||
--- a/drivers/watchdog/Makefile
|
||||
+++ b/drivers/watchdog/Makefile
|
||||
@@ -69,6 +69,7 @@ obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_w
|
||||
obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
|
||||
obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
|
||||
obj-$(CONFIG_BCM7038_WDT) += bcm7038_wdt.o
|
||||
+obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
|
||||
|
||||
# AVR32 Architecture
|
||||
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/watchdog/mt7621_wdt.c
|
||||
@@ -0,0 +1,185 @@
|
||||
+/*
|
||||
+ * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer
|
||||
+ *
|
||||
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ *
|
||||
+ * This driver was based on: drivers/watchdog/softdog.c
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify it
|
||||
+ * under the terms of the GNU General Public License version 2 as published
|
||||
+ * by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/reset.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/watchdog.h>
|
||||
+#include <linux/miscdevice.h>
|
||||
+#include <linux/moduleparam.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#define SYSC_RSTSTAT 0x38
|
||||
+#define WDT_RST_CAUSE BIT(1)
|
||||
+
|
||||
+#define RALINK_WDT_TIMEOUT 30
|
||||
+
|
||||
+#define TIMER_REG_TMRSTAT 0x00
|
||||
+#define TIMER_REG_TMR1LOAD 0x24
|
||||
+#define TIMER_REG_TMR1CTL 0x20
|
||||
+
|
||||
+#define TMR1CTL_ENABLE BIT(7)
|
||||
+#define TMR1CTL_RESTART BIT(9)
|
||||
+
|
||||
+static void __iomem *mt762x_wdt_base;
|
||||
+
|
||||
+static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
+module_param(nowayout, bool, 0);
|
||||
+MODULE_PARM_DESC(nowayout,
|
||||
+ "Watchdog cannot be stopped once started (default="
|
||||
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
+
|
||||
+static inline void rt_wdt_w32(unsigned reg, u32 val)
|
||||
+{
|
||||
+ iowrite32(val, mt762x_wdt_base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline u32 rt_wdt_r32(unsigned reg)
|
||||
+{
|
||||
+ return ioread32(mt762x_wdt_base + reg);
|
||||
+}
|
||||
+
|
||||
+static int mt762x_wdt_ping(struct watchdog_device *w)
|
||||
+{
|
||||
+ rt_wdt_w32(TIMER_REG_TMRSTAT, TMR1CTL_RESTART);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt762x_wdt_set_timeout(struct watchdog_device *w, unsigned int t)
|
||||
+{
|
||||
+ w->timeout = t;
|
||||
+ rt_wdt_w32(TIMER_REG_TMR1LOAD, t * 1000);
|
||||
+ mt762x_wdt_ping(w);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt762x_wdt_start(struct watchdog_device *w)
|
||||
+{
|
||||
+ u32 t;
|
||||
+
|
||||
+ rt_wdt_w32(TIMER_REG_TMR1CTL, 1000 << 16);
|
||||
+ mt762x_wdt_set_timeout(w, w->timeout);
|
||||
+
|
||||
+ t = rt_wdt_r32(TIMER_REG_TMR1CTL);
|
||||
+ t |= TMR1CTL_ENABLE;
|
||||
+ rt_wdt_w32(TIMER_REG_TMR1CTL, t);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt762x_wdt_stop(struct watchdog_device *w)
|
||||
+{
|
||||
+ u32 t;
|
||||
+
|
||||
+ mt762x_wdt_ping(w);
|
||||
+
|
||||
+ t = rt_wdt_r32(TIMER_REG_TMR1CTL);
|
||||
+ t &= ~TMR1CTL_ENABLE;
|
||||
+ rt_wdt_w32(TIMER_REG_TMR1CTL, t);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt762x_wdt_bootcause(void)
|
||||
+{
|
||||
+ if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE)
|
||||
+ return WDIOF_CARDRESET;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct watchdog_info mt762x_wdt_info = {
|
||||
+ .identity = "Mediatek Watchdog",
|
||||
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
+};
|
||||
+
|
||||
+static struct watchdog_ops mt762x_wdt_ops = {
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .start = mt762x_wdt_start,
|
||||
+ .stop = mt762x_wdt_stop,
|
||||
+ .ping = mt762x_wdt_ping,
|
||||
+ .set_timeout = mt762x_wdt_set_timeout,
|
||||
+};
|
||||
+
|
||||
+static struct watchdog_device mt762x_wdt_dev = {
|
||||
+ .info = &mt762x_wdt_info,
|
||||
+ .ops = &mt762x_wdt_ops,
|
||||
+ .min_timeout = 1,
|
||||
+};
|
||||
+
|
||||
+static int mt762x_wdt_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res;
|
||||
+ int ret;
|
||||
+
|
||||
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ mt762x_wdt_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(mt762x_wdt_base))
|
||||
+ return PTR_ERR(mt762x_wdt_base);
|
||||
+
|
||||
+ device_reset(&pdev->dev);
|
||||
+
|
||||
+ mt762x_wdt_dev.dev = &pdev->dev;
|
||||
+ mt762x_wdt_dev.bootstatus = mt762x_wdt_bootcause();
|
||||
+ mt762x_wdt_dev.max_timeout = (0xfffful / 1000);
|
||||
+ mt762x_wdt_dev.timeout = mt762x_wdt_dev.max_timeout;
|
||||
+
|
||||
+ watchdog_set_nowayout(&mt762x_wdt_dev, nowayout);
|
||||
+
|
||||
+ ret = watchdog_register_device(&mt762x_wdt_dev);
|
||||
+ if (!ret)
|
||||
+ dev_info(&pdev->dev, "Initialized\n");
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt762x_wdt_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ watchdog_unregister_device(&mt762x_wdt_dev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt762x_wdt_shutdown(struct platform_device *pdev)
|
||||
+{
|
||||
+ mt762x_wdt_stop(&mt762x_wdt_dev);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mt762x_wdt_match[] = {
|
||||
+ { .compatible = "mtk,mt7621-wdt" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mt762x_wdt_match);
|
||||
+
|
||||
+static struct platform_driver mt762x_wdt_driver = {
|
||||
+ .probe = mt762x_wdt_probe,
|
||||
+ .remove = mt762x_wdt_remove,
|
||||
+ .shutdown = mt762x_wdt_shutdown,
|
||||
+ .driver = {
|
||||
+ .name = KBUILD_MODNAME,
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mt762x_wdt_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(mt762x_wdt_driver);
|
||||
+
|
||||
+MODULE_DESCRIPTION("MediaTek MT762x hardware watchdog driver");
|
||||
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org");
|
||||
+MODULE_LICENSE("GPL v2");
|
||||
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
|
@ -0,0 +1,22 @@
|
|||
From a7eb46e0ea4a11e4dfb56ab129bf816d1059a6c5 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:31:08 +0100
|
||||
Subject: [PATCH 51/53] serial: add ugly custom baud rate hack
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/tty/serial/serial_core.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/tty/serial/serial_core.c
|
||||
+++ b/drivers/tty/serial/serial_core.c
|
||||
@@ -359,6 +359,9 @@ uart_get_baud_rate(struct uart_port *por
|
||||
break;
|
||||
}
|
||||
|
||||
+ if (tty_termios_baud_rate(termios) == 2500000)
|
||||
+ return 250000;
|
||||
+
|
||||
for (try = 0; try < 2; try++) {
|
||||
baud = tty_termios_baud_rate(termios);
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
From fc8f96309c21c1bc3276427309cd7d361347d66e Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 7 Dec 2015 17:16:50 +0100
|
||||
Subject: [PATCH 52/53] pwm: add mediatek support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/pwm/Kconfig | 9 +++
|
||||
drivers/pwm/Makefile | 1 +
|
||||
drivers/pwm/pwm-mediatek.c | 173 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 183 insertions(+)
|
||||
create mode 100644 drivers/pwm/pwm-mediatek.c
|
||||
|
||||
--- a/drivers/pwm/Kconfig
|
||||
+++ b/drivers/pwm/Kconfig
|
||||
@@ -260,6 +260,15 @@ config PWM_MTK_DISP
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-mtk-disp.
|
||||
|
||||
+config PWM_MEDIATEK
|
||||
+ tristate "Mediatek PWM support"
|
||||
+ depends on RALINK && OF
|
||||
+ help
|
||||
+ Generic PWM framework driver for Mediatek ARM SoC.
|
||||
+
|
||||
+ To compile this driver as a module, choose M here: the module
|
||||
+ will be called pwm-mxs.
|
||||
+
|
||||
config PWM_MXS
|
||||
tristate "Freescale MXS PWM support"
|
||||
depends on ARCH_MXS && OF
|
||||
--- a/drivers/pwm/Makefile
|
||||
+++ b/drivers/pwm/Makefile
|
||||
@@ -22,6 +22,7 @@ obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx
|
||||
obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o
|
||||
obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o
|
||||
obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o
|
||||
+obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o
|
||||
obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o
|
||||
obj-$(CONFIG_PWM_MXS) += pwm-mxs.o
|
||||
obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/pwm/pwm-mediatek.c
|
||||
@@ -0,0 +1,173 @@
|
||||
+/*
|
||||
+ * Mediatek Pulse Width Modulator driver
|
||||
+ *
|
||||
+ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
+ *
|
||||
+ * 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 <linux/err.h>
|
||||
+#include <linux/io.h>
|
||||
+#include <linux/ioport.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/pwm.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+#define NUM_PWM 4
|
||||
+
|
||||
+/* PWM registers and bits definitions */
|
||||
+#define PWMCON 0x00
|
||||
+#define PWMHDUR 0x04
|
||||
+#define PWMLDUR 0x08
|
||||
+#define PWMGDUR 0x0c
|
||||
+#define PWMWAVENUM 0x28
|
||||
+#define PWMDWIDTH 0x2c
|
||||
+#define PWMTHRES 0x30
|
||||
+
|
||||
+/**
|
||||
+ * struct mtk_pwm_chip - struct representing pwm chip
|
||||
+ *
|
||||
+ * @mmio_base: base address of pwm chip
|
||||
+ * @chip: linux pwm chip representation
|
||||
+ */
|
||||
+struct mtk_pwm_chip {
|
||||
+ void __iomem *mmio_base;
|
||||
+ struct pwm_chip chip;
|
||||
+};
|
||||
+
|
||||
+static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip)
|
||||
+{
|
||||
+ return container_of(chip, struct mtk_pwm_chip, chip);
|
||||
+}
|
||||
+
|
||||
+static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num,
|
||||
+ unsigned long offset)
|
||||
+{
|
||||
+ return ioread32(chip->mmio_base + 0x10 + (num * 0x40) + offset);
|
||||
+}
|
||||
+
|
||||
+static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip,
|
||||
+ unsigned int num, unsigned long offset,
|
||||
+ unsigned long val)
|
||||
+{
|
||||
+ iowrite32(val, chip->mmio_base + 0x10 + (num * 0x40) + offset);
|
||||
+}
|
||||
+
|
||||
+static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
+ int duty_ns, int period_ns)
|
||||
+{
|
||||
+ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
|
||||
+ u32 resolution = 100 / 4;
|
||||
+ u32 clkdiv = 0;
|
||||
+
|
||||
+ while (period_ns / resolution > 8191) {
|
||||
+ clkdiv++;
|
||||
+ resolution *= 2;
|
||||
+ }
|
||||
+
|
||||
+ if (clkdiv > 7)
|
||||
+ return -1;
|
||||
+
|
||||
+ mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv);
|
||||
+ mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution);
|
||||
+ mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
+{
|
||||
+ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = ioread32(pc->mmio_base);
|
||||
+ val |= BIT(pwm->hwpwm);
|
||||
+ iowrite32(val, pc->mmio_base);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
+{
|
||||
+ struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
|
||||
+ u32 val;
|
||||
+
|
||||
+ val = ioread32(pc->mmio_base);
|
||||
+ val &= ~BIT(pwm->hwpwm);
|
||||
+ iowrite32(val, pc->mmio_base);
|
||||
+}
|
||||
+
|
||||
+static const struct pwm_ops mtk_pwm_ops = {
|
||||
+ .config = mtk_pwm_config,
|
||||
+ .enable = mtk_pwm_enable,
|
||||
+ .disable = mtk_pwm_disable,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static int mtk_pwm_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct mtk_pwm_chip *pc;
|
||||
+ struct resource *r;
|
||||
+ int ret;
|
||||
+
|
||||
+ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
|
||||
+ if (!pc)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ pc->mmio_base = devm_ioremap_resource(&pdev->dev, r);
|
||||
+ if (IS_ERR(pc->mmio_base))
|
||||
+ return PTR_ERR(pc->mmio_base);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, pc);
|
||||
+
|
||||
+ pc->chip.dev = &pdev->dev;
|
||||
+ pc->chip.ops = &mtk_pwm_ops;
|
||||
+ pc->chip.base = -1;
|
||||
+ pc->chip.npwm = NUM_PWM;
|
||||
+
|
||||
+ ret = pwmchip_add(&pc->chip);
|
||||
+ if (ret < 0)
|
||||
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int mtk_pwm_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct mtk_pwm_chip *pc = platform_get_drvdata(pdev);
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < NUM_PWM; i++)
|
||||
+ pwm_disable(&pc->chip.pwms[i]);
|
||||
+
|
||||
+ return pwmchip_remove(&pc->chip);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mtk_pwm_of_match[] = {
|
||||
+ { .compatible = "mediatek,mt7628-pwm" },
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, mtk_pwm_of_match);
|
||||
+
|
||||
+static struct platform_driver mtk_pwm_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "mtk-pwm",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mtk_pwm_of_match,
|
||||
+ },
|
||||
+ .probe = mtk_pwm_probe,
|
||||
+ .remove = mtk_pwm_remove,
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(mtk_pwm_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
|
||||
+MODULE_ALIAS("platform:mtk-pwm");
|
|
@ -0,0 +1,123 @@
|
|||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -1011,6 +1011,66 @@ write_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int spi_nor_chunked_write(struct mtd_info *mtd, loff_t _to, size_t _len,
|
||||
+ size_t *_retlen, const u_char *_buf)
|
||||
+{
|
||||
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
+ int chunk_size;
|
||||
+ int retlen = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ chunk_size = nor->chunk_size;
|
||||
+ if (!chunk_size)
|
||||
+ chunk_size = _len;
|
||||
+
|
||||
+ if (nor->addr_width > 3)
|
||||
+ chunk_size -= nor->addr_width - 3;
|
||||
+
|
||||
+ while (retlen < _len) {
|
||||
+ size_t len = min_t(int, chunk_size, _len - retlen);
|
||||
+ const u_char *buf = _buf + retlen;
|
||||
+ loff_t to = _to + retlen;
|
||||
+
|
||||
+ if (nor->flags & SNOR_F_SST)
|
||||
+ ret = sst_write(mtd, to, len, &retlen, buf);
|
||||
+ else
|
||||
+ ret = spi_nor_write(mtd, to, len, &retlen, buf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ *_retlen += retlen;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int spi_nor_chunked_read(struct mtd_info *mtd, loff_t _from, size_t _len,
|
||||
+ size_t *_retlen, u_char *_buf)
|
||||
+{
|
||||
+ struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
+ int chunk_size;
|
||||
+ int ret;
|
||||
+
|
||||
+ chunk_size = nor->chunk_size;
|
||||
+ if (!chunk_size)
|
||||
+ chunk_size = _len;
|
||||
+
|
||||
+ *_retlen = 0;
|
||||
+ while (*_retlen < _len) {
|
||||
+ size_t len = min_t(int, chunk_size, _len - *_retlen);
|
||||
+ u_char *buf = _buf + *_retlen;
|
||||
+ loff_t from = _from + *_retlen;
|
||||
+ int retlen = 0;
|
||||
+
|
||||
+ ret = spi_nor_read(mtd, from, len, &retlen, buf);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ *_retlen += retlen;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int macronix_quad_enable(struct spi_nor *nor)
|
||||
{
|
||||
int ret, val;
|
||||
@@ -1232,10 +1292,12 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
}
|
||||
|
||||
/* sst nor chips use AAI word program */
|
||||
- if (info->flags & SST_WRITE)
|
||||
+ if (info->flags & SST_WRITE) {
|
||||
mtd->_write = sst_write;
|
||||
- else
|
||||
+ nor->flags |= SNOR_F_SST;
|
||||
+ } else {
|
||||
mtd->_write = spi_nor_write;
|
||||
+ }
|
||||
|
||||
if (info->flags & USE_FSR)
|
||||
nor->flags |= SNOR_F_USE_FSR;
|
||||
@@ -1263,11 +1325,20 @@ int spi_nor_scan(struct spi_nor *nor, co
|
||||
mtd->writebufsize = nor->page_size;
|
||||
|
||||
if (np) {
|
||||
+ u32 val;
|
||||
+
|
||||
/* If we were instantiated by DT, use it */
|
||||
if (of_property_read_bool(np, "m25p,fast-read"))
|
||||
nor->flash_read = SPI_NOR_FAST;
|
||||
else
|
||||
nor->flash_read = SPI_NOR_NORMAL;
|
||||
+
|
||||
+ if (!of_property_read_u32(np, "m25p,chunked-io", &val)) {
|
||||
+ dev_info(dev, "using chunked io (size=%d)\n", val);
|
||||
+ mtd->_read = spi_nor_chunked_read;
|
||||
+ mtd->_write = spi_nor_chunked_write;
|
||||
+ nor->chunk_size = val;
|
||||
+ }
|
||||
} else {
|
||||
/* If we weren't instantiated by DT, default to fast-read */
|
||||
nor->flash_read = SPI_NOR_FAST;
|
||||
--- a/include/linux/mtd/spi-nor.h
|
||||
+++ b/include/linux/mtd/spi-nor.h
|
||||
@@ -116,6 +116,7 @@ enum spi_nor_ops {
|
||||
|
||||
enum spi_nor_option_flags {
|
||||
SNOR_F_USE_FSR = BIT(0),
|
||||
+ SNOR_F_SST = BIT(1),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -156,6 +157,7 @@ struct spi_nor {
|
||||
struct device *dev;
|
||||
struct device_node *flash_node;
|
||||
u32 page_size;
|
||||
+ u16 chunk_size;
|
||||
u8 addr_width;
|
||||
u8 erase_opcode;
|
||||
u8 read_opcode;
|
|
@ -0,0 +1,13 @@
|
|||
--- a/arch/mips/include/asm/mips-cm.h
|
||||
+++ b/arch/mips/include/asm/mips-cm.h
|
||||
@@ -286,8 +286,8 @@ BUILD_CM_Cx_R_(tcid_8_priority, 0x80)
|
||||
#define CM_GCR_GIC_BASE_GICEN_MSK (_ULCAST_(0x1) << 0)
|
||||
|
||||
/* GCR_CPC_BASE register fields */
|
||||
-#define CM_GCR_CPC_BASE_CPCBASE_SHF 17
|
||||
-#define CM_GCR_CPC_BASE_CPCBASE_MSK (_ULCAST_(0x7fff) << 17)
|
||||
+#define CM_GCR_CPC_BASE_CPCBASE_SHF 15
|
||||
+#define CM_GCR_CPC_BASE_CPCBASE_MSK (_ULCAST_(0x1ffff) << 15)
|
||||
#define CM_GCR_CPC_BASE_CPCEN_SHF 0
|
||||
#define CM_GCR_CPC_BASE_CPCEN_MSK (_ULCAST_(0x1) << 0)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
--- a/arch/mips/include/asm/mips-cm.h
|
||||
+++ b/arch/mips/include/asm/mips-cm.h
|
||||
@@ -238,8 +238,7 @@ BUILD_CM_Cx_R_(tcid_8_priority, 0x80)
|
||||
#define CM_GCR_BASE_GCRBASE_MSK (_ULCAST_(0x1ffff) << 15)
|
||||
#define CM_GCR_BASE_CMDEFTGT_SHF 0
|
||||
#define CM_GCR_BASE_CMDEFTGT_MSK (_ULCAST_(0x3) << 0)
|
||||
-#define CM_GCR_BASE_CMDEFTGT_DISABLED 0
|
||||
-#define CM_GCR_BASE_CMDEFTGT_MEM 1
|
||||
+#define CM_GCR_BASE_CMDEFTGT_MEM 0
|
||||
#define CM_GCR_BASE_CMDEFTGT_IOCU0 2
|
||||
#define CM_GCR_BASE_CMDEFTGT_IOCU1 3
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
--- a/arch/mips/ralink/clk.c
|
||||
+++ b/arch/mips/ralink/clk.c
|
||||
@@ -62,6 +62,12 @@ int clk_set_rate(struct clk *clk, unsign
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_set_rate);
|
||||
|
||||
+long clk_round_rate(struct clk *clk, unsigned long rate)
|
||||
+{
|
||||
+ return -1;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(clk_round_rate);
|
||||
+
|
||||
void __init plat_time_init(void)
|
||||
{
|
||||
struct clk *clk;
|
|
@ -0,0 +1,10 @@
|
|||
--- a/arch/mips/ralink/Kconfig
|
||||
+++ b/arch/mips/ralink/Kconfig
|
||||
@@ -51,6 +51,7 @@ choice
|
||||
select SYS_SUPPORTS_MULTITHREADING
|
||||
select SYS_SUPPORTS_SMP
|
||||
select SYS_SUPPORTS_MIPS_CPS
|
||||
+ select SYS_SUPPORTS_HIGHMEM
|
||||
select MIPS_GIC
|
||||
select COMMON_CLK
|
||||
select CLKSRC_MIPS_GIC
|
|
@ -0,0 +1,14 @@
|
|||
--- a/drivers/usb/host/Kconfig
|
||||
+++ b/drivers/usb/host/Kconfig
|
||||
@@ -42,9 +42,9 @@ config USB_XHCI_PLATFORM
|
||||
If unsure, say N.
|
||||
|
||||
config USB_XHCI_MTK
|
||||
- tristate "xHCI support for Mediatek MT65xx"
|
||||
+ tristate "xHCI support for Mediatek MT65xx/MT7621"
|
||||
select MFD_SYSCON
|
||||
- depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
+ depends on SOC_MT7621 || ARCH_MEDIATEK || COMPILE_TEST
|
||||
---help---
|
||||
Say 'Y' to enable the support for the xHCI host controller
|
||||
found in Mediatek MT65xx SoCs.
|
30
target/linux/ramips/patches-4.4/0068-fix-ralink-prom.c
Normal file
30
target/linux/ramips/patches-4.4/0068-fix-ralink-prom.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
--- a/arch/mips/ralink/prom.c
|
||||
+++ b/arch/mips/ralink/prom.c
|
||||
@@ -30,8 +30,10 @@ const char *get_system_type(void)
|
||||
return soc_info.sys_type;
|
||||
}
|
||||
|
||||
-static __init void prom_init_cmdline(int argc, char **argv)
|
||||
+static __init void prom_init_cmdline(void)
|
||||
{
|
||||
+ int argc;
|
||||
+ char **argv;
|
||||
int i;
|
||||
|
||||
pr_debug("prom: fw_arg0=%08x fw_arg1=%08x fw_arg2=%08x fw_arg3=%08x\n",
|
||||
@@ -60,14 +62,11 @@ static __init void prom_init_cmdline(int
|
||||
|
||||
void __init prom_init(void)
|
||||
{
|
||||
- int argc;
|
||||
- char **argv;
|
||||
-
|
||||
prom_soc_init(&soc_info);
|
||||
|
||||
pr_info("SoC Type: %s\n", get_system_type());
|
||||
|
||||
- prom_init_cmdline(argc, argv);
|
||||
+ prom_init_cmdline();
|
||||
}
|
||||
|
||||
void __init prom_free_prom_memory(void)
|
44
target/linux/ramips/patches-4.4/0103-MIPS-OWRTDTB.patch
Normal file
44
target/linux/ramips/patches-4.4/0103-MIPS-OWRTDTB.patch
Normal file
|
@ -0,0 +1,44 @@
|
|||
From c174d2250e402399ad7dbdd57d51883d8804bba0 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 15 Jul 2013 00:40:37 +0200
|
||||
Subject: [PATCH 31/33] owrt: MIPS: add OWRTDTB secion
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
arch/mips/kernel/head.S | 3 +++
|
||||
arch/mips/ralink/Makefile | 2 +-
|
||||
arch/mips/ralink/of.c | 4 +++-
|
||||
3 files changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/arch/mips/kernel/head.S
|
||||
+++ b/arch/mips/kernel/head.S
|
||||
@@ -86,6 +86,9 @@ EXPORT(__image_cmdline)
|
||||
.fill 0x400
|
||||
#endif /* CONFIG_IMAGE_CMDLINE_HACK */
|
||||
|
||||
+ .ascii "OWRTDTB:"
|
||||
+ EXPORT(__image_dtb)
|
||||
+ .fill 0x4000
|
||||
__REF
|
||||
|
||||
NESTED(kernel_entry, 16, sp) # kernel entry point
|
||||
--- a/arch/mips/ralink/of.c
|
||||
+++ b/arch/mips/ralink/of.c
|
||||
@@ -66,6 +66,8 @@ static int __init early_init_dt_find_mem
|
||||
return 0;
|
||||
}
|
||||
|
||||
+extern struct boot_param_header __image_dtb;
|
||||
+
|
||||
void __init plat_mem_setup(void)
|
||||
{
|
||||
set_io_port_base(KSEG1);
|
||||
@@ -74,7 +76,7 @@ void __init plat_mem_setup(void)
|
||||
* Load the builtin devicetree. This causes the chosen node to be
|
||||
* parsed resulting in our memory appearing
|
||||
*/
|
||||
- __dt_setup_arch(__dtb_start);
|
||||
+ __dt_setup_arch(&__image_dtb);
|
||||
|
||||
strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
--- a/arch/mips/ralink/of.c
|
||||
+++ b/arch/mips/ralink/of.c
|
||||
@@ -3,7 +3,7 @@
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
- * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
+ * Copyright (C) 2008-2014 Imre Kaloz <kaloz@openwrt.org>
|
||||
* Copyright (C) 2008-2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
*/
|
||||
@@ -66,6 +66,17 @@ static int __init early_init_dt_find_mem
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int chosen_dtb;
|
||||
+
|
||||
+static int __init early_init_dt_find_chosen(unsigned long node, const char *uname,
|
||||
+ int depth, void *data)
|
||||
+{
|
||||
+ if (depth == 1 && !strcmp(uname, "chosen"))
|
||||
+ chosen_dtb = 1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
extern struct boot_param_header __image_dtb;
|
||||
|
||||
void __init plat_mem_setup(void)
|
||||
@@ -78,7 +89,9 @@ void __init plat_mem_setup(void)
|
||||
*/
|
||||
__dt_setup_arch(&__image_dtb);
|
||||
|
||||
- strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
|
||||
+ of_scan_flat_dt(early_init_dt_find_chosen, NULL);
|
||||
+ if (chosen_dtb)
|
||||
+ strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
|
||||
|
||||
of_scan_flat_dt(early_init_dt_find_memory, NULL);
|
||||
if (memory_dtb)
|
94
target/linux/ramips/patches-4.4/0200-linkit_bootstrap.patch
Normal file
94
target/linux/ramips/patches-4.4/0200-linkit_bootstrap.patch
Normal file
|
@ -0,0 +1,94 @@
|
|||
--- a/drivers/misc/Makefile
|
||||
+++ b/drivers/misc/Makefile
|
||||
@@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE) += genwqe/
|
||||
obj-$(CONFIG_ECHO) += echo/
|
||||
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
|
||||
obj-$(CONFIG_CXL_BASE) += cxl/
|
||||
+obj-$(CONFIG_SOC_MT7620) += linkit.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/misc/linkit.c
|
||||
@@ -0,0 +1,84 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * publishhed by the Free Software Foundation.
|
||||
+ *
|
||||
+ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/mtd/mtd.h>
|
||||
+#include <linux/gpio.h>
|
||||
+
|
||||
+#define LINKIT_LATCH_GPIO 11
|
||||
+
|
||||
+struct linkit_hw_data {
|
||||
+ char board[16];
|
||||
+ char rev[16];
|
||||
+};
|
||||
+
|
||||
+static void sanify_string(char *s)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < 15; i++)
|
||||
+ if (s[i] <= 0x20)
|
||||
+ s[i] = '\0';
|
||||
+ s[15] = '\0';
|
||||
+}
|
||||
+
|
||||
+static int linkit_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct linkit_hw_data hw;
|
||||
+ struct mtd_info *mtd;
|
||||
+ size_t retlen;
|
||||
+ int ret;
|
||||
+
|
||||
+ mtd = get_mtd_device_nm("factory");
|
||||
+ if (IS_ERR(mtd))
|
||||
+ return PTR_ERR(mtd);
|
||||
+
|
||||
+ ret = mtd_read(mtd, 0x400, sizeof(hw), &retlen, (u_char *) &hw);
|
||||
+ put_mtd_device(mtd);
|
||||
+
|
||||
+ sanify_string(hw.board);
|
||||
+ sanify_string(hw.rev);
|
||||
+
|
||||
+ dev_info(&pdev->dev, "Version : %s\n", hw.board);
|
||||
+ dev_info(&pdev->dev, "Revision : %s\n", hw.rev);
|
||||
+
|
||||
+ if (!strcmp(hw.board, "LINKITS7688")) {
|
||||
+ dev_info(&pdev->dev, "setting up bootstrap latch\n");
|
||||
+
|
||||
+ if (devm_gpio_request(&pdev->dev, LINKIT_LATCH_GPIO, "bootstrap")) {
|
||||
+ dev_err(&pdev->dev, "failed to setup bootstrap gpio\n");
|
||||
+ return -1;
|
||||
+ }
|
||||
+ gpio_direction_output(LINKIT_LATCH_GPIO, 0);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id linkit_match[] = {
|
||||
+ { .compatible = "mediatek,linkit" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, linkit_match);
|
||||
+
|
||||
+static struct platform_driver linkit_driver = {
|
||||
+ .probe = linkit_probe,
|
||||
+ .driver = {
|
||||
+ .name = "mtk-linkit",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = linkit_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+int __init linkit_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&linkit_driver);
|
||||
+}
|
||||
+late_initcall_sync(linkit_init);
|
|
@ -0,0 +1,150 @@
|
|||
From 9c2487f148ee38807d86beaf12dc2b818a764a99 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Tue, 17 Nov 2015 00:20:07 +0100
|
||||
Subject: [PATCH 500/513] Documentation: DT: net: add docs for ralink/mediatek
|
||||
SoC ethernet binding
|
||||
|
||||
Add three files. ralink,rt2880-net.txt descibes the actual frame engine
|
||||
and the other two describe the switch forntend bindings.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
Cc: devicetree@vger.kernel.org
|
||||
---
|
||||
.../bindings/net/mediatek,mt7620-gsw.txt | 26 +++++++++
|
||||
.../devicetree/bindings/net/ralink,rt2880-net.txt | 61 ++++++++++++++++++++
|
||||
.../devicetree/bindings/net/ralink,rt3050-esw.txt | 32 ++++++++++
|
||||
3 files changed, 119 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/net/mediatek,mt7620-gsw.txt
|
||||
create mode 100644 Documentation/devicetree/bindings/net/ralink,rt2880-net.txt
|
||||
create mode 100644 Documentation/devicetree/bindings/net/ralink,rt3050-esw.txt
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/net/mediatek,mt7620-gsw.txt
|
||||
@@ -0,0 +1,26 @@
|
||||
+Mediatek Gigabit Switch
|
||||
+=======================
|
||||
+
|
||||
+The mediatek gigabit switch can be found on Mediatek SoCs (mt7620, mt7621).
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible: Should be "mediatek,mt7620-gsw"
|
||||
+- reg: Address and length of the register set for the device
|
||||
+- interrupt-parent: Should be the phandle for the interrupt controller
|
||||
+ that services interrupts for this device
|
||||
+- interrupts: Should contain the gigabit switches interrupt
|
||||
+- resets: Should contain the gigabit switches resets
|
||||
+- reset-names: Should contain the reset names "gsw"
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+gsw@10110000 {
|
||||
+ compatible = "ralink,mt7620-gsw";
|
||||
+ reg = <0x10110000 8000>;
|
||||
+
|
||||
+ resets = <&rstctrl 23>;
|
||||
+ reset-names = "gsw";
|
||||
+
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <17>;
|
||||
+};
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/net/ralink,rt2880-net.txt
|
||||
@@ -0,0 +1,61 @@
|
||||
+Ralink Frame Engine Ethernet controller
|
||||
+=======================================
|
||||
+
|
||||
+The Ralink frame engine ethernet controller can be found on Ralink and
|
||||
+Mediatek SoCs (RT288x, RT3x5x, RT366x, RT388x, rt5350, mt7620, mt7621, mt76x8).
|
||||
+
|
||||
+Depending on the SoC, there is a number of ports connected to the CPU port
|
||||
+directly and/or via a (gigabit-)switch.
|
||||
+
|
||||
+* Ethernet controller node
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible: Should be one of "ralink,rt2880-eth", "ralink,rt3050-eth",
|
||||
+ "ralink,rt3050-eth", "ralink,rt3883-eth", "ralink,rt5350-eth",
|
||||
+ "mediatek,mt7620-eth", "mediatek,mt7621-eth"
|
||||
+- reg: Address and length of the register set for the device
|
||||
+- interrupt-parent: Should be the phandle for the interrupt controller
|
||||
+ that services interrupts for this device
|
||||
+- interrupts: Should contain the frame engines interrupt
|
||||
+- resets: Should contain the frame engines resets
|
||||
+- reset-names: Should contain the reset names "fe". If a switch is present
|
||||
+ "esw" is also required.
|
||||
+
|
||||
+
|
||||
+* Ethernet port node
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible: Should be "ralink,eth-port"
|
||||
+- reg: The number of the physical port
|
||||
+- phy-handle: reference to the node describing the phy
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+mdio-bus {
|
||||
+ ...
|
||||
+ phy0: ethernet-phy@0 {
|
||||
+ phy-mode = "mii";
|
||||
+ reg = <0>;
|
||||
+ };
|
||||
+};
|
||||
+
|
||||
+ethernet@400000 {
|
||||
+ compatible = "ralink,rt2880-eth";
|
||||
+ reg = <0x00400000 10000>;
|
||||
+
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+
|
||||
+ resets = <&rstctrl 18>;
|
||||
+ reset-names = "fe";
|
||||
+
|
||||
+ interrupt-parent = <&cpuintc>;
|
||||
+ interrupts = <5>;
|
||||
+
|
||||
+ port@0 {
|
||||
+ compatible = "ralink,eth-port";
|
||||
+ reg = <0>;
|
||||
+ phy-handle = <&phy0>;
|
||||
+ };
|
||||
+
|
||||
+};
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/net/ralink,rt3050-esw.txt
|
||||
@@ -0,0 +1,32 @@
|
||||
+Ralink Fast Ethernet Embedded Switch
|
||||
+====================================
|
||||
+
|
||||
+The ralink fast ethernet embedded switch can be found on Ralink and Mediatek
|
||||
+SoCs (RT3x5x, rt5350, mt76x8).
|
||||
+
|
||||
+Required properties:
|
||||
+- compatible: Should be "ralink,rt3050-esw"
|
||||
+- reg: Address and length of the register set for the device
|
||||
+- interrupt-parent: Should be the phandle for the interrupt controller
|
||||
+ that services interrupts for this device
|
||||
+- interrupts: Should contain the embedded switches interrupt
|
||||
+- resets: Should contain the embedded switches resets
|
||||
+- reset-names: Should contain the reset names "esw"
|
||||
+
|
||||
+Optional properties:
|
||||
+- ralink,portmap: can be used to choose if the default switch setup is
|
||||
+ llllw or wllll
|
||||
+- ralink,led_polarity: override the active high/low settings of the leds
|
||||
+
|
||||
+Example:
|
||||
+
|
||||
+esw@10110000 {
|
||||
+ compatible = "ralink,rt3050-esw";
|
||||
+ reg = <0x10110000 8000>;
|
||||
+
|
||||
+ resets = <&rstctrl 23>;
|
||||
+ reset-names = "esw";
|
||||
+
|
||||
+ interrupt-parent = <&intc>;
|
||||
+ interrupts = <17>;
|
||||
+};
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,691 @@
|
|||
From 2c39ddc83452c34fedc86261ed1f96d7537adfd1 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 21:28:10 +0100
|
||||
Subject: [PATCH 502/513] net-next: mediatek: add switch driver for rt3050
|
||||
|
||||
This driver is very basic and only provides basic init and irq support.
|
||||
Switchdev support for this device will follow.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/esw_rt3050.c | 640 ++++++++++++++++++++++++++++
|
||||
drivers/net/ethernet/mediatek/esw_rt3050.h | 29 ++
|
||||
2 files changed, 669 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
create mode 100644 drivers/net/ethernet/mediatek/esw_rt3050.h
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
@@ -0,0 +1,640 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/dma-mapping.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <linux/etherdevice.h>
|
||||
+#include <linux/ethtool.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/of_net.h>
|
||||
+#include <linux/of_mdio.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+
|
||||
+#include <linux/ioport.h>
|
||||
+#include <linux/mii.h>
|
||||
+
|
||||
+#include <ralink_regs.h>
|
||||
+
|
||||
+/* HW limitations for this switch:
|
||||
+ * - No large frame support (PKT_MAX_LEN at most 1536)
|
||||
+ * - Can't have untagged vlan and tagged vlan on one port at the same time,
|
||||
+ * though this might be possible using the undocumented PPE.
|
||||
+ */
|
||||
+
|
||||
+#define RT305X_ESW_REG_ISR 0x00
|
||||
+#define RT305X_ESW_REG_IMR 0x04
|
||||
+#define RT305X_ESW_REG_FCT0 0x08
|
||||
+#define RT305X_ESW_REG_PFC1 0x14
|
||||
+#define RT305X_ESW_REG_ATS 0x24
|
||||
+#define RT305X_ESW_REG_ATS0 0x28
|
||||
+#define RT305X_ESW_REG_ATS1 0x2c
|
||||
+#define RT305X_ESW_REG_ATS2 0x30
|
||||
+#define RT305X_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n))
|
||||
+#define RT305X_ESW_REG_VLANI(_n) (0x50 + 4 * (_n))
|
||||
+#define RT305X_ESW_REG_VMSC(_n) (0x70 + 4 * (_n))
|
||||
+#define RT305X_ESW_REG_POA 0x80
|
||||
+#define RT305X_ESW_REG_FPA 0x84
|
||||
+#define RT305X_ESW_REG_SOCPC 0x8c
|
||||
+#define RT305X_ESW_REG_POC0 0x90
|
||||
+#define RT305X_ESW_REG_POC1 0x94
|
||||
+#define RT305X_ESW_REG_POC2 0x98
|
||||
+#define RT305X_ESW_REG_SGC 0x9c
|
||||
+#define RT305X_ESW_REG_STRT 0xa0
|
||||
+#define RT305X_ESW_REG_PCR0 0xc0
|
||||
+#define RT305X_ESW_REG_PCR1 0xc4
|
||||
+#define RT305X_ESW_REG_FPA2 0xc8
|
||||
+#define RT305X_ESW_REG_FCT2 0xcc
|
||||
+#define RT305X_ESW_REG_SGC2 0xe4
|
||||
+#define RT305X_ESW_REG_P0LED 0xa4
|
||||
+#define RT305X_ESW_REG_P1LED 0xa8
|
||||
+#define RT305X_ESW_REG_P2LED 0xac
|
||||
+#define RT305X_ESW_REG_P3LED 0xb0
|
||||
+#define RT305X_ESW_REG_P4LED 0xb4
|
||||
+#define RT305X_ESW_REG_PXPC(_x) (0xe8 + (4 * _x))
|
||||
+#define RT305X_ESW_REG_P1PC 0xec
|
||||
+#define RT305X_ESW_REG_P2PC 0xf0
|
||||
+#define RT305X_ESW_REG_P3PC 0xf4
|
||||
+#define RT305X_ESW_REG_P4PC 0xf8
|
||||
+#define RT305X_ESW_REG_P5PC 0xfc
|
||||
+
|
||||
+#define RT305X_ESW_LED_LINK 0
|
||||
+#define RT305X_ESW_LED_100M 1
|
||||
+#define RT305X_ESW_LED_DUPLEX 2
|
||||
+#define RT305X_ESW_LED_ACTIVITY 3
|
||||
+#define RT305X_ESW_LED_COLLISION 4
|
||||
+#define RT305X_ESW_LED_LINKACT 5
|
||||
+#define RT305X_ESW_LED_DUPLCOLL 6
|
||||
+#define RT305X_ESW_LED_10MACT 7
|
||||
+#define RT305X_ESW_LED_100MACT 8
|
||||
+/* Additional led states not in datasheet: */
|
||||
+#define RT305X_ESW_LED_BLINK 10
|
||||
+#define RT305X_ESW_LED_ON 12
|
||||
+
|
||||
+#define RT305X_ESW_LINK_S 25
|
||||
+#define RT305X_ESW_DUPLEX_S 9
|
||||
+#define RT305X_ESW_SPD_S 0
|
||||
+
|
||||
+#define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16
|
||||
+#define RT305X_ESW_PCR0_WT_PHY_CMD BIT(13)
|
||||
+#define RT305X_ESW_PCR0_CPU_PHY_REG_S 8
|
||||
+
|
||||
+#define RT305X_ESW_PCR1_WT_DONE BIT(0)
|
||||
+
|
||||
+#define RT305X_ESW_ATS_TIMEOUT (5 * HZ)
|
||||
+#define RT305X_ESW_PHY_TIMEOUT (5 * HZ)
|
||||
+
|
||||
+#define RT305X_ESW_PVIDC_PVID_M 0xfff
|
||||
+#define RT305X_ESW_PVIDC_PVID_S 12
|
||||
+
|
||||
+#define RT305X_ESW_VLANI_VID_M 0xfff
|
||||
+#define RT305X_ESW_VLANI_VID_S 12
|
||||
+
|
||||
+#define RT305X_ESW_VMSC_MSC_M 0xff
|
||||
+#define RT305X_ESW_VMSC_MSC_S 8
|
||||
+
|
||||
+#define RT305X_ESW_SOCPC_DISUN2CPU_S 0
|
||||
+#define RT305X_ESW_SOCPC_DISMC2CPU_S 8
|
||||
+#define RT305X_ESW_SOCPC_DISBC2CPU_S 16
|
||||
+#define RT305X_ESW_SOCPC_CRC_PADDING BIT(25)
|
||||
+
|
||||
+#define RT305X_ESW_POC0_EN_BP_S 0
|
||||
+#define RT305X_ESW_POC0_EN_FC_S 8
|
||||
+#define RT305X_ESW_POC0_DIS_RMC2CPU_S 16
|
||||
+#define RT305X_ESW_POC0_DIS_PORT_M 0x7f
|
||||
+#define RT305X_ESW_POC0_DIS_PORT_S 23
|
||||
+
|
||||
+#define RT305X_ESW_POC2_UNTAG_EN_M 0xff
|
||||
+#define RT305X_ESW_POC2_UNTAG_EN_S 0
|
||||
+#define RT305X_ESW_POC2_ENAGING_S 8
|
||||
+#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16
|
||||
+
|
||||
+#define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f
|
||||
+#define RT305X_ESW_SGC2_DOUBLE_TAG_S 0
|
||||
+#define RT305X_ESW_SGC2_LAN_PMAP_M 0x3f
|
||||
+#define RT305X_ESW_SGC2_LAN_PMAP_S 24
|
||||
+
|
||||
+#define RT305X_ESW_PFC1_EN_VLAN_M 0xff
|
||||
+#define RT305X_ESW_PFC1_EN_VLAN_S 16
|
||||
+#define RT305X_ESW_PFC1_EN_TOS_S 24
|
||||
+
|
||||
+#define RT305X_ESW_VLAN_NONE 0xfff
|
||||
+
|
||||
+#define RT305X_ESW_GSC_BC_STROM_MASK 0x3
|
||||
+#define RT305X_ESW_GSC_BC_STROM_SHIFT 4
|
||||
+
|
||||
+#define RT305X_ESW_GSC_LED_FREQ_MASK 0x3
|
||||
+#define RT305X_ESW_GSC_LED_FREQ_SHIFT 23
|
||||
+
|
||||
+#define RT305X_ESW_POA_LINK_MASK 0x1f
|
||||
+#define RT305X_ESW_POA_LINK_SHIFT 25
|
||||
+
|
||||
+#define RT305X_ESW_PORT_ST_CHG BIT(26)
|
||||
+#define RT305X_ESW_PORT0 0
|
||||
+#define RT305X_ESW_PORT1 1
|
||||
+#define RT305X_ESW_PORT2 2
|
||||
+#define RT305X_ESW_PORT3 3
|
||||
+#define RT305X_ESW_PORT4 4
|
||||
+#define RT305X_ESW_PORT5 5
|
||||
+#define RT305X_ESW_PORT6 6
|
||||
+
|
||||
+#define RT305X_ESW_PMAP_LLLLLL 0x3f
|
||||
+#define RT305X_ESW_PMAP_LLLLWL 0x2f
|
||||
+#define RT305X_ESW_PMAP_WLLLLL 0x3e
|
||||
+
|
||||
+#define RT305X_ESW_PORTS_INTERNAL \
|
||||
+ (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) | \
|
||||
+ BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) | \
|
||||
+ BIT(RT305X_ESW_PORT4))
|
||||
+
|
||||
+#define RT305X_ESW_PORTS_NOCPU \
|
||||
+ (RT305X_ESW_PORTS_INTERNAL | BIT(RT305X_ESW_PORT5))
|
||||
+
|
||||
+#define RT305X_ESW_PORTS_CPU BIT(RT305X_ESW_PORT6)
|
||||
+
|
||||
+#define RT305X_ESW_PORTS_ALL \
|
||||
+ (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU)
|
||||
+
|
||||
+#define RT305X_ESW_NUM_PORTS 7
|
||||
+#define RT305X_ESW_NUM_LEDS 5
|
||||
+
|
||||
+#define RT5350_EWS_REG_LED_POLARITY 0x168
|
||||
+#define RT5350_RESET_EPHY BIT(24)
|
||||
+
|
||||
+struct esw_port {
|
||||
+ bool disable;
|
||||
+ u8 led;
|
||||
+};
|
||||
+
|
||||
+struct rt305x_esw {
|
||||
+ struct device *dev;
|
||||
+ void __iomem *base;
|
||||
+ int irq;
|
||||
+
|
||||
+ /* Protects against concurrent register r/w operations. */
|
||||
+ spinlock_t reg_rw_lock;
|
||||
+
|
||||
+ unsigned char port_map;
|
||||
+ unsigned int reg_led_polarity;
|
||||
+
|
||||
+ struct esw_port ports[RT305X_ESW_NUM_PORTS];
|
||||
+
|
||||
+};
|
||||
+
|
||||
+static inline void esw_w32(struct rt305x_esw *esw, u32 val, unsigned reg)
|
||||
+{
|
||||
+ __raw_writel(val, esw->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline u32 esw_r32(struct rt305x_esw *esw, unsigned reg)
|
||||
+{
|
||||
+ return __raw_readl(esw->base + reg);
|
||||
+}
|
||||
+
|
||||
+static inline void esw_rmw_raw(struct rt305x_esw *esw, unsigned reg,
|
||||
+ unsigned long mask, unsigned long val)
|
||||
+{
|
||||
+ unsigned long t;
|
||||
+
|
||||
+ t = __raw_readl(esw->base + reg) & ~mask;
|
||||
+ __raw_writel(t | val, esw->base + reg);
|
||||
+}
|
||||
+
|
||||
+static void esw_rmw(struct rt305x_esw *esw, unsigned reg,
|
||||
+ unsigned long mask, unsigned long val)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&esw->reg_rw_lock, flags);
|
||||
+ esw_rmw_raw(esw, reg, mask, val);
|
||||
+ spin_unlock_irqrestore(&esw->reg_rw_lock, flags);
|
||||
+}
|
||||
+
|
||||
+static u32 rt305x_mii_write(struct rt305x_esw *esw, u32 phy_addr,
|
||||
+ u32 phy_register, u32 write_data)
|
||||
+{
|
||||
+ unsigned long t_start = jiffies;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ while (1) {
|
||||
+ if (!(esw_r32(esw, RT305X_ESW_REG_PCR1) &
|
||||
+ RT305X_ESW_PCR1_WT_DONE))
|
||||
+ break;
|
||||
+ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) {
|
||||
+ ret = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ write_data &= 0xffff;
|
||||
+ esw_w32(esw, (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) |
|
||||
+ (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) |
|
||||
+ (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD,
|
||||
+ RT305X_ESW_REG_PCR0);
|
||||
+
|
||||
+ t_start = jiffies;
|
||||
+ while (1) {
|
||||
+ if (esw_r32(esw, RT305X_ESW_REG_PCR1) &
|
||||
+ RT305X_ESW_PCR1_WT_DONE)
|
||||
+ break;
|
||||
+
|
||||
+ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) {
|
||||
+ ret = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+out:
|
||||
+ if (ret)
|
||||
+ dev_err(esw->dev, "ramips_eth: MDIO timeout\n");
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static unsigned esw_get_port_disable(struct rt305x_esw *esw)
|
||||
+{
|
||||
+ unsigned reg;
|
||||
+
|
||||
+ reg = esw_r32(esw, RT305X_ESW_REG_POC0);
|
||||
+ return (reg >> RT305X_ESW_POC0_DIS_PORT_S) &
|
||||
+ RT305X_ESW_POC0_DIS_PORT_M;
|
||||
+}
|
||||
+
|
||||
+static void esw_hw_init(struct rt305x_esw *esw)
|
||||
+{
|
||||
+ int i;
|
||||
+ u8 port_disable = 0;
|
||||
+ u8 port_map = RT305X_ESW_PMAP_LLLLLL;
|
||||
+
|
||||
+ /* vodoo from original driver */
|
||||
+ esw_w32(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
|
||||
+ esw_w32(esw, 0x00000000, RT305X_ESW_REG_SGC2);
|
||||
+ /* Port priority 1 for all ports, vlan enabled. */
|
||||
+ esw_w32(esw, 0x00005555 |
|
||||
+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S),
|
||||
+ RT305X_ESW_REG_PFC1);
|
||||
+
|
||||
+ /* Enable Back Pressure, and Flow Control */
|
||||
+ esw_w32(esw, ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) |
|
||||
+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)),
|
||||
+ RT305X_ESW_REG_POC0);
|
||||
+
|
||||
+ /* Enable Aging, and VLAN TAG removal */
|
||||
+ esw_w32(esw, ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) |
|
||||
+ (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)),
|
||||
+ RT305X_ESW_REG_POC2);
|
||||
+
|
||||
+ esw_w32(esw, 0x00d6500c, RT305X_ESW_REG_FCT2);
|
||||
+
|
||||
+ /* 300s aging timer, max packet len 1536, broadcast storm prevention
|
||||
+ * disabled, disable collision abort, mac xor48 hash, 10 packet back
|
||||
+ * pressure jam, GMII disable was_transmit, back pressure disabled,
|
||||
+ * 30ms led flash, unmatched IGMP as broadcast, rmc tb fault to all
|
||||
+ * ports.
|
||||
+ */
|
||||
+ esw_w32(esw, 0x0008a301, RT305X_ESW_REG_SGC);
|
||||
+
|
||||
+ /* Setup SoC Port control register */
|
||||
+ esw_w32(esw,
|
||||
+ (RT305X_ESW_SOCPC_CRC_PADDING |
|
||||
+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISUN2CPU_S) |
|
||||
+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISMC2CPU_S) |
|
||||
+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISBC2CPU_S)),
|
||||
+ RT305X_ESW_REG_SOCPC);
|
||||
+
|
||||
+ /* ext phy base addr 31, enable port 5 polling, rx/tx clock skew 1,
|
||||
+ * turbo mii off, rgmi 3.3v off
|
||||
+ * port5: disabled
|
||||
+ * port6: enabled, gige, full-duplex, rx/tx-flow-control
|
||||
+ */
|
||||
+ esw_w32(esw, 0x3f502b28, RT305X_ESW_REG_FPA2);
|
||||
+ esw_w32(esw, 0x00000000, RT305X_ESW_REG_FPA);
|
||||
+
|
||||
+ /* Force Link/Activity on ports */
|
||||
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P0LED);
|
||||
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P1LED);
|
||||
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P2LED);
|
||||
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P3LED);
|
||||
+ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P4LED);
|
||||
+
|
||||
+ /* Copy disabled port configuration from bootloader setup */
|
||||
+ port_disable = esw_get_port_disable(esw);
|
||||
+ for (i = 0; i < 6; i++)
|
||||
+ esw->ports[i].disable = (port_disable & (1 << i)) != 0;
|
||||
+
|
||||
+ if (ralink_soc == RT305X_SOC_RT3352) {
|
||||
+ /* reset EPHY */
|
||||
+ fe_reset(RT5350_RESET_EPHY);
|
||||
+
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x8000);
|
||||
+ for (i = 0; i < 5; i++) {
|
||||
+ if (esw->ports[i].disable) {
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
|
||||
+ } else {
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR,
|
||||
+ BMCR_FULLDPLX |
|
||||
+ BMCR_ANENABLE |
|
||||
+ BMCR_SPEED100);
|
||||
+ }
|
||||
+ /* TX10 waveform coefficient LSB=0 disable PHY */
|
||||
+ rt305x_mii_write(esw, i, 26, 0x1601);
|
||||
+ /* TX100/TX10 AD/DA current bias */
|
||||
+ rt305x_mii_write(esw, i, 29, 0x7016);
|
||||
+ /* TX100 slew rate control */
|
||||
+ rt305x_mii_write(esw, i, 30, 0x0038);
|
||||
+ }
|
||||
+
|
||||
+ /* select global register */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x0);
|
||||
+ /* enlarge agcsel threshold 3 and threshold 2 */
|
||||
+ rt305x_mii_write(esw, 0, 1, 0x4a40);
|
||||
+ /* enlarge agcsel threshold 5 and threshold 4 */
|
||||
+ rt305x_mii_write(esw, 0, 2, 0x6254);
|
||||
+ /* enlarge agcsel threshold */
|
||||
+ rt305x_mii_write(esw, 0, 3, 0xa17f);
|
||||
+ rt305x_mii_write(esw, 0, 12, 0x7eaa);
|
||||
+ /* longer TP_IDL tail length */
|
||||
+ rt305x_mii_write(esw, 0, 14, 0x65);
|
||||
+ /* increased squelch pulse count threshold. */
|
||||
+ rt305x_mii_write(esw, 0, 16, 0x0684);
|
||||
+ /* set TX10 signal amplitude threshold to minimum */
|
||||
+ rt305x_mii_write(esw, 0, 17, 0x0fe0);
|
||||
+ /* set squelch amplitude to higher threshold */
|
||||
+ rt305x_mii_write(esw, 0, 18, 0x40ba);
|
||||
+ /* tune TP_IDL tail and head waveform, enable power
|
||||
+ * down slew rate control
|
||||
+ */
|
||||
+ rt305x_mii_write(esw, 0, 22, 0x253f);
|
||||
+ /* set PLL/Receive bias current are calibrated */
|
||||
+ rt305x_mii_write(esw, 0, 27, 0x2fda);
|
||||
+ /* change PLL/Receive bias current to internal(RT3350) */
|
||||
+ rt305x_mii_write(esw, 0, 28, 0xc410);
|
||||
+ /* change PLL bias current to internal(RT3052_MP3) */
|
||||
+ rt305x_mii_write(esw, 0, 29, 0x598b);
|
||||
+ /* select local register */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x8000);
|
||||
+ } else if (ralink_soc == RT305X_SOC_RT5350) {
|
||||
+ /* reset EPHY */
|
||||
+ fe_reset(RT5350_RESET_EPHY);
|
||||
+
|
||||
+ /* set the led polarity */
|
||||
+ esw_w32(esw, esw->reg_led_polarity & 0x1F,
|
||||
+ RT5350_EWS_REG_LED_POLARITY);
|
||||
+
|
||||
+ /* local registers */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x8000);
|
||||
+ for (i = 0; i < 5; i++) {
|
||||
+ if (esw->ports[i].disable) {
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
|
||||
+ } else {
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR,
|
||||
+ BMCR_FULLDPLX |
|
||||
+ BMCR_ANENABLE |
|
||||
+ BMCR_SPEED100);
|
||||
+ }
|
||||
+ /* TX10 waveform coefficient LSB=0 disable PHY */
|
||||
+ rt305x_mii_write(esw, i, 26, 0x1601);
|
||||
+ /* TX100/TX10 AD/DA current bias */
|
||||
+ rt305x_mii_write(esw, i, 29, 0x7015);
|
||||
+ /* TX100 slew rate control */
|
||||
+ rt305x_mii_write(esw, i, 30, 0x0038);
|
||||
+ }
|
||||
+
|
||||
+ /* global registers */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x0);
|
||||
+ /* enlarge agcsel threshold 3 and threshold 2 */
|
||||
+ rt305x_mii_write(esw, 0, 1, 0x4a40);
|
||||
+ /* enlarge agcsel threshold 5 and threshold 4 */
|
||||
+ rt305x_mii_write(esw, 0, 2, 0x6254);
|
||||
+ /* enlarge agcsel threshold 6 */
|
||||
+ rt305x_mii_write(esw, 0, 3, 0xa17f);
|
||||
+ rt305x_mii_write(esw, 0, 12, 0x7eaa);
|
||||
+ /* longer TP_IDL tail length */
|
||||
+ rt305x_mii_write(esw, 0, 14, 0x65);
|
||||
+ /* increased squelch pulse count threshold. */
|
||||
+ rt305x_mii_write(esw, 0, 16, 0x0684);
|
||||
+ /* set TX10 signal amplitude threshold to minimum */
|
||||
+ rt305x_mii_write(esw, 0, 17, 0x0fe0);
|
||||
+ /* set squelch amplitude to higher threshold */
|
||||
+ rt305x_mii_write(esw, 0, 18, 0x40ba);
|
||||
+ /* tune TP_IDL tail and head waveform, enable power
|
||||
+ * down slew rate control
|
||||
+ */
|
||||
+ rt305x_mii_write(esw, 0, 22, 0x253f);
|
||||
+ /* set PLL/Receive bias current are calibrated */
|
||||
+ rt305x_mii_write(esw, 0, 27, 0x2fda);
|
||||
+ /* change PLL/Receive bias current to internal(RT3350) */
|
||||
+ rt305x_mii_write(esw, 0, 28, 0xc410);
|
||||
+ /* change PLL bias current to internal(RT3052_MP3) */
|
||||
+ rt305x_mii_write(esw, 0, 29, 0x598b);
|
||||
+ /* select local register */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x8000);
|
||||
+ } else if (ralink_soc == MT762X_SOC_MT7628AN || ralink_soc == MT762X_SOC_MT7688) {
|
||||
+ int i;
|
||||
+
|
||||
+ /* reset EPHY */
|
||||
+ fe_reset(RT5350_RESET_EPHY);
|
||||
+
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x2000); /* change G2 page */
|
||||
+ rt305x_mii_write(esw, 0, 26, 0x0020);
|
||||
+
|
||||
+ for (i = 0; i < 5; i++) {
|
||||
+ rt305x_mii_write(esw, i, 31, 0x8000);
|
||||
+ rt305x_mii_write(esw, i, 0, 0x3100);
|
||||
+ rt305x_mii_write(esw, i, 30, 0xa000);
|
||||
+ rt305x_mii_write(esw, i, 31, 0xa000);
|
||||
+ rt305x_mii_write(esw, i, 16, 0x0606);
|
||||
+ rt305x_mii_write(esw, i, 23, 0x0f0e);
|
||||
+ rt305x_mii_write(esw, i, 24, 0x1610);
|
||||
+ rt305x_mii_write(esw, i, 30, 0x1f15);
|
||||
+ rt305x_mii_write(esw, i, 28, 0x6111);
|
||||
+ rt305x_mii_write(esw, i, 31, 0x2000);
|
||||
+ rt305x_mii_write(esw, i, 26, 0x0000);
|
||||
+ }
|
||||
+
|
||||
+ /* 100Base AOI setting */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x5000);
|
||||
+ rt305x_mii_write(esw, 0, 19, 0x004a);
|
||||
+ rt305x_mii_write(esw, 0, 20, 0x015a);
|
||||
+ rt305x_mii_write(esw, 0, 21, 0x00ee);
|
||||
+ rt305x_mii_write(esw, 0, 22, 0x0033);
|
||||
+ rt305x_mii_write(esw, 0, 23, 0x020a);
|
||||
+ rt305x_mii_write(esw, 0, 24, 0x0000);
|
||||
+ rt305x_mii_write(esw, 0, 25, 0x024a);
|
||||
+ rt305x_mii_write(esw, 0, 26, 0x035a);
|
||||
+ rt305x_mii_write(esw, 0, 27, 0x02ee);
|
||||
+ rt305x_mii_write(esw, 0, 28, 0x0233);
|
||||
+ rt305x_mii_write(esw, 0, 29, 0x000a);
|
||||
+ rt305x_mii_write(esw, 0, 30, 0x0000);
|
||||
+ } else {
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x8000);
|
||||
+ for (i = 0; i < 5; i++) {
|
||||
+ if (esw->ports[i].disable) {
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN);
|
||||
+ } else {
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR,
|
||||
+ BMCR_FULLDPLX |
|
||||
+ BMCR_ANENABLE |
|
||||
+ BMCR_SPEED100);
|
||||
+ }
|
||||
+ /* TX10 waveform coefficient */
|
||||
+ rt305x_mii_write(esw, i, 26, 0x1601);
|
||||
+ /* TX100/TX10 AD/DA current bias */
|
||||
+ rt305x_mii_write(esw, i, 29, 0x7058);
|
||||
+ /* TX100 slew rate control */
|
||||
+ rt305x_mii_write(esw, i, 30, 0x0018);
|
||||
+ }
|
||||
+
|
||||
+ /* PHY IOT */
|
||||
+ /* select global register */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x0);
|
||||
+ /* tune TP_IDL tail and head waveform */
|
||||
+ rt305x_mii_write(esw, 0, 22, 0x052f);
|
||||
+ /* set TX10 signal amplitude threshold to minimum */
|
||||
+ rt305x_mii_write(esw, 0, 17, 0x0fe0);
|
||||
+ /* set squelch amplitude to higher threshold */
|
||||
+ rt305x_mii_write(esw, 0, 18, 0x40ba);
|
||||
+ /* longer TP_IDL tail length */
|
||||
+ rt305x_mii_write(esw, 0, 14, 0x65);
|
||||
+ /* select local register */
|
||||
+ rt305x_mii_write(esw, 0, 31, 0x8000);
|
||||
+ }
|
||||
+
|
||||
+ if (esw->port_map)
|
||||
+ port_map = esw->port_map;
|
||||
+ else
|
||||
+ port_map = RT305X_ESW_PMAP_LLLLLL;
|
||||
+
|
||||
+ /* Unused HW feature, but still nice to be consistent here...
|
||||
+ * This is also exported to userspace ('lan' attribute) so it's
|
||||
+ * conveniently usable to decide which ports go into the wan vlan by
|
||||
+ * default.
|
||||
+ */
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_SGC2,
|
||||
+ RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
|
||||
+ port_map << RT305X_ESW_SGC2_LAN_PMAP_S);
|
||||
+
|
||||
+ /* make the switch leds blink */
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++)
|
||||
+ esw->ports[i].led = 0x05;
|
||||
+
|
||||
+ /* Only unmask the port change interrupt */
|
||||
+ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t esw_interrupt(int irq, void *_esw)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = (struct rt305x_esw *)_esw;
|
||||
+ u32 status;
|
||||
+
|
||||
+ status = esw_r32(esw, RT305X_ESW_REG_ISR);
|
||||
+ if (status & RT305X_ESW_PORT_ST_CHG) {
|
||||
+ u32 link = esw_r32(esw, RT305X_ESW_REG_POA);
|
||||
+
|
||||
+ link >>= RT305X_ESW_POA_LINK_SHIFT;
|
||||
+ link &= RT305X_ESW_POA_LINK_MASK;
|
||||
+ dev_info(esw->dev, "link changed 0x%02X\n", link);
|
||||
+ }
|
||||
+ esw_w32(esw, status, RT305X_ESW_REG_ISR);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static int esw_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res = platform_get_resource(p, IORESOURCE_MEM, 0);
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+ const __be32 *port_map, *reg_init;
|
||||
+ struct rt305x_esw *esw;
|
||||
+ struct resource *irq;
|
||||
+ int ret;
|
||||
+
|
||||
+ esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
|
||||
+ if (!esw)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ esw->dev = &pdev->dev;
|
||||
+ esw->irq = irq->start;
|
||||
+ esw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (!esw->base)
|
||||
+ return -EADDRNOTAVAIL;
|
||||
+
|
||||
+ port_map = of_get_property(np, "mediatek,portmap", NULL);
|
||||
+ if (port_map)
|
||||
+ esw->port_map = be32_to_cpu(*port_map);
|
||||
+
|
||||
+ reg_init = of_get_property(np, "mediatek,led_polarity", NULL);
|
||||
+ if (reg_init)
|
||||
+ esw->reg_led_polarity = be32_to_cpu(*reg_init);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, esw);
|
||||
+
|
||||
+ spin_lock_init(&esw->reg_rw_lock);
|
||||
+
|
||||
+ esw_hw_init(esw);
|
||||
+
|
||||
+ ret = devm_request_irq(&pdev->dev, esw->irq, esw_interrupt, 0, "esw",
|
||||
+ esw);
|
||||
+
|
||||
+ if (!ret) {
|
||||
+ esw_w32(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR);
|
||||
+ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int esw_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = platform_get_drvdata(pdev);
|
||||
+
|
||||
+ if (esw) {
|
||||
+ esw_w32(esw, ~0, RT305X_ESW_REG_IMR);
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id ralink_esw_match[] = {
|
||||
+ { .compatible = "ralink,rt3050-esw" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, ralink_esw_match);
|
||||
+
|
||||
+static struct platform_driver esw_driver = {
|
||||
+ .probe = esw_probe,
|
||||
+ .remove = esw_remove,
|
||||
+ .driver = {
|
||||
+ .name = "rt3050-esw",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = ralink_esw_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+int __init mtk_switch_init(void)
|
||||
+{
|
||||
+ return platform_driver_register(&esw_driver);
|
||||
+}
|
||||
+
|
||||
+void mtk_switch_exit(void)
|
||||
+{
|
||||
+ platform_driver_unregister(&esw_driver);
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.h
|
||||
@@ -0,0 +1,29 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RALINK_ESW_RT3052_H__
|
||||
+#define _RALINK_ESW_RT3052_H__
|
||||
+
|
||||
+#ifdef CONFIG_NET_MEDIATEK_ESW_RT3052
|
||||
+
|
||||
+int __init mtk_switch_init(void);
|
||||
+void mtk_switch_exit(void);
|
||||
+
|
||||
+#else
|
||||
+
|
||||
+static inline int __init mtk_switch_init(void) { return 0; }
|
||||
+static inline void mtk_switch_exit(void) { }
|
||||
+
|
||||
+#endif
|
||||
+#endif
|
|
@ -0,0 +1,394 @@
|
|||
From 322a9598692943961791ac6e5a3f385b379dcdc3 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 21:23:18 +0100
|
||||
Subject: [PATCH 503/513] net-next: mediatek: add switch driver for mt7620
|
||||
|
||||
This driver is very basic and only provides basic init and irq support.
|
||||
Switchdev support for this device will follow.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/gsw_mt7620.c | 255 ++++++++++++++++++++++++++++
|
||||
drivers/net/ethernet/mediatek/gsw_mt7620.h | 117 +++++++++++++
|
||||
2 files changed, 372 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.c
|
||||
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.c
|
||||
@@ -0,0 +1,255 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+
|
||||
+#include <ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "gsw_mt7620.h"
|
||||
+
|
||||
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
|
||||
+{
|
||||
+ iowrite32(val, gsw->base + reg);
|
||||
+}
|
||||
+
|
||||
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
|
||||
+{
|
||||
+ return ioread32(gsw->base + reg);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t gsw_interrupt_mt7620(int irq, void *_priv)
|
||||
+{
|
||||
+ struct fe_priv *priv = (struct fe_priv *)_priv;
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+ u32 status;
|
||||
+ int i, max = (gsw->port4 == PORT4_EPHY) ? (4) : (3);
|
||||
+
|
||||
+ status = mtk_switch_r32(gsw, GSW_REG_ISR);
|
||||
+ if (status & PORT_IRQ_ST_CHG)
|
||||
+ for (i = 0; i <= max; i++) {
|
||||
+ u32 status = mtk_switch_r32(gsw, GSW_REG_PORT_STATUS(i));
|
||||
+ int link = status & 0x1;
|
||||
+
|
||||
+ if (link != priv->link[i])
|
||||
+ mt7620_print_link_state(priv, i, link,
|
||||
+ (status >> 2) & 3,
|
||||
+ (status & 0x2));
|
||||
+
|
||||
+ priv->link[i] = link;
|
||||
+ }
|
||||
+ mtk_switch_w32(gsw, status, GSW_REG_ISR);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static void mt7620_hw_init(struct mt7620_gsw *gsw, struct device_node *np)
|
||||
+{
|
||||
+ u32 is_BGA = (rt_sysc_r32(0x0c) >> 16) & 1;
|
||||
+
|
||||
+ rt_sysc_w32(rt_sysc_r32(SYSC_REG_CFG1) | BIT(8), SYSC_REG_CFG1);
|
||||
+ mtk_switch_w32(gsw, mtk_switch_r32(gsw, GSW_REG_CKGCR) & ~(0x3 << 4), GSW_REG_CKGCR);
|
||||
+
|
||||
+ if (of_property_read_bool(np, "mediatek,mt7530")) {
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* turn off ephy and set phy base addr to 12 */
|
||||
+ mtk_switch_w32(gsw, mtk_switch_r32(gsw, GSW_REG_GPC1) |
|
||||
+ (0x1f << 24) | (0xc << 16),
|
||||
+ GSW_REG_GPC1);
|
||||
+
|
||||
+ /* set MT7530 central align */
|
||||
+ val = mt7530_mdio_r32(gsw, 0x7830);
|
||||
+ val &= ~BIT(0);
|
||||
+ val |= BIT(1);
|
||||
+ mt7530_mdio_w32(gsw, 0x7830, val);
|
||||
+
|
||||
+ val = mt7530_mdio_r32(gsw, 0x7a40);
|
||||
+ val &= ~BIT(30);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a40, val);
|
||||
+
|
||||
+ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
|
||||
+ } else {
|
||||
+ /* global page 4 */
|
||||
+ _mt7620_mii_write(gsw, 1, 31, 0x4000);
|
||||
+
|
||||
+ _mt7620_mii_write(gsw, 1, 17, 0x7444);
|
||||
+ if (is_BGA)
|
||||
+ _mt7620_mii_write(gsw, 1, 19, 0x0114);
|
||||
+ else
|
||||
+ _mt7620_mii_write(gsw, 1, 19, 0x0117);
|
||||
+
|
||||
+ _mt7620_mii_write(gsw, 1, 22, 0x10cf);
|
||||
+ _mt7620_mii_write(gsw, 1, 25, 0x6212);
|
||||
+ _mt7620_mii_write(gsw, 1, 26, 0x0777);
|
||||
+ _mt7620_mii_write(gsw, 1, 29, 0x4000);
|
||||
+ _mt7620_mii_write(gsw, 1, 28, 0xc077);
|
||||
+ _mt7620_mii_write(gsw, 1, 24, 0x0000);
|
||||
+
|
||||
+ /* global page 3 */
|
||||
+ _mt7620_mii_write(gsw, 1, 31, 0x3000);
|
||||
+ _mt7620_mii_write(gsw, 1, 17, 0x4838);
|
||||
+
|
||||
+ /* global page 2 */
|
||||
+ _mt7620_mii_write(gsw, 1, 31, 0x2000);
|
||||
+ if (is_BGA) {
|
||||
+ _mt7620_mii_write(gsw, 1, 21, 0x0515);
|
||||
+ _mt7620_mii_write(gsw, 1, 22, 0x0053);
|
||||
+ _mt7620_mii_write(gsw, 1, 23, 0x00bf);
|
||||
+ _mt7620_mii_write(gsw, 1, 24, 0x0aaf);
|
||||
+ _mt7620_mii_write(gsw, 1, 25, 0x0fad);
|
||||
+ _mt7620_mii_write(gsw, 1, 26, 0x0fc1);
|
||||
+ } else {
|
||||
+ _mt7620_mii_write(gsw, 1, 21, 0x0517);
|
||||
+ _mt7620_mii_write(gsw, 1, 22, 0x0fd2);
|
||||
+ _mt7620_mii_write(gsw, 1, 23, 0x00bf);
|
||||
+ _mt7620_mii_write(gsw, 1, 24, 0x0aab);
|
||||
+ _mt7620_mii_write(gsw, 1, 25, 0x00ae);
|
||||
+ _mt7620_mii_write(gsw, 1, 26, 0x0fff);
|
||||
+ }
|
||||
+ /* global page 1 */
|
||||
+ _mt7620_mii_write(gsw, 1, 31, 0x1000);
|
||||
+ _mt7620_mii_write(gsw, 1, 17, 0xe7f8);
|
||||
+ }
|
||||
+
|
||||
+ /* global page 0 */
|
||||
+ _mt7620_mii_write(gsw, 1, 31, 0x8000);
|
||||
+ _mt7620_mii_write(gsw, 0, 30, 0xa000);
|
||||
+ _mt7620_mii_write(gsw, 1, 30, 0xa000);
|
||||
+ _mt7620_mii_write(gsw, 2, 30, 0xa000);
|
||||
+ _mt7620_mii_write(gsw, 3, 30, 0xa000);
|
||||
+
|
||||
+ _mt7620_mii_write(gsw, 0, 4, 0x05e1);
|
||||
+ _mt7620_mii_write(gsw, 1, 4, 0x05e1);
|
||||
+ _mt7620_mii_write(gsw, 2, 4, 0x05e1);
|
||||
+ _mt7620_mii_write(gsw, 3, 4, 0x05e1);
|
||||
+
|
||||
+ /* global page 2 */
|
||||
+ _mt7620_mii_write(gsw, 1, 31, 0xa000);
|
||||
+ _mt7620_mii_write(gsw, 0, 16, 0x1111);
|
||||
+ _mt7620_mii_write(gsw, 1, 16, 0x1010);
|
||||
+ _mt7620_mii_write(gsw, 2, 16, 0x1515);
|
||||
+ _mt7620_mii_write(gsw, 3, 16, 0x0f0f);
|
||||
+
|
||||
+ /* CPU Port6 Force Link 1G, FC ON */
|
||||
+ mtk_switch_w32(gsw, 0x5e33b, GSW_REG_PORT_PMCR(6));
|
||||
+
|
||||
+ /* Set Port 6 as CPU Port */
|
||||
+ mtk_switch_w32(gsw, 0x7f7f7fe0, 0x0010);
|
||||
+
|
||||
+ /* setup port 4 */
|
||||
+ if (gsw->port4 == PORT4_EPHY) {
|
||||
+ u32 val = rt_sysc_r32(SYSC_REG_CFG1);
|
||||
+
|
||||
+ val |= 3 << 14;
|
||||
+ rt_sysc_w32(val, SYSC_REG_CFG1);
|
||||
+ _mt7620_mii_write(gsw, 4, 30, 0xa000);
|
||||
+ _mt7620_mii_write(gsw, 4, 4, 0x05e1);
|
||||
+ _mt7620_mii_write(gsw, 4, 16, 0x1313);
|
||||
+ pr_info("gsw: setting port4 to ephy mode\n");
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mediatek_gsw_match[] = {
|
||||
+ { .compatible = "mediatek,mt7620-gsw" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
|
||||
+
|
||||
+int mtk_gsw_init(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct device_node *np = priv->switch_np;
|
||||
+ struct platform_device *pdev = of_find_device_by_node(np);
|
||||
+ struct mt7620_gsw *gsw;
|
||||
+
|
||||
+ if (!pdev)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ gsw = platform_get_drvdata(pdev);
|
||||
+ priv->soc->swpriv = gsw;
|
||||
+
|
||||
+ mt7620_hw_init(gsw, np);
|
||||
+
|
||||
+ if (gsw->irq) {
|
||||
+ request_irq(gsw->irq, gsw_interrupt_mt7620, 0,
|
||||
+ "gsw", priv);
|
||||
+ mtk_switch_w32(gsw, ~PORT_IRQ_ST_CHG, GSW_REG_IMR);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_gsw_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ const char *port4 = NULL;
|
||||
+ struct mt7620_gsw *gsw;
|
||||
+ struct device_node *np = pdev->dev.of_node;
|
||||
+
|
||||
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
|
||||
+ if (!gsw)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ gsw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (!gsw->base)
|
||||
+ return -EADDRNOTAVAIL;
|
||||
+
|
||||
+ gsw->dev = &pdev->dev;
|
||||
+
|
||||
+ of_property_read_string(np, "mediatek,port4", &port4);
|
||||
+ if (port4 && !strcmp(port4, "ephy"))
|
||||
+ gsw->port4 = PORT4_EPHY;
|
||||
+ else if (port4 && !strcmp(port4, "gmac"))
|
||||
+ gsw->port4 = PORT4_EXT;
|
||||
+ else
|
||||
+ gsw->port4 = PORT4_EPHY;
|
||||
+
|
||||
+ gsw->irq = irq_of_parse_and_map(np, 0);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, gsw);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7620_gsw_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver gsw_driver = {
|
||||
+ .probe = mt7620_gsw_probe,
|
||||
+ .remove = mt7620_gsw_remove,
|
||||
+ .driver = {
|
||||
+ .name = "mt7620-gsw",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mediatek_gsw_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(gsw_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
|
||||
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7620 SoC");
|
||||
+MODULE_VERSION(MTK_FE_DRV_VERSION);
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
|
||||
@@ -0,0 +1,117 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RALINK_GSW_MT7620_H__
|
||||
+#define _RALINK_GSW_MT7620_H__
|
||||
+
|
||||
+#define GSW_REG_PHY_TIMEOUT (5 * HZ)
|
||||
+
|
||||
+#ifdef CONFIG_SOC_MT7621
|
||||
+#define MT7620A_GSW_REG_PIAC 0x0004
|
||||
+#else
|
||||
+#define MT7620A_GSW_REG_PIAC 0x7004
|
||||
+#endif
|
||||
+
|
||||
+#define GSW_NUM_VLANS 16
|
||||
+#define GSW_NUM_VIDS 4096
|
||||
+#define GSW_NUM_PORTS 7
|
||||
+#define GSW_PORT6 6
|
||||
+
|
||||
+#define GSW_MDIO_ACCESS BIT(31)
|
||||
+#define GSW_MDIO_READ BIT(19)
|
||||
+#define GSW_MDIO_WRITE BIT(18)
|
||||
+#define GSW_MDIO_START BIT(16)
|
||||
+#define GSW_MDIO_ADDR_SHIFT 20
|
||||
+#define GSW_MDIO_REG_SHIFT 25
|
||||
+
|
||||
+#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
|
||||
+#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
|
||||
+#define GSW_REG_SMACCR0 0x3fE4
|
||||
+#define GSW_REG_SMACCR1 0x3fE8
|
||||
+#define GSW_REG_CKGCR 0x3ff0
|
||||
+
|
||||
+#define GSW_REG_IMR 0x7008
|
||||
+#define GSW_REG_ISR 0x700c
|
||||
+#define GSW_REG_GPC1 0x7014
|
||||
+
|
||||
+#define SYSC_REG_CHIP_REV_ID 0x0c
|
||||
+#define SYSC_REG_CFG1 0x14
|
||||
+#define RST_CTRL_MCM BIT(2)
|
||||
+#define SYSC_PAD_RGMII2_MDIO 0x58
|
||||
+#define SYSC_GPIO_MODE 0x60
|
||||
+
|
||||
+#define PORT_IRQ_ST_CHG 0x7f
|
||||
+
|
||||
+#ifdef CONFIG_SOC_MT7621
|
||||
+#define ESW_PHY_POLLING 0x0000
|
||||
+#else
|
||||
+#define ESW_PHY_POLLING 0x7000
|
||||
+#endif
|
||||
+
|
||||
+#define PMCR_IPG BIT(18)
|
||||
+#define PMCR_MAC_MODE BIT(16)
|
||||
+#define PMCR_FORCE BIT(15)
|
||||
+#define PMCR_TX_EN BIT(14)
|
||||
+#define PMCR_RX_EN BIT(13)
|
||||
+#define PMCR_BACKOFF BIT(9)
|
||||
+#define PMCR_BACKPRES BIT(8)
|
||||
+#define PMCR_RX_FC BIT(5)
|
||||
+#define PMCR_TX_FC BIT(4)
|
||||
+#define PMCR_SPEED(_x) (_x << 2)
|
||||
+#define PMCR_DUPLEX BIT(1)
|
||||
+#define PMCR_LINK BIT(0)
|
||||
+
|
||||
+#define PHY_AN_EN BIT(31)
|
||||
+#define PHY_PRE_EN BIT(30)
|
||||
+#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
|
||||
+
|
||||
+enum {
|
||||
+ /* Global attributes. */
|
||||
+ GSW_ATTR_ENABLE_VLAN,
|
||||
+ /* Port attributes. */
|
||||
+ GSW_ATTR_PORT_UNTAG,
|
||||
+};
|
||||
+
|
||||
+enum {
|
||||
+ PORT4_EPHY = 0,
|
||||
+ PORT4_EXT,
|
||||
+};
|
||||
+
|
||||
+struct mt7620_gsw {
|
||||
+ struct device *dev;
|
||||
+ void __iomem *base;
|
||||
+ int irq;
|
||||
+ int port4;
|
||||
+ unsigned long int autopoll;
|
||||
+};
|
||||
+
|
||||
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
|
||||
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
|
||||
+int mtk_gsw_init(struct fe_priv *priv);
|
||||
+
|
||||
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
|
||||
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
|
||||
+void mt7620_mdio_link_adjust(struct fe_priv *priv, int port);
|
||||
+int mt7620_has_carrier(struct fe_priv *priv);
|
||||
+void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
|
||||
+ int speed, int duplex);
|
||||
+
|
||||
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
|
||||
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
|
||||
+
|
||||
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
|
||||
+ u32 phy_register, u32 write_data);
|
||||
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
|
||||
+
|
||||
+#endif
|
|
@ -0,0 +1,301 @@
|
|||
From 9fc19d5f7354709298dcb15b3a4c7cd9a18acebf Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 21:24:46 +0100
|
||||
Subject: [PATCH 504/513] net-next: mediatek: add switch driver for mt7621
|
||||
|
||||
This driver is very basic and only provides basic init and irq support.
|
||||
Switchdev support for this device will follow.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/gsw_mt7621.c | 284 ++++++++++++++++++++++++++++
|
||||
1 file changed, 284 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7621.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/gsw_mt7621.c
|
||||
@@ -0,0 +1,284 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/of_device.h>
|
||||
+#include <linux/of_irq.h>
|
||||
+
|
||||
+#include <ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "gsw_mt7620.h"
|
||||
+
|
||||
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
|
||||
+{
|
||||
+ iowrite32(val, gsw->base + reg);
|
||||
+}
|
||||
+
|
||||
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
|
||||
+{
|
||||
+ return ioread32(gsw->base + reg);
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t gsw_interrupt_mt7621(int irq, void *_priv)
|
||||
+{
|
||||
+ struct fe_priv *priv = (struct fe_priv *)_priv;
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+ u32 reg, i;
|
||||
+
|
||||
+ reg = mt7530_mdio_r32(gsw, 0x700c);
|
||||
+
|
||||
+ for (i = 0; i < 5; i++)
|
||||
+ if (reg & BIT(i)) {
|
||||
+ unsigned int link;
|
||||
+
|
||||
+ link = mt7530_mdio_r32(gsw,
|
||||
+ 0x3008 + (i * 0x100)) & 0x1;
|
||||
+
|
||||
+ if (link != priv->link[i]) {
|
||||
+ priv->link[i] = link;
|
||||
+ if (link)
|
||||
+ netdev_info(priv->netdev,
|
||||
+ "port %d link up\n", i);
|
||||
+ else
|
||||
+ netdev_info(priv->netdev,
|
||||
+ "port %d link down\n", i);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ mt7530_mdio_w32(gsw, 0x700c, 0x1f);
|
||||
+
|
||||
+ return IRQ_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static void mt7621_hw_init(struct mt7620_gsw *gsw, struct device_node *np)
|
||||
+{
|
||||
+ u32 i;
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* wardware reset the switch */
|
||||
+ fe_reset(RST_CTRL_MCM);
|
||||
+ mdelay(10);
|
||||
+
|
||||
+ /* reduce RGMII2 PAD driving strength */
|
||||
+ rt_sysc_m32(3 << 4, 0, SYSC_PAD_RGMII2_MDIO);
|
||||
+
|
||||
+ /* gpio mux - RGMII1=Normal mode */
|
||||
+ rt_sysc_m32(BIT(14), 0, SYSC_GPIO_MODE);
|
||||
+
|
||||
+ /* set GMAC1 RGMII mode */
|
||||
+ rt_sysc_m32(3 << 12, 0, SYSC_REG_CFG1);
|
||||
+
|
||||
+ /* enable MDIO to control MT7530 */
|
||||
+ rt_sysc_m32(3 << 12, 0, SYSC_GPIO_MODE);
|
||||
+
|
||||
+ /* turn off all PHYs */
|
||||
+ for (i = 0; i <= 4; i++) {
|
||||
+ val = _mt7620_mii_read(gsw, i, 0x0);
|
||||
+ val |= BIT(11);
|
||||
+ _mt7620_mii_write(gsw, i, 0x0, val);
|
||||
+ }
|
||||
+
|
||||
+ /* reset the switch */
|
||||
+ mt7530_mdio_w32(gsw, 0x7000, 0x3);
|
||||
+ usleep_range(10, 20);
|
||||
+
|
||||
+ if ((rt_sysc_r32(SYSC_REG_CHIP_REV_ID) & 0xFFFF) == 0x0101) {
|
||||
+ /* (GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536) */
|
||||
+ mtk_switch_w32(gsw, 0x2105e30b, 0x100);
|
||||
+ mt7530_mdio_w32(gsw, 0x3600, 0x5e30b);
|
||||
+ } else {
|
||||
+ /* (GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536) */
|
||||
+ mtk_switch_w32(gsw, 0x2105e33b, 0x100);
|
||||
+ mt7530_mdio_w32(gsw, 0x3600, 0x5e33b);
|
||||
+ }
|
||||
+
|
||||
+ /* (GE2, Link down) */
|
||||
+ mtk_switch_w32(gsw, 0x8000, 0x200);
|
||||
+
|
||||
+ /* Enable Port 6, P5 as GMAC5, P5 disable */
|
||||
+ val = mt7530_mdio_r32(gsw, 0x7804);
|
||||
+ val &= ~BIT(8);
|
||||
+ val |= BIT(6) | BIT(13) | BIT(16);
|
||||
+ mt7530_mdio_w32(gsw, 0x7804, val);
|
||||
+
|
||||
+ val = rt_sysc_r32(0x10);
|
||||
+ val = (val >> 6) & 0x7;
|
||||
+ if (val >= 6) {
|
||||
+ /* 25Mhz Xtal - do nothing */
|
||||
+ } else if (val >= 3) {
|
||||
+ /* 40Mhz */
|
||||
+
|
||||
+ /* disable MT7530 core clock */
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x0);
|
||||
+
|
||||
+ /* disable MT7530 PLL */
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x2020);
|
||||
+
|
||||
+ /* for MT7530 core clock = 500Mhz */
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x40e);
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x119);
|
||||
+
|
||||
+ /* enable MT7530 PLL */
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x2820);
|
||||
+
|
||||
+ usleep_range(20, 40);
|
||||
+
|
||||
+ /* enable MT7530 core clock */
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
|
||||
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
|
||||
+ } else {
|
||||
+ /* 20Mhz Xtal - TODO */
|
||||
+ }
|
||||
+
|
||||
+ /* RGMII */
|
||||
+ _mt7620_mii_write(gsw, 0, 14, 0x1);
|
||||
+
|
||||
+ /* set MT7530 central align */
|
||||
+ val = mt7530_mdio_r32(gsw, 0x7830);
|
||||
+ val &= ~BIT(0);
|
||||
+ val |= BIT(1);
|
||||
+ mt7530_mdio_w32(gsw, 0x7830, val);
|
||||
+ val = mt7530_mdio_r32(gsw, 0x7a40);
|
||||
+ val &= ~BIT(30);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a40, val);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a78, 0x855);
|
||||
+
|
||||
+ /* delay setting for 10/1000M */
|
||||
+ mt7530_mdio_w32(gsw, 0x7b00, 0x102);
|
||||
+ mt7530_mdio_w32(gsw, 0x7b04, 0x14);
|
||||
+
|
||||
+ /* lower Tx Driving*/
|
||||
+ mt7530_mdio_w32(gsw, 0x7a54, 0x44);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a5c, 0x44);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a64, 0x44);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a6c, 0x44);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a74, 0x44);
|
||||
+ mt7530_mdio_w32(gsw, 0x7a7c, 0x44);
|
||||
+
|
||||
+ /* turn on all PHYs */
|
||||
+ for (i = 0; i <= 4; i++) {
|
||||
+ val = _mt7620_mii_read(gsw, i, 0);
|
||||
+ val &= ~BIT(11);
|
||||
+ _mt7620_mii_write(gsw, i, 0, val);
|
||||
+ }
|
||||
+
|
||||
+ /* enable irq */
|
||||
+ val = mt7530_mdio_r32(gsw, 0x7808);
|
||||
+ val |= 3 << 16;
|
||||
+ mt7530_mdio_w32(gsw, 0x7808, val);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mediatek_gsw_match[] = {
|
||||
+ { .compatible = "mediatek,mt7621-gsw" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
|
||||
+
|
||||
+int mtk_gsw_init(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct device_node *np = priv->switch_np;
|
||||
+ struct platform_device *pdev = of_find_device_by_node(np);
|
||||
+ struct mt7620_gsw *gsw;
|
||||
+
|
||||
+ if (!pdev)
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ gsw = platform_get_drvdata(pdev);
|
||||
+ priv->soc->swpriv = gsw;
|
||||
+
|
||||
+ mt7621_hw_init(gsw, np);
|
||||
+
|
||||
+ if (gsw->irq) {
|
||||
+ request_irq(gsw->irq, gsw_interrupt_mt7621, 0,
|
||||
+ "gsw", priv);
|
||||
+ mt7530_mdio_w32(gsw, 0x7008, 0x1f);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7621_gsw_probe(struct platform_device *pdev)
|
||||
+{
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
+ const char *port4 = NULL;
|
||||
+ struct mt7620_gsw *gsw;
|
||||
+ struct device_node *np;
|
||||
+
|
||||
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
|
||||
+ if (!gsw)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ gsw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (!gsw->base)
|
||||
+ return -EADDRNOTAVAIL;
|
||||
+
|
||||
+ gsw->dev = &pdev->dev;
|
||||
+
|
||||
+ of_property_read_string(np, "mediatek,port4", &port4);
|
||||
+ if (port4 && !strcmp(port4, "ephy"))
|
||||
+ gsw->port4 = PORT4_EPHY;
|
||||
+ else if (port4 && !strcmp(port4, "gmac"))
|
||||
+ gsw->port4 = PORT4_EXT;
|
||||
+ else
|
||||
+ gsw->port4 = PORT4_EPHY;
|
||||
+
|
||||
+ gsw->irq = irq_of_parse_and_map(np, 0);
|
||||
+
|
||||
+ platform_set_drvdata(pdev, gsw);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mt7621_gsw_remove(struct platform_device *pdev)
|
||||
+{
|
||||
+ platform_set_drvdata(pdev, NULL);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static struct platform_driver gsw_driver = {
|
||||
+ .probe = mt7621_gsw_probe,
|
||||
+ .remove = mt7621_gsw_remove,
|
||||
+ .driver = {
|
||||
+ .name = "mt7621-gsw",
|
||||
+ .owner = THIS_MODULE,
|
||||
+ .of_match_table = mediatek_gsw_match,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+module_platform_driver(gsw_driver);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
|
||||
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7621 SoC");
|
||||
+MODULE_VERSION(MTK_FE_DRV_VERSION);
|
|
@ -0,0 +1,351 @@
|
|||
From f8c8f4bd2a13e0cc060c93812377373d436f7f02 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Wed, 18 Nov 2015 03:13:05 +0100
|
||||
Subject: [PATCH 505/513] net-next: mediatek: add support for rt2880
|
||||
|
||||
rt2880 is the oldest SoC with this core. It has a single gBit port that will
|
||||
normally be attached to an external phy of switch. The patch also adds the
|
||||
code required to drive the mdio bus.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/mdio_rt2880.c | 222 +++++++++++++++++++++++++++
|
||||
drivers/net/ethernet/mediatek/mdio_rt2880.h | 23 +++
|
||||
drivers/net/ethernet/mediatek/soc_rt2880.c | 76 +++++++++
|
||||
3 files changed, 321 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/mdio_rt2880.c
|
||||
create mode 100644 drivers/net/ethernet/mediatek/mdio_rt2880.h
|
||||
create mode 100644 drivers/net/ethernet/mediatek/soc_rt2880.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/mdio_rt2880.c
|
||||
@@ -0,0 +1,222 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/of_net.h>
|
||||
+#include <linux/of_mdio.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "mdio_rt2880.h"
|
||||
+#include "mdio.h"
|
||||
+
|
||||
+#define FE_MDIO_RETRY 1000
|
||||
+
|
||||
+static unsigned char *rt2880_speed_str(struct fe_priv *priv)
|
||||
+{
|
||||
+ switch (priv->phy->speed[0]) {
|
||||
+ case SPEED_1000:
|
||||
+ return "1000";
|
||||
+ case SPEED_100:
|
||||
+ return "100";
|
||||
+ case SPEED_10:
|
||||
+ return "10";
|
||||
+ }
|
||||
+
|
||||
+ return "?";
|
||||
+}
|
||||
+
|
||||
+void rt2880_mdio_link_adjust(struct fe_priv *priv, int port)
|
||||
+{
|
||||
+ u32 mdio_cfg;
|
||||
+
|
||||
+ if (!priv->link[0]) {
|
||||
+ netif_carrier_off(priv->netdev);
|
||||
+ netdev_info(priv->netdev, "link down\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ mdio_cfg = FE_MDIO_CFG_TX_CLK_SKEW_200 |
|
||||
+ FE_MDIO_CFG_RX_CLK_SKEW_200 |
|
||||
+ FE_MDIO_CFG_GP1_FRC_EN;
|
||||
+
|
||||
+ if (priv->phy->duplex[0] == DUPLEX_FULL)
|
||||
+ mdio_cfg |= FE_MDIO_CFG_GP1_DUPLEX;
|
||||
+
|
||||
+ if (priv->phy->tx_fc[0])
|
||||
+ mdio_cfg |= FE_MDIO_CFG_GP1_FC_TX;
|
||||
+
|
||||
+ if (priv->phy->rx_fc[0])
|
||||
+ mdio_cfg |= FE_MDIO_CFG_GP1_FC_RX;
|
||||
+
|
||||
+ switch (priv->phy->speed[0]) {
|
||||
+ case SPEED_10:
|
||||
+ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_10;
|
||||
+ break;
|
||||
+ case SPEED_100:
|
||||
+ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_100;
|
||||
+ break;
|
||||
+ case SPEED_1000:
|
||||
+ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_1000;
|
||||
+ break;
|
||||
+ default:
|
||||
+ BUG();
|
||||
+ }
|
||||
+
|
||||
+ fe_w32(mdio_cfg, FE_MDIO_CFG);
|
||||
+
|
||||
+ netif_carrier_on(priv->netdev);
|
||||
+ netdev_info(priv->netdev, "link up (%sMbps/%s duplex)\n",
|
||||
+ rt2880_speed_str(priv),
|
||||
+ (priv->phy->duplex[0] == DUPLEX_FULL) ? "Full" : "Half");
|
||||
+}
|
||||
+
|
||||
+static int rt2880_mdio_wait_ready(struct fe_priv *priv)
|
||||
+{
|
||||
+ int retries;
|
||||
+
|
||||
+ retries = FE_MDIO_RETRY;
|
||||
+ while (1) {
|
||||
+ u32 t;
|
||||
+
|
||||
+ t = fe_r32(FE_MDIO_ACCESS);
|
||||
+ if ((t & BIT(31)) == 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (retries-- == 0)
|
||||
+ break;
|
||||
+
|
||||
+ udelay(1);
|
||||
+ }
|
||||
+
|
||||
+ dev_err(priv->device, "MDIO operation timed out\n");
|
||||
+ return -ETIMEDOUT;
|
||||
+}
|
||||
+
|
||||
+int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
|
||||
+{
|
||||
+ struct fe_priv *priv = bus->priv;
|
||||
+ int err;
|
||||
+ u32 t;
|
||||
+
|
||||
+ err = rt2880_mdio_wait_ready(priv);
|
||||
+ if (err)
|
||||
+ return 0xffff;
|
||||
+
|
||||
+ t = (phy_addr << 24) | (phy_reg << 16);
|
||||
+ fe_w32(t, FE_MDIO_ACCESS);
|
||||
+ t |= BIT(31);
|
||||
+ fe_w32(t, FE_MDIO_ACCESS);
|
||||
+
|
||||
+ err = rt2880_mdio_wait_ready(priv);
|
||||
+ if (err)
|
||||
+ return 0xffff;
|
||||
+
|
||||
+ pr_debug("%s: addr=%04x, reg=%04x, value=%04x\n", __func__,
|
||||
+ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff);
|
||||
+
|
||||
+ return fe_r32(FE_MDIO_ACCESS) & 0xffff;
|
||||
+}
|
||||
+
|
||||
+int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
|
||||
+{
|
||||
+ struct fe_priv *priv = bus->priv;
|
||||
+ int err;
|
||||
+ u32 t;
|
||||
+
|
||||
+ pr_debug("%s: addr=%04x, reg=%04x, value=%04x\n", __func__,
|
||||
+ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff);
|
||||
+
|
||||
+ err = rt2880_mdio_wait_ready(priv);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ t = (1 << 30) | (phy_addr << 24) | (phy_reg << 16) | val;
|
||||
+ fe_w32(t, FE_MDIO_ACCESS);
|
||||
+ t |= BIT(31);
|
||||
+ fe_w32(t, FE_MDIO_ACCESS);
|
||||
+
|
||||
+ return rt2880_mdio_wait_ready(priv);
|
||||
+}
|
||||
+
|
||||
+void rt2880_port_init(struct fe_priv *priv, struct device_node *np)
|
||||
+{
|
||||
+ const __be32 *id = of_get_property(np, "reg", NULL);
|
||||
+ const __be32 *link;
|
||||
+ int size;
|
||||
+ int phy_mode;
|
||||
+
|
||||
+ if (!id || (be32_to_cpu(*id) != 0)) {
|
||||
+ pr_err("%s: invalid port id\n", np->name);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ priv->phy->phy_fixed[0] = of_get_property(np,
|
||||
+ "mediatek,fixed-link", &size);
|
||||
+ if (priv->phy->phy_fixed[0] &&
|
||||
+ (size != (4 * sizeof(*priv->phy->phy_fixed[0])))) {
|
||||
+ pr_err("%s: invalid fixed link property\n", np->name);
|
||||
+ priv->phy->phy_fixed[0] = NULL;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ phy_mode = of_get_phy_mode(np);
|
||||
+ switch (phy_mode) {
|
||||
+ case PHY_INTERFACE_MODE_RGMII:
|
||||
+ break;
|
||||
+ case PHY_INTERFACE_MODE_MII:
|
||||
+ break;
|
||||
+ case PHY_INTERFACE_MODE_RMII:
|
||||
+ break;
|
||||
+ default:
|
||||
+ if (!priv->phy->phy_fixed[0])
|
||||
+ dev_err(priv->device, "port %d - invalid phy mode\n",
|
||||
+ priv->phy->speed[0]);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ priv->phy->phy_node[0] = of_parse_phandle(np, "phy-handle", 0);
|
||||
+ if (!priv->phy->phy_node[0] && !priv->phy->phy_fixed[0])
|
||||
+ return;
|
||||
+
|
||||
+ if (priv->phy->phy_fixed[0]) {
|
||||
+ link = priv->phy->phy_fixed[0];
|
||||
+ priv->phy->speed[0] = be32_to_cpup(link++);
|
||||
+ priv->phy->duplex[0] = be32_to_cpup(link++);
|
||||
+ priv->phy->tx_fc[0] = be32_to_cpup(link++);
|
||||
+ priv->phy->rx_fc[0] = be32_to_cpup(link++);
|
||||
+
|
||||
+ priv->link[0] = 1;
|
||||
+ switch (priv->phy->speed[0]) {
|
||||
+ case SPEED_10:
|
||||
+ break;
|
||||
+ case SPEED_100:
|
||||
+ break;
|
||||
+ case SPEED_1000:
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(priv->device, "invalid link speed: %d\n",
|
||||
+ priv->phy->speed[0]);
|
||||
+ priv->phy->phy_fixed[0] = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+ dev_info(priv->device, "using fixed link parameters\n");
|
||||
+ rt2880_mdio_link_adjust(priv, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (priv->phy->phy_node[0] && priv->mii_bus->phy_map[0])
|
||||
+ fe_connect_phy_node(priv, priv->phy->phy_node[0]);
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/mdio_rt2880.h
|
||||
@@ -0,0 +1,23 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#ifndef _RALINK_MDIO_RT2880_H__
|
||||
+#define _RALINK_MDIO_RT2880_H__
|
||||
+
|
||||
+void rt2880_mdio_link_adjust(struct fe_priv *priv, int port);
|
||||
+int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
|
||||
+int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
|
||||
+void rt2880_port_init(struct fe_priv *priv, struct device_node *np);
|
||||
+
|
||||
+#endif
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/soc_rt2880.c
|
||||
@@ -0,0 +1,76 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "mdio_rt2880.h"
|
||||
+
|
||||
+#define RT2880_RESET_FE BIT(18)
|
||||
+
|
||||
+static void rt2880_init_data(struct fe_soc_data *data,
|
||||
+ struct net_device *netdev)
|
||||
+{
|
||||
+ struct fe_priv *priv = netdev_priv(netdev);
|
||||
+
|
||||
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_PADDING_BUG |
|
||||
+ FE_FLAG_JUMBO_FRAME | FE_FLAG_CALIBRATE_CLK;
|
||||
+ netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX;
|
||||
+ /* this should work according to the datasheet but actually does not*/
|
||||
+ /* netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; */
|
||||
+}
|
||||
+
|
||||
+void rt2880_fe_reset(void)
|
||||
+{
|
||||
+ fe_reset(RT2880_RESET_FE);
|
||||
+}
|
||||
+
|
||||
+static int rt2880_fwd_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = fe_set_clock_cycle(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ fe_fwd_config(priv);
|
||||
+ fe_w32(FE_PSE_FQFC_CFG_INIT, FE_PSE_FQ_CFG);
|
||||
+ fe_csum_config(priv);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+struct fe_soc_data rt2880_data = {
|
||||
+ .init_data = rt2880_init_data,
|
||||
+ .reset_fe = rt2880_fe_reset,
|
||||
+ .fwd_config = rt2880_fwd_config,
|
||||
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
||||
+ .checksum_bit = RX_DMA_L4VALID,
|
||||
+ .rx_int = FE_RX_DONE_INT,
|
||||
+ .tx_int = FE_TX_DONE_INT,
|
||||
+ .status_int = FE_CNT_GDM_AF,
|
||||
+ .mdio_read = rt2880_mdio_read,
|
||||
+ .mdio_write = rt2880_mdio_write,
|
||||
+ .mdio_adjust_link = rt2880_mdio_link_adjust,
|
||||
+ .port_init = rt2880_port_init,
|
||||
+};
|
||||
+
|
||||
+const struct of_device_id of_fe_match[] = {
|
||||
+ { .compatible = "ralink,rt2880-eth", .data = &rt2880_data },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, of_fe_match);
|
|
@ -0,0 +1,239 @@
|
|||
From a3555658ce5dd97df3dc225289b92800da9d38ba Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 21:28:51 +0100
|
||||
Subject: [PATCH 506/513] net-next: mediatek: add support for rt3050
|
||||
|
||||
Add support for SoCs from the rt3050 family. This include rt3050, rt3052,
|
||||
rt3352 and rt5350. These all have a builtin 5 port 100mbit switch. This patch
|
||||
includes rudimentary code to power up the switch. There are a lot of magic
|
||||
values that get written to the switch and the internal phys. These values
|
||||
come straight from the SDK driver and we do not know the meaning of most of
|
||||
them.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/esw_rt3050.c | 18 +---
|
||||
drivers/net/ethernet/mediatek/soc_rt3050.c | 158 ++++++++++++++++++++++++++++
|
||||
2 files changed, 159 insertions(+), 17 deletions(-)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/soc_rt3050.c
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
@@ -14,27 +14,11 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
-#include <linux/types.h>
|
||||
-#include <linux/dma-mapping.h>
|
||||
-#include <linux/init.h>
|
||||
-#include <linux/skbuff.h>
|
||||
-#include <linux/etherdevice.h>
|
||||
-#include <linux/ethtool.h>
|
||||
#include <linux/platform_device.h>
|
||||
-#include <linux/of_device.h>
|
||||
-#include <linux/clk.h>
|
||||
-#include <linux/of_net.h>
|
||||
-#include <linux/of_mdio.h>
|
||||
-
|
||||
#include <asm/mach-ralink/ralink_regs.h>
|
||||
|
||||
#include "mtk_eth_soc.h"
|
||||
|
||||
-#include <linux/ioport.h>
|
||||
-#include <linux/mii.h>
|
||||
-
|
||||
-#include <ralink_regs.h>
|
||||
-
|
||||
/* HW limitations for this switch:
|
||||
* - No large frame support (PKT_MAX_LEN at most 1536)
|
||||
* - Can't have untagged vlan and tagged vlan on one port at the same time,
|
||||
@@ -559,7 +543,7 @@ static irqreturn_t esw_interrupt(int irq
|
||||
|
||||
static int esw_probe(struct platform_device *pdev)
|
||||
{
|
||||
- struct resource *res = platform_get_resource(p, IORESOURCE_MEM, 0);
|
||||
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const __be32 *port_map, *reg_init;
|
||||
struct rt305x_esw *esw;
|
||||
@@ -629,12 +613,9 @@ static struct platform_driver esw_driver
|
||||
},
|
||||
};
|
||||
|
||||
-int __init mtk_switch_init(void)
|
||||
-{
|
||||
- return platform_driver_register(&esw_driver);
|
||||
-}
|
||||
+module_platform_driver(esw_driver);
|
||||
|
||||
-void mtk_switch_exit(void)
|
||||
-{
|
||||
- platform_driver_unregister(&esw_driver);
|
||||
-}
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
|
||||
+MODULE_DESCRIPTION("Switch driver for RT305X SoC");
|
||||
+MODULE_VERSION(MTK_FE_DRV_VERSION);
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/soc_rt3050.c
|
||||
@@ -0,0 +1,158 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "mdio_rt2880.h"
|
||||
+
|
||||
+#define RT305X_RESET_FE BIT(21)
|
||||
+#define RT305X_RESET_ESW BIT(23)
|
||||
+
|
||||
+static const u16 rt5350_reg_table[FE_REG_COUNT] = {
|
||||
+ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
|
||||
+ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
|
||||
+ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
|
||||
+ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
|
||||
+ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
|
||||
+ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
|
||||
+ [FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
|
||||
+ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
|
||||
+ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
|
||||
+ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
|
||||
+ [FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
|
||||
+ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
|
||||
+ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
|
||||
+ [FE_REG_FE_RST_GL] = 0,
|
||||
+ [FE_REG_FE_DMA_VID_BASE] = 0,
|
||||
+};
|
||||
+
|
||||
+static void rt305x_init_data(struct fe_soc_data *data,
|
||||
+ struct net_device *netdev)
|
||||
+{
|
||||
+ struct fe_priv *priv = netdev_priv(netdev);
|
||||
+
|
||||
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_PADDING_BUG |
|
||||
+ FE_FLAG_CALIBRATE_CLK | FE_FLAG_HAS_SWITCH;
|
||||
+ netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
|
||||
+ NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX;
|
||||
+}
|
||||
+
|
||||
+static int rt3050_fwd_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ if (ralink_soc != RT305X_SOC_RT3052) {
|
||||
+ ret = fe_set_clock_cycle(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ fe_fwd_config(priv);
|
||||
+ if (ralink_soc != RT305X_SOC_RT3352)
|
||||
+ fe_w32(FE_PSE_FQFC_CFG_INIT, FE_PSE_FQ_CFG);
|
||||
+ fe_csum_config(priv);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rt305x_fe_reset(void)
|
||||
+{
|
||||
+ fe_reset(RT305X_RESET_FE);
|
||||
+}
|
||||
+
|
||||
+static void rt5350_init_data(struct fe_soc_data *data,
|
||||
+ struct net_device *netdev)
|
||||
+{
|
||||
+ struct fe_priv *priv = netdev_priv(netdev);
|
||||
+
|
||||
+ priv->flags = FE_FLAG_HAS_SWITCH;
|
||||
+ netdev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM;
|
||||
+}
|
||||
+
|
||||
+static void rt5350_set_mac(struct fe_priv *priv, unsigned char *mac)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&priv->page_lock, flags);
|
||||
+ fe_w32((mac[0] << 8) | mac[1], RT5350_SDM_MAC_ADRH);
|
||||
+ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
|
||||
+ RT5350_SDM_MAC_ADRL);
|
||||
+ spin_unlock_irqrestore(&priv->page_lock, flags);
|
||||
+}
|
||||
+
|
||||
+static void rt5350_rxcsum_config(bool enable)
|
||||
+{
|
||||
+ if (enable)
|
||||
+ fe_w32(fe_r32(RT5350_SDM_CFG) | (RT5350_SDM_ICS_EN |
|
||||
+ RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN),
|
||||
+ RT5350_SDM_CFG);
|
||||
+ else
|
||||
+ fe_w32(fe_r32(RT5350_SDM_CFG) & ~(RT5350_SDM_ICS_EN |
|
||||
+ RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN),
|
||||
+ RT5350_SDM_CFG);
|
||||
+}
|
||||
+
|
||||
+static int rt5350_fwd_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct net_device *dev = priv_netdev(priv);
|
||||
+
|
||||
+ rt5350_rxcsum_config((dev->features & NETIF_F_RXCSUM));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void rt5350_tx_dma(struct fe_tx_dma *txd)
|
||||
+{
|
||||
+ txd->txd4 = 0;
|
||||
+}
|
||||
+
|
||||
+static void rt5350_fe_reset(void)
|
||||
+{
|
||||
+ fe_reset(RT305X_RESET_FE | RT305X_RESET_ESW);
|
||||
+}
|
||||
+
|
||||
+static struct fe_soc_data rt3050_data = {
|
||||
+ .init_data = rt305x_init_data,
|
||||
+ .reset_fe = rt305x_fe_reset,
|
||||
+ .fwd_config = rt3050_fwd_config,
|
||||
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
||||
+ .checksum_bit = RX_DMA_L4VALID,
|
||||
+ .rx_int = FE_RX_DONE_INT,
|
||||
+ .tx_int = FE_TX_DONE_INT,
|
||||
+ .status_int = FE_CNT_GDM_AF,
|
||||
+};
|
||||
+
|
||||
+static struct fe_soc_data rt5350_data = {
|
||||
+ .init_data = rt5350_init_data,
|
||||
+ .reg_table = rt5350_reg_table,
|
||||
+ .reset_fe = rt5350_fe_reset,
|
||||
+ .set_mac = rt5350_set_mac,
|
||||
+ .fwd_config = rt5350_fwd_config,
|
||||
+ .tx_dma = rt5350_tx_dma,
|
||||
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
||||
+ .checksum_bit = RX_DMA_L4VALID,
|
||||
+ .rx_int = RT5350_RX_DONE_INT,
|
||||
+ .tx_int = RT5350_TX_DONE_INT,
|
||||
+};
|
||||
+
|
||||
+const struct of_device_id of_fe_match[] = {
|
||||
+ { .compatible = "ralink,rt3050-eth", .data = &rt3050_data },
|
||||
+ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, of_fe_match);
|
|
@ -0,0 +1,94 @@
|
|||
From 5ad283c69029a519681ed453e7f7ddf250c10559 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Wed, 18 Nov 2015 03:51:24 +0100
|
||||
Subject: [PATCH 507/513] net-next: mediatek: add support for rt3883
|
||||
|
||||
Add support for rt3883 and its smaller version rt3662. They both have a single
|
||||
gBit port that will normally be attached to an external phy of switch.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/soc_rt3883.c | 75 ++++++++++++++++++++++++++++
|
||||
1 file changed, 75 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/soc_rt3883.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/soc_rt3883.c
|
||||
@@ -0,0 +1,75 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "mdio_rt2880.h"
|
||||
+
|
||||
+#define RT3883_RSTCTRL_FE BIT(21)
|
||||
+
|
||||
+static void rt3883_fe_reset(void)
|
||||
+{
|
||||
+ fe_reset(RT3883_RSTCTRL_FE);
|
||||
+}
|
||||
+
|
||||
+static int rt3883_fwd_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = fe_set_clock_cycle(priv);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ fe_fwd_config(priv);
|
||||
+ fe_w32(FE_PSE_FQFC_CFG_256Q, FE_PSE_FQ_CFG);
|
||||
+ fe_csum_config(priv);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void rt3883_init_data(struct fe_soc_data *data,
|
||||
+ struct net_device *netdev)
|
||||
+{
|
||||
+ struct fe_priv *priv = netdev_priv(netdev);
|
||||
+
|
||||
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_PADDING_BUG |
|
||||
+ FE_FLAG_JUMBO_FRAME | FE_FLAG_CALIBRATE_CLK;
|
||||
+ netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |
|
||||
+ NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX;
|
||||
+}
|
||||
+
|
||||
+static struct fe_soc_data rt3883_data = {
|
||||
+ .init_data = rt3883_init_data,
|
||||
+ .reset_fe = rt3883_fe_reset,
|
||||
+ .fwd_config = rt3883_fwd_config,
|
||||
+ .pdma_glo_cfg = FE_PDMA_SIZE_8DWORDS,
|
||||
+ .rx_int = FE_RX_DONE_INT,
|
||||
+ .tx_int = FE_TX_DONE_INT,
|
||||
+ .status_int = FE_CNT_GDM_AF,
|
||||
+ .checksum_bit = RX_DMA_L4VALID,
|
||||
+ .mdio_read = rt2880_mdio_read,
|
||||
+ .mdio_write = rt2880_mdio_write,
|
||||
+ .mdio_adjust_link = rt2880_mdio_link_adjust,
|
||||
+ .port_init = rt2880_port_init,
|
||||
+};
|
||||
+
|
||||
+const struct of_device_id of_fe_match[] = {
|
||||
+ { .compatible = "ralink,rt3883-eth", .data = &rt3883_data },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, of_fe_match);
|
|
@ -0,0 +1,519 @@
|
|||
From 1efca7b539a91c49ab1d6484ec3a69c48fa6062b Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 21:25:35 +0100
|
||||
Subject: [PATCH 508/513] net-next: mediatek: add support for mt7620
|
||||
|
||||
Add support for SoCs from the mt7620 family. This include mt7620 and mt7621.
|
||||
These all have one dedicated external gbit port and a builtin 5 port 100mbit
|
||||
switch. Additionally one of the 5 switch ports can be changed to become an
|
||||
additional gbit port that we can attach a phy to. This patch includes
|
||||
rudimentary code to power up the switch. There are a lot of magic values
|
||||
that get written to the switch and the internal phys. These values come
|
||||
straight from the SDK driver.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/mdio_mt7620.c | 156 +++++++++++++
|
||||
drivers/net/ethernet/mediatek/soc_mt7620.c | 334 +++++++++++++++++++++++++++
|
||||
2 files changed, 490 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/mdio_mt7620.c
|
||||
create mode 100644 drivers/net/ethernet/mediatek/soc_mt7620.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/mdio_mt7620.c
|
||||
@@ -0,0 +1,156 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "gsw_mt7620.h"
|
||||
+#include "mdio.h"
|
||||
+
|
||||
+static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
|
||||
+{
|
||||
+ unsigned long t_start = jiffies;
|
||||
+
|
||||
+ while (1) {
|
||||
+ if (!(mtk_switch_r32(gsw, MT7620A_GSW_REG_PIAC) & GSW_MDIO_ACCESS))
|
||||
+ return 0;
|
||||
+ if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ dev_err(gsw->dev, "mdio: MDIO timeout\n");
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
|
||||
+ u32 phy_register, u32 write_data)
|
||||
+{
|
||||
+ if (mt7620_mii_busy_wait(gsw))
|
||||
+ return -1;
|
||||
+
|
||||
+ write_data &= 0xffff;
|
||||
+
|
||||
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
|
||||
+ (phy_register << GSW_MDIO_REG_SHIFT) |
|
||||
+ (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
|
||||
+ MT7620A_GSW_REG_PIAC);
|
||||
+
|
||||
+ if (mt7620_mii_busy_wait(gsw))
|
||||
+ return -1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
|
||||
+{
|
||||
+ u32 d;
|
||||
+
|
||||
+ if (mt7620_mii_busy_wait(gsw))
|
||||
+ return 0xffff;
|
||||
+
|
||||
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
|
||||
+ (phy_reg << GSW_MDIO_REG_SHIFT) |
|
||||
+ (phy_addr << GSW_MDIO_ADDR_SHIFT),
|
||||
+ MT7620A_GSW_REG_PIAC);
|
||||
+
|
||||
+ if (mt7620_mii_busy_wait(gsw))
|
||||
+ return 0xffff;
|
||||
+
|
||||
+ d = mtk_switch_r32(gsw, MT7620A_GSW_REG_PIAC) & 0xffff;
|
||||
+
|
||||
+ return d;
|
||||
+}
|
||||
+
|
||||
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
|
||||
+{
|
||||
+ struct fe_priv *priv = bus->priv;
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+
|
||||
+ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
|
||||
+}
|
||||
+
|
||||
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
|
||||
+{
|
||||
+ struct fe_priv *priv = bus->priv;
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+
|
||||
+ return _mt7620_mii_read(gsw, phy_addr, phy_reg);
|
||||
+}
|
||||
+
|
||||
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
|
||||
+{
|
||||
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
||||
+ _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
|
||||
+ _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
|
||||
+}
|
||||
+
|
||||
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
|
||||
+{
|
||||
+ u16 high, low;
|
||||
+
|
||||
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
||||
+ low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
|
||||
+ high = _mt7620_mii_read(gsw, 0x1f, 0x10);
|
||||
+
|
||||
+ return (high << 16) | (low & 0xffff);
|
||||
+}
|
||||
+
|
||||
+static unsigned char *fe_speed_str(int speed)
|
||||
+{
|
||||
+ switch (speed) {
|
||||
+ case 2:
|
||||
+ case SPEED_1000:
|
||||
+ return "1000";
|
||||
+ case 1:
|
||||
+ case SPEED_100:
|
||||
+ return "100";
|
||||
+ case 0:
|
||||
+ case SPEED_10:
|
||||
+ return "10";
|
||||
+ }
|
||||
+
|
||||
+ return "? ";
|
||||
+}
|
||||
+
|
||||
+int mt7620_has_carrier(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < GSW_PORT6; i++)
|
||||
+ if (mtk_switch_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
|
||||
+ return 1;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
|
||||
+ int speed, int duplex)
|
||||
+{
|
||||
+ if (link)
|
||||
+ netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n",
|
||||
+ port, fe_speed_str(speed),
|
||||
+ (duplex) ? "Full" : "Half");
|
||||
+ else
|
||||
+ netdev_info(priv->netdev, "port %d link down\n", port);
|
||||
+}
|
||||
+
|
||||
+void mt7620_mdio_link_adjust(struct fe_priv *priv, int port)
|
||||
+{
|
||||
+ mt7620_print_link_state(priv, port, priv->link[port],
|
||||
+ priv->phy->speed[port],
|
||||
+ (priv->phy->duplex[port] == DUPLEX_FULL));
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/soc_mt7620.c
|
||||
@@ -0,0 +1,334 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/if_vlan.h>
|
||||
+#include <linux/of_net.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#include <mt7620.h>
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "gsw_mt7620.h"
|
||||
+#include "mt7530.h"
|
||||
+#include "mdio.h"
|
||||
+
|
||||
+#define MT7620A_CDMA_CSG_CFG 0x400
|
||||
+#define MT7620_DMA_VID (MT7620A_CDMA_CSG_CFG | 0x30)
|
||||
+#define MT7621_CDMP_IG_CTRL (MT7620A_CDMA_CSG_CFG + 0x00)
|
||||
+#define MT7621_CDMP_EG_CTRL (MT7620A_CDMA_CSG_CFG + 0x04)
|
||||
+#define MT7620A_RESET_FE BIT(21)
|
||||
+#define MT7621_RESET_FE BIT(6)
|
||||
+#define MT7620A_RESET_ESW BIT(23)
|
||||
+#define MT7620_L4_VALID BIT(23)
|
||||
+#define MT7621_L4_VALID BIT(24)
|
||||
+
|
||||
+#define MT7620_TX_DMA_UDF BIT(15)
|
||||
+#define MT7621_TX_DMA_UDF BIT(19)
|
||||
+#define TX_DMA_FP_BMAP ((0xff) << 19)
|
||||
+
|
||||
+#define CDMA_ICS_EN BIT(2)
|
||||
+#define CDMA_UCS_EN BIT(1)
|
||||
+#define CDMA_TCS_EN BIT(0)
|
||||
+
|
||||
+#define GDMA_ICS_EN BIT(22)
|
||||
+#define GDMA_TCS_EN BIT(21)
|
||||
+#define GDMA_UCS_EN BIT(20)
|
||||
+
|
||||
+/* frame engine counters */
|
||||
+#define MT7620_REG_MIB_OFFSET 0x1000
|
||||
+#define MT7620_PPE_AC_BCNT0 (MT7620_REG_MIB_OFFSET + 0x00)
|
||||
+#define MT7620_GDM1_TX_GBCNT (MT7620_REG_MIB_OFFSET + 0x300)
|
||||
+#define MT7620_GDM2_TX_GBCNT (MT7620_GDM1_TX_GBCNT + 0x40)
|
||||
+
|
||||
+#define MT7621_REG_MIB_OFFSET 0x2000
|
||||
+#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
|
||||
+#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
|
||||
+#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
|
||||
+
|
||||
+#define GSW_REG_GDMA1_MAC_ADRL 0x508
|
||||
+#define GSW_REG_GDMA1_MAC_ADRH 0x50C
|
||||
+
|
||||
+#define MT7621_FE_RST_GL (FE_FE_OFFSET + 0x04)
|
||||
+#define MT7620_FE_INT_STATUS2 (FE_FE_OFFSET + 0x08)
|
||||
+
|
||||
+/* FE_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
|
||||
+ * but after test it should be BIT(13).
|
||||
+ */
|
||||
+#define MT7620_FE_GDM1_AF BIT(13)
|
||||
+#define MT7621_FE_GDM1_AF BIT(28)
|
||||
+#define MT7621_FE_GDM2_AF BIT(29)
|
||||
+
|
||||
+static const u16 mt7620_reg_table[FE_REG_COUNT] = {
|
||||
+ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
|
||||
+ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
|
||||
+ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
|
||||
+ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
|
||||
+ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
|
||||
+ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
|
||||
+ [FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
|
||||
+ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
|
||||
+ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
|
||||
+ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
|
||||
+ [FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
|
||||
+ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
|
||||
+ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
|
||||
+ [FE_REG_FE_DMA_VID_BASE] = MT7620_DMA_VID,
|
||||
+ [FE_REG_FE_COUNTER_BASE] = MT7620_GDM1_TX_GBCNT,
|
||||
+ [FE_REG_FE_RST_GL] = MT7621_FE_RST_GL,
|
||||
+ [FE_REG_FE_INT_STATUS2] = MT7620_FE_INT_STATUS2,
|
||||
+};
|
||||
+
|
||||
+static int mt7620_gsw_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv;
|
||||
+
|
||||
+ /* is the mt7530 internal or external */
|
||||
+ if (priv->mii_bus && priv->mii_bus->phy_map[0x1f]) {
|
||||
+ mt7530_probe(priv->device, gsw->base, NULL, 0);
|
||||
+ mt7530_probe(priv->device, NULL, priv->mii_bus, 1);
|
||||
+ } else {
|
||||
+ mt7530_probe(priv->device, gsw->base, NULL, 1);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac)
|
||||
+{
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&priv->page_lock, flags);
|
||||
+ mtk_switch_w32(gsw, (mac[0] << 8) | mac[1], GSW_REG_SMACCR1);
|
||||
+ mtk_switch_w32(gsw, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
|
||||
+ GSW_REG_SMACCR0);
|
||||
+ spin_unlock_irqrestore(&priv->page_lock, flags);
|
||||
+}
|
||||
+
|
||||
+static void mt7620_auto_poll(struct mt7620_gsw *gsw)
|
||||
+{
|
||||
+ int phy;
|
||||
+ int lsb = -1, msb = 0;
|
||||
+
|
||||
+ for_each_set_bit(phy, &gsw->autopoll, 32) {
|
||||
+ if (lsb < 0)
|
||||
+ lsb = phy;
|
||||
+ msb = phy;
|
||||
+ }
|
||||
+
|
||||
+ if (lsb == msb)
|
||||
+ lsb--;
|
||||
+
|
||||
+ mtk_switch_w32(gsw, PHY_AN_EN | PHY_PRE_EN | PMY_MDC_CONF(5) |
|
||||
+ (msb << 8) | lsb, ESW_PHY_POLLING);
|
||||
+}
|
||||
+
|
||||
+static void mt7620_port_init(struct fe_priv *priv, struct device_node *np)
|
||||
+{
|
||||
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
|
||||
+ const __be32 *_id = of_get_property(np, "reg", NULL);
|
||||
+ int phy_mode, size, id;
|
||||
+ int shift = 12;
|
||||
+ u32 val, mask = 0;
|
||||
+ int min = (gsw->port4 == PORT4_EPHY) ? (5) : (4);
|
||||
+
|
||||
+ if (!_id || (be32_to_cpu(*_id) < min) || (be32_to_cpu(*_id) > 5)) {
|
||||
+ if (_id)
|
||||
+ pr_err("%s: invalid port id %d\n", np->name,
|
||||
+ be32_to_cpu(*_id));
|
||||
+ else
|
||||
+ pr_err("%s: invalid port id\n", np->name);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ id = be32_to_cpu(*_id);
|
||||
+
|
||||
+ if (id == 4)
|
||||
+ shift = 14;
|
||||
+
|
||||
+ priv->phy->phy_fixed[id] = of_get_property(np, "mediatek,fixed-link",
|
||||
+ &size);
|
||||
+ if (priv->phy->phy_fixed[id] &&
|
||||
+ (size != (4 * sizeof(*priv->phy->phy_fixed[id])))) {
|
||||
+ pr_err("%s: invalid fixed link property\n", np->name);
|
||||
+ priv->phy->phy_fixed[id] = NULL;
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ phy_mode = of_get_phy_mode(np);
|
||||
+ switch (phy_mode) {
|
||||
+ case PHY_INTERFACE_MODE_RGMII:
|
||||
+ mask = 0;
|
||||
+ break;
|
||||
+ case PHY_INTERFACE_MODE_MII:
|
||||
+ mask = 1;
|
||||
+ break;
|
||||
+ case PHY_INTERFACE_MODE_RMII:
|
||||
+ mask = 2;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(priv->device, "port %d - invalid phy mode\n", id);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ priv->phy->phy_node[id] = of_parse_phandle(np, "phy-handle", 0);
|
||||
+ if (!priv->phy->phy_node[id] && !priv->phy->phy_fixed[id])
|
||||
+ return;
|
||||
+
|
||||
+ val = rt_sysc_r32(SYSC_REG_CFG1);
|
||||
+ val &= ~(3 << shift);
|
||||
+ val |= mask << shift;
|
||||
+ rt_sysc_w32(val, SYSC_REG_CFG1);
|
||||
+
|
||||
+ if (priv->phy->phy_fixed[id]) {
|
||||
+ const __be32 *link = priv->phy->phy_fixed[id];
|
||||
+ int tx_fc, rx_fc;
|
||||
+ u32 val = 0;
|
||||
+
|
||||
+ priv->phy->speed[id] = be32_to_cpup(link++);
|
||||
+ tx_fc = be32_to_cpup(link++);
|
||||
+ rx_fc = be32_to_cpup(link++);
|
||||
+ priv->phy->duplex[id] = be32_to_cpup(link++);
|
||||
+ priv->link[id] = 1;
|
||||
+
|
||||
+ switch (priv->phy->speed[id]) {
|
||||
+ case SPEED_10:
|
||||
+ val = 0;
|
||||
+ break;
|
||||
+ case SPEED_100:
|
||||
+ val = 1;
|
||||
+ break;
|
||||
+ case SPEED_1000:
|
||||
+ val = 2;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(priv->device, "invalid link speed: %d\n",
|
||||
+ priv->phy->speed[id]);
|
||||
+ priv->phy->phy_fixed[id] = 0;
|
||||
+ return;
|
||||
+ }
|
||||
+ val = PMCR_SPEED(val);
|
||||
+ val |= PMCR_LINK | PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN |
|
||||
+ PMCR_TX_EN | PMCR_FORCE | PMCR_MAC_MODE | PMCR_IPG;
|
||||
+ if (tx_fc)
|
||||
+ val |= PMCR_TX_FC;
|
||||
+ if (rx_fc)
|
||||
+ val |= PMCR_RX_FC;
|
||||
+ if (priv->phy->duplex[id])
|
||||
+ val |= PMCR_DUPLEX;
|
||||
+ mtk_switch_w32(gsw, val, GSW_REG_PORT_PMCR(id));
|
||||
+ dev_info(priv->device, "using fixed link parameters\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if (priv->phy->phy_node[id] && priv->mii_bus->phy_map[id]) {
|
||||
+ u32 val = PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN |
|
||||
+ PMCR_TX_EN | PMCR_MAC_MODE | PMCR_IPG;
|
||||
+
|
||||
+ mtk_switch_w32(gsw, val, GSW_REG_PORT_PMCR(id));
|
||||
+ fe_connect_phy_node(priv, priv->phy->phy_node[id]);
|
||||
+ gsw->autopoll |= BIT(id);
|
||||
+ mt7620_auto_poll(gsw);
|
||||
+ return;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void mt7620_fe_reset(void)
|
||||
+{
|
||||
+ fe_reset(MT7620A_RESET_FE | MT7620A_RESET_ESW);
|
||||
+}
|
||||
+
|
||||
+static void mt7620_rxcsum_config(bool enable)
|
||||
+{
|
||||
+ if (enable)
|
||||
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) | (GDMA_ICS_EN |
|
||||
+ GDMA_TCS_EN | GDMA_UCS_EN),
|
||||
+ MT7620A_GDMA1_FWD_CFG);
|
||||
+ else
|
||||
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~(GDMA_ICS_EN |
|
||||
+ GDMA_TCS_EN | GDMA_UCS_EN),
|
||||
+ MT7620A_GDMA1_FWD_CFG);
|
||||
+}
|
||||
+
|
||||
+static void mt7620_txcsum_config(bool enable)
|
||||
+{
|
||||
+ if (enable)
|
||||
+ fe_w32(fe_r32(MT7620A_CDMA_CSG_CFG) | (CDMA_ICS_EN |
|
||||
+ CDMA_UCS_EN | CDMA_TCS_EN),
|
||||
+ MT7620A_CDMA_CSG_CFG);
|
||||
+ else
|
||||
+ fe_w32(fe_r32(MT7620A_CDMA_CSG_CFG) & ~(CDMA_ICS_EN |
|
||||
+ CDMA_UCS_EN | CDMA_TCS_EN),
|
||||
+ MT7620A_CDMA_CSG_CFG);
|
||||
+}
|
||||
+
|
||||
+static int mt7620_fwd_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct net_device *dev = priv_netdev(priv);
|
||||
+
|
||||
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~7, MT7620A_GDMA1_FWD_CFG);
|
||||
+
|
||||
+ mt7620_txcsum_config((dev->features & NETIF_F_IP_CSUM));
|
||||
+ mt7620_rxcsum_config((dev->features & NETIF_F_RXCSUM));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt7620_tx_dma(struct fe_tx_dma *txd)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static void mt7620_init_data(struct fe_soc_data *data,
|
||||
+ struct net_device *netdev)
|
||||
+{
|
||||
+ struct fe_priv *priv = netdev_priv(netdev);
|
||||
+
|
||||
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_RX_2B_OFFSET |
|
||||
+ FE_FLAG_RX_SG_DMA | FE_FLAG_HAS_SWITCH;
|
||||
+
|
||||
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
|
||||
+ NETIF_F_HW_VLAN_CTAG_TX;
|
||||
+ if (mt7620_get_eco() >= 5)
|
||||
+ netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
|
||||
+ NETIF_F_IPV6_CSUM;
|
||||
+}
|
||||
+
|
||||
+static struct fe_soc_data mt7620_data = {
|
||||
+ .init_data = mt7620_init_data,
|
||||
+ .reset_fe = mt7620_fe_reset,
|
||||
+ .set_mac = mt7620_set_mac,
|
||||
+ .fwd_config = mt7620_fwd_config,
|
||||
+ .tx_dma = mt7620_tx_dma,
|
||||
+ .switch_init = mtk_gsw_init,
|
||||
+ .port_init = mt7620_port_init,
|
||||
+ .reg_table = mt7620_reg_table,
|
||||
+ .pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
|
||||
+ .rx_int = RT5350_RX_DONE_INT,
|
||||
+ .tx_int = RT5350_TX_DONE_INT,
|
||||
+ .status_int = MT7620_FE_GDM1_AF,
|
||||
+ .checksum_bit = MT7620_L4_VALID,
|
||||
+ .has_carrier = mt7620_has_carrier,
|
||||
+ .mdio_read = mt7620_mdio_read,
|
||||
+ .mdio_write = mt7620_mdio_write,
|
||||
+ .mdio_adjust_link = mt7620_mdio_link_adjust,
|
||||
+};
|
||||
+
|
||||
+const struct of_device_id of_fe_match[] = {
|
||||
+ { .compatible = "mediatek,mt7620-eth", .data = &mt7620_data },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, of_fe_match);
|
|
@ -0,0 +1,210 @@
|
|||
From 107ff718dad1c8f6abbf6247d6796a4535b71276 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 23:50:53 +0100
|
||||
Subject: [PATCH 509/513] net-next: mediatek: add support for mt7621
|
||||
|
||||
Add support for SoCs from the mt7620 family. This include mt7620 and mt7621.
|
||||
These all have one dedicated external gbit port and a builtin 5 port 100mbit
|
||||
switch. Additionally one of the 5 switch ports can be changed to become an
|
||||
additional gbit port that we can attach a phy to. This patch includes
|
||||
rudimentary code to power up the switch. There are a lot of magic values
|
||||
that get written to the switch and the internal phys. These values come
|
||||
straight from the SDK driver.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/soc_mt7621.c | 186 ++++++++++++++++++++++++++++
|
||||
1 file changed, 186 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/soc_mt7621.c
|
||||
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/soc_mt7621.c
|
||||
@@ -0,0 +1,186 @@
|
||||
+/* This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License as published by
|
||||
+ * the Free Software Foundation; version 2 of the License
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
|
||||
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@openwrt.org>
|
||||
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include <linux/if_vlan.h>
|
||||
+#include <linux/of_net.h>
|
||||
+
|
||||
+#include <asm/mach-ralink/ralink_regs.h>
|
||||
+
|
||||
+#include "mtk_eth_soc.h"
|
||||
+#include "gsw_mt7620.h"
|
||||
+#include "mt7530.h"
|
||||
+#include "mdio.h"
|
||||
+
|
||||
+#define MT7620A_CDMA_CSG_CFG 0x400
|
||||
+#define MT7621_CDMP_IG_CTRL (MT7620A_CDMA_CSG_CFG + 0x00)
|
||||
+#define MT7621_CDMP_EG_CTRL (MT7620A_CDMA_CSG_CFG + 0x04)
|
||||
+#define MT7621_RESET_FE BIT(6)
|
||||
+#define MT7621_L4_VALID BIT(24)
|
||||
+
|
||||
+#define MT7621_TX_DMA_UDF BIT(19)
|
||||
+#define MT7621_TX_DMA_FPORT BIT(25)
|
||||
+
|
||||
+#define CDMA_ICS_EN BIT(2)
|
||||
+#define CDMA_UCS_EN BIT(1)
|
||||
+#define CDMA_TCS_EN BIT(0)
|
||||
+
|
||||
+#define GDMA_ICS_EN BIT(22)
|
||||
+#define GDMA_TCS_EN BIT(21)
|
||||
+#define GDMA_UCS_EN BIT(20)
|
||||
+
|
||||
+/* frame engine counters */
|
||||
+#define MT7621_REG_MIB_OFFSET 0x2000
|
||||
+#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
|
||||
+#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
|
||||
+#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
|
||||
+
|
||||
+#define GSW_REG_GDMA1_MAC_ADRL 0x508
|
||||
+#define GSW_REG_GDMA1_MAC_ADRH 0x50C
|
||||
+
|
||||
+#define MT7621_FE_RST_GL (FE_FE_OFFSET + 0x04)
|
||||
+#define MT7620_FE_INT_STATUS2 (FE_FE_OFFSET + 0x08)
|
||||
+
|
||||
+/* FE_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
|
||||
+ * but after test it should be BIT(13).
|
||||
+ */
|
||||
+#define MT7620_FE_GDM1_AF BIT(13)
|
||||
+#define MT7621_FE_GDM1_AF BIT(28)
|
||||
+#define MT7621_FE_GDM2_AF BIT(29)
|
||||
+
|
||||
+static const u16 mt7621_reg_table[FE_REG_COUNT] = {
|
||||
+ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
|
||||
+ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
|
||||
+ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
|
||||
+ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
|
||||
+ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
|
||||
+ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
|
||||
+ [FE_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
|
||||
+ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
|
||||
+ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
|
||||
+ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
|
||||
+ [FE_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
|
||||
+ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE,
|
||||
+ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS,
|
||||
+ [FE_REG_FE_DMA_VID_BASE] = 0,
|
||||
+ [FE_REG_FE_COUNTER_BASE] = MT7621_GDM1_TX_GBCNT,
|
||||
+ [FE_REG_FE_RST_GL] = MT7621_FE_RST_GL,
|
||||
+ [FE_REG_FE_INT_STATUS2] = MT7620_FE_INT_STATUS2,
|
||||
+};
|
||||
+
|
||||
+static int mt7621_gsw_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ if (priv->mii_bus && priv->mii_bus->phy_map[0x1f])
|
||||
+ mt7530_probe(priv->device, NULL, priv->mii_bus, 1);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt7621_fe_reset(void)
|
||||
+{
|
||||
+ fe_reset(MT7621_RESET_FE);
|
||||
+}
|
||||
+
|
||||
+static void mt7621_rxcsum_config(bool enable)
|
||||
+{
|
||||
+ if (enable)
|
||||
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) | (GDMA_ICS_EN |
|
||||
+ GDMA_TCS_EN | GDMA_UCS_EN),
|
||||
+ MT7620A_GDMA1_FWD_CFG);
|
||||
+ else
|
||||
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~(GDMA_ICS_EN |
|
||||
+ GDMA_TCS_EN | GDMA_UCS_EN),
|
||||
+ MT7620A_GDMA1_FWD_CFG);
|
||||
+}
|
||||
+
|
||||
+static void mt7621_rxvlan_config(bool enable)
|
||||
+{
|
||||
+ if (enable)
|
||||
+ fe_w32(1, MT7621_CDMP_EG_CTRL);
|
||||
+ else
|
||||
+ fe_w32(0, MT7621_CDMP_EG_CTRL);
|
||||
+}
|
||||
+
|
||||
+static int mt7621_fwd_config(struct fe_priv *priv)
|
||||
+{
|
||||
+ struct net_device *dev = priv_netdev(priv);
|
||||
+
|
||||
+ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~0xffff,
|
||||
+ MT7620A_GDMA1_FWD_CFG);
|
||||
+
|
||||
+ /* mt7621 doesn't have txcsum config */
|
||||
+ mt7621_rxcsum_config((dev->features & NETIF_F_RXCSUM));
|
||||
+ mt7621_rxvlan_config((dev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
|
||||
+ (priv->flags & FE_FLAG_RX_VLAN_CTAG));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mt7621_tx_dma(struct fe_tx_dma *txd)
|
||||
+{
|
||||
+ txd->txd4 = MT7621_TX_DMA_FPORT;
|
||||
+}
|
||||
+
|
||||
+static void mt7621_init_data(struct fe_soc_data *data,
|
||||
+ struct net_device *netdev)
|
||||
+{
|
||||
+ struct fe_priv *priv = netdev_priv(netdev);
|
||||
+
|
||||
+ priv->flags = FE_FLAG_PADDING_64B | FE_FLAG_RX_2B_OFFSET |
|
||||
+ FE_FLAG_RX_SG_DMA | FE_FLAG_NAPI_WEIGHT |
|
||||
+ FE_FLAG_HAS_SWITCH;
|
||||
+
|
||||
+ netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
|
||||
+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG | NETIF_F_TSO |
|
||||
+ NETIF_F_TSO6 | NETIF_F_IPV6_CSUM;
|
||||
+}
|
||||
+
|
||||
+static void mt7621_set_mac(struct fe_priv *priv, unsigned char *mac)
|
||||
+{
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&priv->page_lock, flags);
|
||||
+ fe_w32((mac[0] << 8) | mac[1], GSW_REG_GDMA1_MAC_ADRH);
|
||||
+ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5],
|
||||
+ GSW_REG_GDMA1_MAC_ADRL);
|
||||
+ spin_unlock_irqrestore(&priv->page_lock, flags);
|
||||
+}
|
||||
+
|
||||
+static struct fe_soc_data mt7621_data = {
|
||||
+ .init_data = mt7621_init_data,
|
||||
+ .reset_fe = mt7621_fe_reset,
|
||||
+ .set_mac = mt7621_set_mac,
|
||||
+ .fwd_config = mt7621_fwd_config,
|
||||
+ .tx_dma = mt7621_tx_dma,
|
||||
+ .switch_init = mtk_gsw_init,
|
||||
+ .switch_config = mt7621_gsw_config,
|
||||
+ .reg_table = mt7621_reg_table,
|
||||
+ .pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
|
||||
+ .rx_int = RT5350_RX_DONE_INT,
|
||||
+ .tx_int = RT5350_TX_DONE_INT,
|
||||
+ .status_int = (MT7621_FE_GDM1_AF | MT7621_FE_GDM2_AF),
|
||||
+ .checksum_bit = MT7621_L4_VALID,
|
||||
+ .has_carrier = mt7620_has_carrier,
|
||||
+ .mdio_read = mt7620_mdio_read,
|
||||
+ .mdio_write = mt7620_mdio_write,
|
||||
+ .mdio_adjust_link = mt7620_mdio_link_adjust,
|
||||
+};
|
||||
+
|
||||
+const struct of_device_id of_fe_match[] = {
|
||||
+ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
|
||||
+ {},
|
||||
+};
|
||||
+
|
||||
+MODULE_DEVICE_TABLE(of, of_fe_match);
|
|
@ -0,0 +1,127 @@
|
|||
From b6f779ea9c329451b89404583b45b9eb00155b32 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Wed, 18 Nov 2015 03:58:26 +0100
|
||||
Subject: [PATCH 510/513] net-next: mediatek: add Kconfig and Makefile
|
||||
|
||||
This patch adds the Makefile and Kconfig required to make the driver build.
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
Signed-off-by: Michael Lee <igvtee@gmail.com>
|
||||
---
|
||||
drivers/net/ethernet/Kconfig | 1 +
|
||||
drivers/net/ethernet/Makefile | 1 +
|
||||
drivers/net/ethernet/mediatek/Kconfig | 62 ++++++++++++++++++++++++++++++++
|
||||
drivers/net/ethernet/mediatek/Makefile | 20 +++++++++++
|
||||
4 files changed, 84 insertions(+)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/Kconfig
|
||||
create mode 100644 drivers/net/ethernet/mediatek/Makefile
|
||||
|
||||
--- a/drivers/net/ethernet/Kconfig
|
||||
+++ b/drivers/net/ethernet/Kconfig
|
||||
@@ -106,6 +106,7 @@ config LANTIQ_ETOP
|
||||
Support for the MII0 inside the Lantiq SoC
|
||||
|
||||
source "drivers/net/ethernet/marvell/Kconfig"
|
||||
+source "drivers/net/ethernet/mediatek/Kconfig"
|
||||
source "drivers/net/ethernet/mellanox/Kconfig"
|
||||
source "drivers/net/ethernet/micrel/Kconfig"
|
||||
source "drivers/net/ethernet/microchip/Kconfig"
|
||||
--- a/drivers/net/ethernet/Makefile
|
||||
+++ b/drivers/net/ethernet/Makefile
|
||||
@@ -46,6 +46,7 @@ obj-$(CONFIG_JME) += jme.o
|
||||
obj-$(CONFIG_KORINA) += korina.o
|
||||
obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o
|
||||
obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
|
||||
+obj-$(CONFIG_NET_VENDOR_MEDIATEK) += mediatek/
|
||||
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
|
||||
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
|
||||
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/Kconfig
|
||||
@@ -0,0 +1,62 @@
|
||||
+config NET_VENDOR_MEDIATEK
|
||||
+ tristate "Mediatek/Ralink ethernet driver"
|
||||
+ depends on RALINK
|
||||
+ help
|
||||
+ This driver supports the ethernet mac inside the Mediatek and Ralink WiSoCs
|
||||
+
|
||||
+config NET_MEDIATEK_SOC
|
||||
+ def_tristate NET_VENDOR_MEDIATEK
|
||||
+
|
||||
+if NET_MEDIATEK_SOC
|
||||
+choice
|
||||
+ prompt "MAC type"
|
||||
+
|
||||
+config NET_MEDIATEK_RT2880
|
||||
+ bool "RT2882"
|
||||
+ depends on MIPS && SOC_RT288X
|
||||
+
|
||||
+config NET_MEDIATEK_RT3050
|
||||
+ bool "RT3050/MT7628"
|
||||
+ depends on MIPS && (SOC_RT305X || SOC_MT7620)
|
||||
+
|
||||
+config NET_MEDIATEK_RT3883
|
||||
+ bool "RT3883"
|
||||
+ depends on MIPS && SOC_RT3883
|
||||
+
|
||||
+config NET_MEDIATEK_MT7620
|
||||
+ bool "MT7620"
|
||||
+ depends on MIPS && SOC_MT7620
|
||||
+
|
||||
+config NET_MEDIATEK_MT7621
|
||||
+ bool "MT7621"
|
||||
+ depends on MIPS && SOC_MT7621
|
||||
+
|
||||
+endchoice
|
||||
+
|
||||
+config NET_MEDIATEK_MDIO
|
||||
+ def_bool NET_MEDIATEK_SOC
|
||||
+ depends on (NET_MEDIATEK_RT2880 || NET_MEDIATEK_RT3883 || NET_MEDIATEK_MT7620 || NET_MEDIATEK_MT7621)
|
||||
+ select PHYLIB
|
||||
+
|
||||
+config NET_MEDIATEK_MDIO_RT2880
|
||||
+ def_bool NET_MEDIATEK_SOC
|
||||
+ depends on (NET_MEDIATEK_RT2880 || NET_MEDIATEK_RT3883)
|
||||
+ select NET_MEDIATEK_MDIO
|
||||
+
|
||||
+config NET_MEDIATEK_MDIO_MT7620
|
||||
+ def_bool NET_MEDIATEK_SOC
|
||||
+ depends on (NET_MEDIATEK_MT7620 || NET_MEDIATEK_MT7621)
|
||||
+ select NET_MEDIATEK_MDIO
|
||||
+
|
||||
+config NET_MEDIATEK_ESW_RT3050
|
||||
+ def_tristate NET_MEDIATEK_SOC
|
||||
+ depends on NET_MEDIATEK_RT3050
|
||||
+
|
||||
+config NET_MEDIATEK_GSW_MT7620
|
||||
+ def_tristate NET_MEDIATEK_SOC
|
||||
+ depends on NET_MEDIATEK_MT7620
|
||||
+
|
||||
+config NET_MEDIATEK_GSW_MT7621
|
||||
+ def_tristate NET_MEDIATEK_SOC
|
||||
+ depends on NET_MEDIATEK_MT7621
|
||||
+endif
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/Makefile
|
||||
@@ -0,0 +1,20 @@
|
||||
+#
|
||||
+# Makefile for the Ralink SoCs built-in ethernet macs
|
||||
+#
|
||||
+
|
||||
+mtk-eth-soc-y += mtk_eth_soc.o ethtool.o
|
||||
+
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO) += mdio.o
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_RT2880) += mdio_rt2880.o
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_MT7620) += mdio_mt7620.o
|
||||
+
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_RT2880) += soc_rt2880.o
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_RT3050) += soc_rt3050.o
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_RT3883) += soc_rt3883.o
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7620) += soc_mt7620.o
|
||||
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
|
||||
+
|
||||
+obj-$(CONFIG_NET_MEDIATEK_ESW_RT3050) += esw_rt3050.o
|
||||
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o
|
||||
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
|
||||
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk-eth-soc.o
|
|
@ -0,0 +1,80 @@
|
|||
From 6543b4cef96c12903f5ec5c015cd223a6b3c9a33 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 21:16:59 +0100
|
||||
Subject: [PATCH 511/513] net: mediatek: add support for the multiphy carrier
|
||||
patch
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/gsw_mt7620.c | 1 +
|
||||
drivers/net/ethernet/mediatek/gsw_mt7620.h | 1 +
|
||||
drivers/net/ethernet/mediatek/gsw_mt7621.c | 1 +
|
||||
drivers/net/ethernet/mediatek/mdio.c | 1 +
|
||||
drivers/net/ethernet/mediatek/mdio_mt7620.c | 12 ++++++++++++
|
||||
5 files changed, 16 insertions(+)
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/gsw_mt7620.c
|
||||
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.c
|
||||
@@ -54,6 +54,7 @@ static irqreturn_t gsw_interrupt_mt7620(
|
||||
|
||||
priv->link[i] = link;
|
||||
}
|
||||
+ mt7620_handle_carrier(priv);
|
||||
mtk_switch_w32(gsw, status, GSW_REG_ISR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
--- a/drivers/net/ethernet/mediatek/gsw_mt7620.h
|
||||
+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
|
||||
@@ -113,5 +113,6 @@ u32 mt7530_mdio_r32(struct mt7620_gsw *g
|
||||
u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
|
||||
u32 phy_register, u32 write_data);
|
||||
u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
|
||||
+void mt7620_handle_carrier(struct fe_priv *priv);
|
||||
|
||||
#endif
|
||||
--- a/drivers/net/ethernet/mediatek/gsw_mt7621.c
|
||||
+++ b/drivers/net/ethernet/mediatek/gsw_mt7621.c
|
||||
@@ -60,6 +60,7 @@ static irqreturn_t gsw_interrupt_mt7621(
|
||||
}
|
||||
}
|
||||
|
||||
+ mt7620_handle_carrier(priv);
|
||||
mt7530_mdio_w32(gsw, 0x700c, 0x1f);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
--- a/drivers/net/ethernet/mediatek/mdio.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mdio.c
|
||||
@@ -89,6 +89,7 @@ int fe_connect_phy_node(struct fe_priv *
|
||||
|
||||
phydev->supported &= PHY_GBIT_FEATURES;
|
||||
phydev->advertising = phydev->supported;
|
||||
+ phydev->no_auto_carrier_off = 1;
|
||||
|
||||
dev_info(priv->device,
|
||||
"connected port %d to PHY at %s [uid=%08x, driver=%s]\n",
|
||||
--- a/drivers/net/ethernet/mediatek/mdio_mt7620.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mdio_mt7620.c
|
||||
@@ -137,6 +137,17 @@ int mt7620_has_carrier(struct fe_priv *p
|
||||
}
|
||||
|
||||
|
||||
+void mt7620_handle_carrier(struct fe_priv *priv)
|
||||
+{
|
||||
+ if (!priv->phy)
|
||||
+ return;
|
||||
+
|
||||
+ if (mt7620_has_carrier(priv))
|
||||
+ netif_carrier_on(priv->netdev);
|
||||
+ else
|
||||
+ netif_carrier_off(priv->netdev);
|
||||
+}
|
||||
+
|
||||
void mt7620_print_link_state(struct fe_priv *priv, int port, int link,
|
||||
int speed, int duplex)
|
||||
{
|
||||
@@ -153,4 +164,5 @@ void mt7620_mdio_link_adjust(struct fe_p
|
||||
mt7620_print_link_state(priv, port, priv->link[port],
|
||||
priv->phy->speed[port],
|
||||
(priv->phy->duplex[port] == DUPLEX_FULL));
|
||||
+ mt7620_handle_carrier(priv);
|
||||
}
|
|
@ -0,0 +1,901 @@
|
|||
From 4473f30809eed09037e1932a0c1805172cd997f7 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 22:07:31 +0100
|
||||
Subject: [PATCH 512/513] net: mediatek: add swconfig driver for esw_rt3050
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/esw_rt3050.c | 805 ++++++++++++++++++++++++++++
|
||||
1 file changed, 805 insertions(+)
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/mach-ralink/ralink_regs.h>
|
||||
|
||||
+#include <linux/switch.h>
|
||||
+
|
||||
#include "mtk_eth_soc.h"
|
||||
|
||||
/* HW limitations for this switch:
|
||||
@@ -141,6 +143,8 @@
|
||||
#define RT305X_ESW_PORT5 5
|
||||
#define RT305X_ESW_PORT6 6
|
||||
|
||||
+#define RT305X_ESW_PORTS_NONE 0
|
||||
+
|
||||
#define RT305X_ESW_PMAP_LLLLLL 0x3f
|
||||
#define RT305X_ESW_PMAP_LLLLWL 0x2f
|
||||
#define RT305X_ESW_PMAP_WLLLLL 0x3e
|
||||
@@ -158,15 +162,51 @@
|
||||
#define RT305X_ESW_PORTS_ALL \
|
||||
(RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU)
|
||||
|
||||
+#define RT305X_ESW_NUM_VLANS 16
|
||||
+#define RT305X_ESW_NUM_VIDS 4096
|
||||
#define RT305X_ESW_NUM_PORTS 7
|
||||
+#define RT305X_ESW_NUM_LANWAN 6
|
||||
#define RT305X_ESW_NUM_LEDS 5
|
||||
|
||||
+#define RT5350_ESW_REG_PXTPC(_x) (0x150 + (4 * _x))
|
||||
#define RT5350_EWS_REG_LED_POLARITY 0x168
|
||||
#define RT5350_RESET_EPHY BIT(24)
|
||||
|
||||
+enum {
|
||||
+ /* Global attributes. */
|
||||
+ RT305X_ESW_ATTR_ENABLE_VLAN,
|
||||
+ RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
|
||||
+ RT305X_ESW_ATTR_BC_STATUS,
|
||||
+ RT305X_ESW_ATTR_LED_FREQ,
|
||||
+ /* Port attributes. */
|
||||
+ RT305X_ESW_ATTR_PORT_DISABLE,
|
||||
+ RT305X_ESW_ATTR_PORT_DOUBLETAG,
|
||||
+ RT305X_ESW_ATTR_PORT_UNTAG,
|
||||
+ RT305X_ESW_ATTR_PORT_LED,
|
||||
+ RT305X_ESW_ATTR_PORT_LAN,
|
||||
+ RT305X_ESW_ATTR_PORT_RECV_BAD,
|
||||
+ RT305X_ESW_ATTR_PORT_RECV_GOOD,
|
||||
+ RT5350_ESW_ATTR_PORT_TR_BAD,
|
||||
+ RT5350_ESW_ATTR_PORT_TR_GOOD,
|
||||
+};
|
||||
+
|
||||
struct esw_port {
|
||||
bool disable;
|
||||
+ bool doubletag;
|
||||
+ bool untag;
|
||||
u8 led;
|
||||
+ u16 pvid;
|
||||
+};
|
||||
+
|
||||
+struct esw_vlan {
|
||||
+ u8 ports;
|
||||
+ u16 vid;
|
||||
+};
|
||||
+
|
||||
+enum {
|
||||
+ RT305X_ESW_VLAN_CONFIG_NONE = 0,
|
||||
+ RT305X_ESW_VLAN_CONFIG_LLLLW,
|
||||
+ RT305X_ESW_VLAN_CONFIG_WLLLL,
|
||||
};
|
||||
|
||||
struct rt305x_esw {
|
||||
@@ -180,6 +220,12 @@ struct rt305x_esw {
|
||||
unsigned char port_map;
|
||||
unsigned int reg_led_polarity;
|
||||
|
||||
+ struct switch_dev swdev;
|
||||
+ bool global_vlan_enable;
|
||||
+ bool alt_vlan_disable;
|
||||
+ int bc_storm_protect;
|
||||
+ int led_frequency;
|
||||
+ struct esw_vlan vlans[RT305X_ESW_NUM_VLANS];
|
||||
struct esw_port ports[RT305X_ESW_NUM_PORTS];
|
||||
|
||||
};
|
||||
@@ -252,6 +298,71 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static unsigned esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan)
|
||||
+{
|
||||
+ unsigned s;
|
||||
+ unsigned val;
|
||||
+
|
||||
+ s = RT305X_ESW_VLANI_VID_S * (vlan % 2);
|
||||
+ val = esw_r32(esw, RT305X_ESW_REG_VLANI(vlan / 2));
|
||||
+ val = (val >> s) & RT305X_ESW_VLANI_VID_M;
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
+static void esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid)
|
||||
+{
|
||||
+ unsigned s;
|
||||
+
|
||||
+ s = RT305X_ESW_VLANI_VID_S * (vlan % 2);
|
||||
+ esw_rmw(esw,
|
||||
+ RT305X_ESW_REG_VLANI(vlan / 2),
|
||||
+ RT305X_ESW_VLANI_VID_M << s,
|
||||
+ (vid & RT305X_ESW_VLANI_VID_M) << s);
|
||||
+}
|
||||
+
|
||||
+static unsigned esw_get_pvid(struct rt305x_esw *esw, unsigned port)
|
||||
+{
|
||||
+ unsigned s, val;
|
||||
+
|
||||
+ s = RT305X_ESW_PVIDC_PVID_S * (port % 2);
|
||||
+ val = esw_r32(esw, RT305X_ESW_REG_PVIDC(port / 2));
|
||||
+ return (val >> s) & RT305X_ESW_PVIDC_PVID_M;
|
||||
+}
|
||||
+
|
||||
+static void esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid)
|
||||
+{
|
||||
+ unsigned s;
|
||||
+
|
||||
+ s = RT305X_ESW_PVIDC_PVID_S * (port % 2);
|
||||
+ esw_rmw(esw,
|
||||
+ RT305X_ESW_REG_PVIDC(port / 2),
|
||||
+ RT305X_ESW_PVIDC_PVID_M << s,
|
||||
+ (pvid & RT305X_ESW_PVIDC_PVID_M) << s);
|
||||
+}
|
||||
+
|
||||
+static unsigned esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan)
|
||||
+{
|
||||
+ unsigned s, val;
|
||||
+
|
||||
+ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4);
|
||||
+ val = esw_r32(esw, RT305X_ESW_REG_VMSC(vlan / 4));
|
||||
+ val = (val >> s) & RT305X_ESW_VMSC_MSC_M;
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
+static void esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc)
|
||||
+{
|
||||
+ unsigned s;
|
||||
+
|
||||
+ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4);
|
||||
+ esw_rmw(esw,
|
||||
+ RT305X_ESW_REG_VMSC(vlan / 4),
|
||||
+ RT305X_ESW_VMSC_MSC_M << s,
|
||||
+ (msc & RT305X_ESW_VMSC_MSC_M) << s);
|
||||
+}
|
||||
+
|
||||
static unsigned esw_get_port_disable(struct rt305x_esw *esw)
|
||||
{
|
||||
unsigned reg;
|
||||
@@ -261,6 +372,59 @@ static unsigned esw_get_port_disable(str
|
||||
RT305X_ESW_POC0_DIS_PORT_M;
|
||||
}
|
||||
|
||||
+static void esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask)
|
||||
+{
|
||||
+ unsigned old_mask;
|
||||
+ unsigned enable_mask;
|
||||
+ unsigned changed;
|
||||
+ int i;
|
||||
+
|
||||
+ old_mask = esw_get_port_disable(esw);
|
||||
+ changed = old_mask ^ disable_mask;
|
||||
+ enable_mask = old_mask & disable_mask;
|
||||
+
|
||||
+ /* enable before writing to MII */
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_POC0,
|
||||
+ (RT305X_ESW_POC0_DIS_PORT_M <<
|
||||
+ RT305X_ESW_POC0_DIS_PORT_S),
|
||||
+ enable_mask << RT305X_ESW_POC0_DIS_PORT_S);
|
||||
+
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) {
|
||||
+ if (!(changed & (1 << i)))
|
||||
+ continue;
|
||||
+ if (disable_mask & (1 << i)) {
|
||||
+ /* disable */
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR,
|
||||
+ BMCR_PDOWN);
|
||||
+ } else {
|
||||
+ /* enable */
|
||||
+ rt305x_mii_write(esw, i, MII_BMCR,
|
||||
+ BMCR_FULLDPLX |
|
||||
+ BMCR_ANENABLE |
|
||||
+ BMCR_ANRESTART |
|
||||
+ BMCR_SPEED100);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* disable after writing to MII */
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_POC0,
|
||||
+ (RT305X_ESW_POC0_DIS_PORT_M <<
|
||||
+ RT305X_ESW_POC0_DIS_PORT_S),
|
||||
+ disable_mask << RT305X_ESW_POC0_DIS_PORT_S);
|
||||
+}
|
||||
+
|
||||
+static void esw_set_gsc(struct rt305x_esw *esw)
|
||||
+{
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_SGC,
|
||||
+ RT305X_ESW_GSC_BC_STROM_MASK << RT305X_ESW_GSC_BC_STROM_SHIFT,
|
||||
+ esw->bc_storm_protect << RT305X_ESW_GSC_BC_STROM_SHIFT);
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_SGC,
|
||||
+ RT305X_ESW_GSC_LED_FREQ_MASK << RT305X_ESW_GSC_LED_FREQ_SHIFT,
|
||||
+ esw->led_frequency << RT305X_ESW_GSC_LED_FREQ_SHIFT);
|
||||
+}
|
||||
+
|
||||
+static int esw_apply_config(struct switch_dev *dev);
|
||||
+
|
||||
static void esw_hw_init(struct rt305x_esw *esw)
|
||||
{
|
||||
int i;
|
||||
@@ -519,6 +683,9 @@ static void esw_hw_init(struct rt305x_es
|
||||
for (i = 0; i < RT305X_ESW_NUM_LEDS; i++)
|
||||
esw->ports[i].led = 0x05;
|
||||
|
||||
+ /* Apply the empty config. */
|
||||
+ esw_apply_config(&esw->swdev);
|
||||
+
|
||||
/* Only unmask the port change interrupt */
|
||||
esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR);
|
||||
}
|
||||
@@ -541,11 +708,629 @@ static irqreturn_t esw_interrupt(int irq
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
+static int esw_apply_config(struct switch_dev *dev)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int i;
|
||||
+ u8 disable = 0;
|
||||
+ u8 doubletag = 0;
|
||||
+ u8 en_vlan = 0;
|
||||
+ u8 untag = 0;
|
||||
+
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
|
||||
+ u32 vid, vmsc;
|
||||
+ if (esw->global_vlan_enable) {
|
||||
+ vid = esw->vlans[i].vid;
|
||||
+ vmsc = esw->vlans[i].ports;
|
||||
+ } else {
|
||||
+ vid = RT305X_ESW_VLAN_NONE;
|
||||
+ vmsc = RT305X_ESW_PORTS_NONE;
|
||||
+ }
|
||||
+ esw_set_vlan_id(esw, i, vid);
|
||||
+ esw_set_vmsc(esw, i, vmsc);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
|
||||
+ u32 pvid;
|
||||
+ disable |= esw->ports[i].disable << i;
|
||||
+ if (esw->global_vlan_enable) {
|
||||
+ doubletag |= esw->ports[i].doubletag << i;
|
||||
+ en_vlan |= 1 << i;
|
||||
+ untag |= esw->ports[i].untag << i;
|
||||
+ pvid = esw->ports[i].pvid;
|
||||
+ } else {
|
||||
+ int x = esw->alt_vlan_disable ? 0 : 1;
|
||||
+ doubletag |= x << i;
|
||||
+ en_vlan |= x << i;
|
||||
+ untag |= x << i;
|
||||
+ pvid = 0;
|
||||
+ }
|
||||
+ esw_set_pvid(esw, i, pvid);
|
||||
+ if (i < RT305X_ESW_NUM_LEDS)
|
||||
+ esw_w32(esw, esw->ports[i].led,
|
||||
+ RT305X_ESW_REG_P0LED + 4*i);
|
||||
+ }
|
||||
+
|
||||
+ esw_set_gsc(esw);
|
||||
+ esw_set_port_disable(esw, disable);
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_SGC2,
|
||||
+ (RT305X_ESW_SGC2_DOUBLE_TAG_M <<
|
||||
+ RT305X_ESW_SGC2_DOUBLE_TAG_S),
|
||||
+ doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S);
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_PFC1,
|
||||
+ RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
|
||||
+ en_vlan << RT305X_ESW_PFC1_EN_VLAN_S);
|
||||
+ esw_rmw(esw, RT305X_ESW_REG_POC2,
|
||||
+ RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S,
|
||||
+ untag << RT305X_ESW_POC2_UNTAG_EN_S);
|
||||
+
|
||||
+ if (!esw->global_vlan_enable) {
|
||||
+ /*
|
||||
+ * Still need to put all ports into vlan 0 or they'll be
|
||||
+ * isolated.
|
||||
+ * NOTE: vlan 0 is special, no vlan tag is prepended
|
||||
+ */
|
||||
+ esw_set_vlan_id(esw, 0, 0);
|
||||
+ esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_reset_switch(struct switch_dev *dev)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ esw->global_vlan_enable = 0;
|
||||
+ memset(esw->ports, 0, sizeof(esw->ports));
|
||||
+ memset(esw->vlans, 0, sizeof(esw->vlans));
|
||||
+ esw_hw_init(esw);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_vlan_enable(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ val->value.i = esw->global_vlan_enable;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_set_vlan_enable(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ esw->global_vlan_enable = val->value.i != 0;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_alt_vlan_disable(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ val->value.i = esw->alt_vlan_disable;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_set_alt_vlan_disable(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ esw->alt_vlan_disable = val->value.i != 0;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+rt305x_esw_set_bc_status(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ esw->bc_storm_protect = val->value.i & RT305X_ESW_GSC_BC_STROM_MASK;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+rt305x_esw_get_bc_status(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ val->value.i = esw->bc_storm_protect;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+rt305x_esw_set_led_freq(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ esw->led_frequency = val->value.i & RT305X_ESW_GSC_LED_FREQ_MASK;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+rt305x_esw_get_led_freq(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ val->value.i = esw->led_frequency;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_port_link(struct switch_dev *dev,
|
||||
+ int port,
|
||||
+ struct switch_port_link *link)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ u32 speed, poa;
|
||||
+
|
||||
+ if (port < 0 || port >= RT305X_ESW_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ poa = esw_r32(esw, RT305X_ESW_REG_POA) >> port;
|
||||
+
|
||||
+ link->link = (poa >> RT305X_ESW_LINK_S) & 1;
|
||||
+ link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1;
|
||||
+ if (port < RT305X_ESW_NUM_LEDS) {
|
||||
+ speed = (poa >> RT305X_ESW_SPD_S) & 1;
|
||||
+ } else {
|
||||
+ if (port == RT305X_ESW_NUM_PORTS - 1)
|
||||
+ poa >>= 1;
|
||||
+ speed = (poa >> RT305X_ESW_SPD_S) & 3;
|
||||
+ }
|
||||
+ switch (speed) {
|
||||
+ case 0:
|
||||
+ link->speed = SWITCH_PORT_SPEED_10;
|
||||
+ break;
|
||||
+ case 1:
|
||||
+ link->speed = SWITCH_PORT_SPEED_100;
|
||||
+ break;
|
||||
+ case 2:
|
||||
+ case 3: /* forced gige speed can be 2 or 3 */
|
||||
+ link->speed = SWITCH_PORT_SPEED_1000;
|
||||
+ break;
|
||||
+ default:
|
||||
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_port_bool(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int idx = val->port_vlan;
|
||||
+ u32 x, reg, shift;
|
||||
+
|
||||
+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ switch (attr->id) {
|
||||
+ case RT305X_ESW_ATTR_PORT_DISABLE:
|
||||
+ reg = RT305X_ESW_REG_POC0;
|
||||
+ shift = RT305X_ESW_POC0_DIS_PORT_S;
|
||||
+ break;
|
||||
+ case RT305X_ESW_ATTR_PORT_DOUBLETAG:
|
||||
+ reg = RT305X_ESW_REG_SGC2;
|
||||
+ shift = RT305X_ESW_SGC2_DOUBLE_TAG_S;
|
||||
+ break;
|
||||
+ case RT305X_ESW_ATTR_PORT_UNTAG:
|
||||
+ reg = RT305X_ESW_REG_POC2;
|
||||
+ shift = RT305X_ESW_POC2_UNTAG_EN_S;
|
||||
+ break;
|
||||
+ case RT305X_ESW_ATTR_PORT_LAN:
|
||||
+ reg = RT305X_ESW_REG_SGC2;
|
||||
+ shift = RT305X_ESW_SGC2_LAN_PMAP_S;
|
||||
+ if (idx >= RT305X_ESW_NUM_LANWAN)
|
||||
+ return -EINVAL;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ x = esw_r32(esw, reg);
|
||||
+ val->value.i = (x >> (idx + shift)) & 1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_set_port_bool(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int idx = val->port_vlan;
|
||||
+
|
||||
+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS ||
|
||||
+ val->value.i < 0 || val->value.i > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ switch (attr->id) {
|
||||
+ case RT305X_ESW_ATTR_PORT_DISABLE:
|
||||
+ esw->ports[idx].disable = val->value.i;
|
||||
+ break;
|
||||
+ case RT305X_ESW_ATTR_PORT_DOUBLETAG:
|
||||
+ esw->ports[idx].doubletag = val->value.i;
|
||||
+ break;
|
||||
+ case RT305X_ESW_ATTR_PORT_UNTAG:
|
||||
+ esw->ports[idx].untag = val->value.i;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_port_recv_badgood(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int idx = val->port_vlan;
|
||||
+ int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16;
|
||||
+ u32 reg;
|
||||
+
|
||||
+ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
|
||||
+ return -EINVAL;
|
||||
+ reg = esw_r32(esw, RT305X_ESW_REG_PXPC(idx));
|
||||
+ val->value.i = (reg >> shift) & 0xffff;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+esw_get_port_tr_badgood(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ int idx = val->port_vlan;
|
||||
+ int shift = attr->id == RT5350_ESW_ATTR_PORT_TR_GOOD ? 0 : 16;
|
||||
+ u32 reg;
|
||||
+
|
||||
+ if ((ralink_soc != RT305X_SOC_RT5350) && (ralink_soc != MT762X_SOC_MT7628AN) && (ralink_soc != MT762X_SOC_MT7688))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = esw_r32(esw, RT5350_ESW_REG_PXTPC(idx));
|
||||
+ val->value.i = (reg >> shift) & 0xffff;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_port_led(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int idx = val->port_vlan;
|
||||
+
|
||||
+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS ||
|
||||
+ idx >= RT305X_ESW_NUM_LEDS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ val->value.i = esw_r32(esw, RT305X_ESW_REG_P0LED + 4*idx);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_set_port_led(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int idx = val->port_vlan;
|
||||
+
|
||||
+ if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ esw->ports[idx].led = val->value.i;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_port_pvid(struct switch_dev *dev, int port, int *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ if (port >= RT305X_ESW_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *val = esw_get_pvid(esw, port);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_set_port_pvid(struct switch_dev *dev, int port, int val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+
|
||||
+ if (port >= RT305X_ESW_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ esw->ports[port].pvid = val;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ u32 vmsc, poc2;
|
||||
+ int vlan_idx = -1;
|
||||
+ int i;
|
||||
+
|
||||
+ val->len = 0;
|
||||
+
|
||||
+ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* valid vlan? */
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
|
||||
+ if (esw_get_vlan_id(esw, i) == val->port_vlan &&
|
||||
+ esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) {
|
||||
+ vlan_idx = i;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (vlan_idx == -1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ vmsc = esw_get_vmsc(esw, vlan_idx);
|
||||
+ poc2 = esw_r32(esw, RT305X_ESW_REG_POC2);
|
||||
+
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
|
||||
+ struct switch_port *p;
|
||||
+ int port_mask = 1 << i;
|
||||
+
|
||||
+ if (!(vmsc & port_mask))
|
||||
+ continue;
|
||||
+
|
||||
+ p = &val->value.ports[val->len++];
|
||||
+ p->id = i;
|
||||
+ if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S))
|
||||
+ p->flags = 0;
|
||||
+ else
|
||||
+ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
||||
+{
|
||||
+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
|
||||
+ int ports;
|
||||
+ int vlan_idx = -1;
|
||||
+ int i;
|
||||
+
|
||||
+ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS ||
|
||||
+ val->len > RT305X_ESW_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* one of the already defined vlans? */
|
||||
+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
|
||||
+ if (esw->vlans[i].vid == val->port_vlan &&
|
||||
+ esw->vlans[i].ports != RT305X_ESW_PORTS_NONE) {
|
||||
+ vlan_idx = i;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* select a free slot */
|
||||
+ for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) {
|
||||
+ if (esw->vlans[i].ports == RT305X_ESW_PORTS_NONE)
|
||||
+ vlan_idx = i;
|
||||
+ }
|
||||
+
|
||||
+ /* bail if all slots are in use */
|
||||
+ if (vlan_idx == -1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ports = RT305X_ESW_PORTS_NONE;
|
||||
+ for (i = 0; i < val->len; i++) {
|
||||
+ struct switch_port *p = &val->value.ports[i];
|
||||
+ int port_mask = 1 << p->id;
|
||||
+ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED));
|
||||
+
|
||||
+ if (p->id >= RT305X_ESW_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ports |= port_mask;
|
||||
+ esw->ports[p->id].untag = untagged;
|
||||
+ }
|
||||
+ esw->vlans[vlan_idx].ports = ports;
|
||||
+ if (ports == RT305X_ESW_PORTS_NONE)
|
||||
+ esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE;
|
||||
+ else
|
||||
+ esw->vlans[vlan_idx].vid = val->port_vlan;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct switch_attr esw_global[] = {
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "enable_vlan",
|
||||
+ .description = "VLAN mode (1:enabled)",
|
||||
+ .max = 1,
|
||||
+ .id = RT305X_ESW_ATTR_ENABLE_VLAN,
|
||||
+ .get = esw_get_vlan_enable,
|
||||
+ .set = esw_set_vlan_enable,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "alternate_vlan_disable",
|
||||
+ .description = "Use en_vlan instead of doubletag to disable"
|
||||
+ " VLAN mode",
|
||||
+ .max = 1,
|
||||
+ .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
|
||||
+ .get = esw_get_alt_vlan_disable,
|
||||
+ .set = esw_set_alt_vlan_disable,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "bc_storm_protect",
|
||||
+ .description = "Global broadcast storm protection (0:Disable, 1:64 blocks, 2:96 blocks, 3:128 blocks)",
|
||||
+ .max = 3,
|
||||
+ .id = RT305X_ESW_ATTR_BC_STATUS,
|
||||
+ .get = rt305x_esw_get_bc_status,
|
||||
+ .set = rt305x_esw_set_bc_status,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "led_frequency",
|
||||
+ .description = "LED Flash frequency (0:30mS, 1:60mS, 2:240mS, 3:480mS)",
|
||||
+ .max = 3,
|
||||
+ .id = RT305X_ESW_ATTR_LED_FREQ,
|
||||
+ .get = rt305x_esw_get_led_freq,
|
||||
+ .set = rt305x_esw_set_led_freq,
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+static const struct switch_attr esw_port[] = {
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "disable",
|
||||
+ .description = "Port state (1:disabled)",
|
||||
+ .max = 1,
|
||||
+ .id = RT305X_ESW_ATTR_PORT_DISABLE,
|
||||
+ .get = esw_get_port_bool,
|
||||
+ .set = esw_set_port_bool,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "doubletag",
|
||||
+ .description = "Double tagging for incoming vlan packets "
|
||||
+ "(1:enabled)",
|
||||
+ .max = 1,
|
||||
+ .id = RT305X_ESW_ATTR_PORT_DOUBLETAG,
|
||||
+ .get = esw_get_port_bool,
|
||||
+ .set = esw_set_port_bool,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "untag",
|
||||
+ .description = "Untag (1:strip outgoing vlan tag)",
|
||||
+ .max = 1,
|
||||
+ .id = RT305X_ESW_ATTR_PORT_UNTAG,
|
||||
+ .get = esw_get_port_bool,
|
||||
+ .set = esw_set_port_bool,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "led",
|
||||
+ .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity,"
|
||||
+ " 4:collision, 5:linkact, 6:duplcoll, 7:10mact,"
|
||||
+ " 8:100mact, 10:blink, 11:off, 12:on)",
|
||||
+ .max = 15,
|
||||
+ .id = RT305X_ESW_ATTR_PORT_LED,
|
||||
+ .get = esw_get_port_led,
|
||||
+ .set = esw_set_port_led,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "lan",
|
||||
+ .description = "HW port group (0:wan, 1:lan)",
|
||||
+ .max = 1,
|
||||
+ .id = RT305X_ESW_ATTR_PORT_LAN,
|
||||
+ .get = esw_get_port_bool,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "recv_bad",
|
||||
+ .description = "Receive bad packet counter",
|
||||
+ .id = RT305X_ESW_ATTR_PORT_RECV_BAD,
|
||||
+ .get = esw_get_port_recv_badgood,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "recv_good",
|
||||
+ .description = "Receive good packet counter",
|
||||
+ .id = RT305X_ESW_ATTR_PORT_RECV_GOOD,
|
||||
+ .get = esw_get_port_recv_badgood,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "tr_bad",
|
||||
+
|
||||
+ .description = "Transmit bad packet counter. rt5350 only",
|
||||
+ .id = RT5350_ESW_ATTR_PORT_TR_BAD,
|
||||
+ .get = esw_get_port_tr_badgood,
|
||||
+ },
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "tr_good",
|
||||
+
|
||||
+ .description = "Transmit good packet counter. rt5350 only",
|
||||
+ .id = RT5350_ESW_ATTR_PORT_TR_GOOD,
|
||||
+ .get = esw_get_port_tr_badgood,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct switch_attr esw_vlan[] = {
|
||||
+};
|
||||
+
|
||||
+static const struct switch_dev_ops esw_ops = {
|
||||
+ .attr_global = {
|
||||
+ .attr = esw_global,
|
||||
+ .n_attr = ARRAY_SIZE(esw_global),
|
||||
+ },
|
||||
+ .attr_port = {
|
||||
+ .attr = esw_port,
|
||||
+ .n_attr = ARRAY_SIZE(esw_port),
|
||||
+ },
|
||||
+ .attr_vlan = {
|
||||
+ .attr = esw_vlan,
|
||||
+ .n_attr = ARRAY_SIZE(esw_vlan),
|
||||
+ },
|
||||
+ .get_vlan_ports = esw_get_vlan_ports,
|
||||
+ .set_vlan_ports = esw_set_vlan_ports,
|
||||
+ .get_port_pvid = esw_get_port_pvid,
|
||||
+ .set_port_pvid = esw_set_port_pvid,
|
||||
+ .get_port_link = esw_get_port_link,
|
||||
+ .apply_config = esw_apply_config,
|
||||
+ .reset_switch = esw_reset_switch,
|
||||
+};
|
||||
+
|
||||
static int esw_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const __be32 *port_map, *reg_init;
|
||||
+ struct switch_dev *swdev;
|
||||
struct rt305x_esw *esw;
|
||||
struct resource *irq;
|
||||
int ret;
|
||||
@@ -568,6 +1353,21 @@ static int esw_probe(struct platform_dev
|
||||
if (reg_init)
|
||||
esw->reg_led_polarity = be32_to_cpu(*reg_init);
|
||||
|
||||
+ swdev = &esw->swdev;
|
||||
+ swdev->of_node = pdev->dev.of_node;
|
||||
+ swdev->name = "rt305x-esw";
|
||||
+ swdev->alias = "rt305x";
|
||||
+ swdev->cpu_port = RT305X_ESW_PORT6;
|
||||
+ swdev->ports = RT305X_ESW_NUM_PORTS;
|
||||
+ swdev->vlans = RT305X_ESW_NUM_VIDS;
|
||||
+ swdev->ops = &esw_ops;
|
||||
+
|
||||
+ ret = register_switch(swdev, NULL);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(&pdev->dev, "register_switch failed\n");
|
||||
+ goto unmap_base;
|
||||
+ }
|
||||
+
|
||||
platform_set_drvdata(pdev, esw);
|
||||
|
||||
spin_lock_init(&esw->reg_rw_lock);
|
||||
@@ -583,6 +1383,11 @@ static int esw_probe(struct platform_dev
|
||||
}
|
||||
|
||||
return ret;
|
||||
+
|
||||
+unmap_base:
|
||||
+ iounmap(esw->base);
|
||||
+ kfree(esw);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int esw_remove(struct platform_device *pdev)
|
|
@ -0,0 +1,896 @@
|
|||
From cf5a08f1f16913da8bb24a96afaa2969b29d0827 Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Mon, 14 Dec 2015 22:25:57 +0100
|
||||
Subject: [PATCH 513/513] net: mediatek: add swconfig driver for gsw_mt762x
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/Makefile | 4 +-
|
||||
drivers/net/ethernet/mediatek/mt7530.c | 804 +++++++++++++++++++++++++++
|
||||
drivers/net/ethernet/mediatek/mt7530.h | 20 +
|
||||
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 9 +-
|
||||
drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 +
|
||||
drivers/net/ethernet/mediatek/soc_mt7620.c | 1 +
|
||||
6 files changed, 835 insertions(+), 4 deletions(-)
|
||||
create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
|
||||
create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/Makefile
|
||||
+++ b/drivers/net/ethernet/mediatek/Makefile
|
||||
@@ -15,6 +15,6 @@ mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7620
|
||||
mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
|
||||
|
||||
obj-$(CONFIG_NET_MEDIATEK_ESW_RT3050) += esw_rt3050.o
|
||||
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o
|
||||
-obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
|
||||
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7620) += gsw_mt7620.o mt7530.o
|
||||
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o mt7530.o
|
||||
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk-eth-soc.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/mt7530.c
|
||||
@@ -0,0 +1,804 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2
|
||||
+ * of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/if.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/if_ether.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/netlink.h>
|
||||
+#include <linux/bitops.h>
|
||||
+#include <net/genetlink.h>
|
||||
+#include <linux/switch.h>
|
||||
+#include <linux/delay.h>
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/etherdevice.h>
|
||||
+#include <linux/lockdep.h>
|
||||
+#include <linux/workqueue.h>
|
||||
+#include <linux/of_device.h>
|
||||
+
|
||||
+#include "mt7530.h"
|
||||
+
|
||||
+#define MT7530_CPU_PORT 6
|
||||
+#define MT7530_NUM_PORTS 8
|
||||
+#define MT7530_NUM_VLANS 16
|
||||
+#define MT7530_MAX_VID 4095
|
||||
+#define MT7530_MIN_VID 0
|
||||
+
|
||||
+/* registers */
|
||||
+#define REG_ESW_VLAN_VTCR 0x90
|
||||
+#define REG_ESW_VLAN_VAWD1 0x94
|
||||
+#define REG_ESW_VLAN_VAWD2 0x98
|
||||
+#define REG_ESW_VLAN_VTIM(x) (0x100 + 4 * ((x) / 2))
|
||||
+
|
||||
+#define REG_ESW_VLAN_VAWD1_IVL_MAC BIT(30)
|
||||
+#define REG_ESW_VLAN_VAWD1_VTAG_EN BIT(28)
|
||||
+#define REG_ESW_VLAN_VAWD1_VALID BIT(0)
|
||||
+
|
||||
+/* vlan egress mode */
|
||||
+enum {
|
||||
+ ETAG_CTRL_UNTAG = 0,
|
||||
+ ETAG_CTRL_TAG = 2,
|
||||
+ ETAG_CTRL_SWAP = 1,
|
||||
+ ETAG_CTRL_STACK = 3,
|
||||
+};
|
||||
+
|
||||
+#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
|
||||
+#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
|
||||
+#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
|
||||
+
|
||||
+#define REG_HWTRAP 0x7804
|
||||
+
|
||||
+#define MIB_DESC(_s , _o, _n) \
|
||||
+ { \
|
||||
+ .size = (_s), \
|
||||
+ .offset = (_o), \
|
||||
+ .name = (_n), \
|
||||
+ }
|
||||
+
|
||||
+struct mt7xxx_mib_desc {
|
||||
+ unsigned int size;
|
||||
+ unsigned int offset;
|
||||
+ const char *name;
|
||||
+};
|
||||
+
|
||||
+#define MT7621_MIB_COUNTER_BASE 0x4000
|
||||
+#define MT7621_MIB_COUNTER_PORT_OFFSET 0x100
|
||||
+#define MT7621_STATS_TDPC 0x00
|
||||
+#define MT7621_STATS_TCRC 0x04
|
||||
+#define MT7621_STATS_TUPC 0x08
|
||||
+#define MT7621_STATS_TMPC 0x0C
|
||||
+#define MT7621_STATS_TBPC 0x10
|
||||
+#define MT7621_STATS_TCEC 0x14
|
||||
+#define MT7621_STATS_TSCEC 0x18
|
||||
+#define MT7621_STATS_TMCEC 0x1C
|
||||
+#define MT7621_STATS_TDEC 0x20
|
||||
+#define MT7621_STATS_TLCEC 0x24
|
||||
+#define MT7621_STATS_TXCEC 0x28
|
||||
+#define MT7621_STATS_TPPC 0x2C
|
||||
+#define MT7621_STATS_TL64PC 0x30
|
||||
+#define MT7621_STATS_TL65PC 0x34
|
||||
+#define MT7621_STATS_TL128PC 0x38
|
||||
+#define MT7621_STATS_TL256PC 0x3C
|
||||
+#define MT7621_STATS_TL512PC 0x40
|
||||
+#define MT7621_STATS_TL1024PC 0x44
|
||||
+#define MT7621_STATS_TOC 0x48
|
||||
+#define MT7621_STATS_RDPC 0x60
|
||||
+#define MT7621_STATS_RFPC 0x64
|
||||
+#define MT7621_STATS_RUPC 0x68
|
||||
+#define MT7621_STATS_RMPC 0x6C
|
||||
+#define MT7621_STATS_RBPC 0x70
|
||||
+#define MT7621_STATS_RAEPC 0x74
|
||||
+#define MT7621_STATS_RCEPC 0x78
|
||||
+#define MT7621_STATS_RUSPC 0x7C
|
||||
+#define MT7621_STATS_RFEPC 0x80
|
||||
+#define MT7621_STATS_ROSPC 0x84
|
||||
+#define MT7621_STATS_RJEPC 0x88
|
||||
+#define MT7621_STATS_RPPC 0x8C
|
||||
+#define MT7621_STATS_RL64PC 0x90
|
||||
+#define MT7621_STATS_RL65PC 0x94
|
||||
+#define MT7621_STATS_RL128PC 0x98
|
||||
+#define MT7621_STATS_RL256PC 0x9C
|
||||
+#define MT7621_STATS_RL512PC 0xA0
|
||||
+#define MT7621_STATS_RL1024PC 0xA4
|
||||
+#define MT7621_STATS_ROC 0xA8
|
||||
+#define MT7621_STATS_RDPC_CTRL 0xB0
|
||||
+#define MT7621_STATS_RDPC_ING 0xB4
|
||||
+#define MT7621_STATS_RDPC_ARL 0xB8
|
||||
+
|
||||
+static const struct mt7xxx_mib_desc mt7621_mibs[] = {
|
||||
+ MIB_DESC(1, MT7621_STATS_TDPC, "TxDrop"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TCRC, "TxCRC"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TUPC, "TxUni"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TMPC, "TxMulti"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TBPC, "TxBroad"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TCEC, "TxCollision"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TSCEC, "TxSingleCol"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TMCEC, "TxMultiCol"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TDEC, "TxDefer"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TLCEC, "TxLateCol"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TXCEC, "TxExcCol"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TPPC, "TxPause"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TL64PC, "Tx64Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TL65PC, "Tx65Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TL128PC, "Tx128Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TL256PC, "Tx256Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TL512PC, "Tx512Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_TL1024PC, "Tx1024Byte"),
|
||||
+ MIB_DESC(2, MT7621_STATS_TOC, "TxByte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RDPC, "RxDrop"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RFPC, "RxFiltered"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RUPC, "RxUni"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RMPC, "RxMulti"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RBPC, "RxBroad"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RAEPC, "RxAlignErr"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RCEPC, "RxCRC"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RUSPC, "RxUnderSize"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RFEPC, "RxFragment"),
|
||||
+ MIB_DESC(1, MT7621_STATS_ROSPC, "RxOverSize"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RJEPC, "RxJabber"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RPPC, "RxPause"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RL64PC, "Rx64Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RL65PC, "Rx65Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RL128PC, "Rx128Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RL256PC, "Rx256Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RL512PC, "Rx512Byte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RL1024PC, "Rx1024Byte"),
|
||||
+ MIB_DESC(2, MT7621_STATS_ROC, "RxByte"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RDPC_CTRL, "RxCtrlDrop"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RDPC_ING, "RxIngDrop"),
|
||||
+ MIB_DESC(1, MT7621_STATS_RDPC_ARL, "RxARLDrop")
|
||||
+};
|
||||
+
|
||||
+enum {
|
||||
+ /* Global attributes. */
|
||||
+ MT7530_ATTR_ENABLE_VLAN,
|
||||
+};
|
||||
+
|
||||
+struct mt7530_port_entry {
|
||||
+ u16 pvid;
|
||||
+};
|
||||
+
|
||||
+struct mt7530_vlan_entry {
|
||||
+ u16 vid;
|
||||
+ u8 member;
|
||||
+ u8 etags;
|
||||
+};
|
||||
+
|
||||
+struct mt7530_priv {
|
||||
+ void __iomem *base;
|
||||
+ struct mii_bus *bus;
|
||||
+ struct switch_dev swdev;
|
||||
+
|
||||
+ bool global_vlan_enable;
|
||||
+ struct mt7530_vlan_entry vlan_entries[MT7530_NUM_VLANS];
|
||||
+ struct mt7530_port_entry port_entries[MT7530_NUM_PORTS];
|
||||
+};
|
||||
+
|
||||
+struct mt7530_mapping {
|
||||
+ char *name;
|
||||
+ u16 pvids[MT7530_NUM_PORTS];
|
||||
+ u8 members[MT7530_NUM_VLANS];
|
||||
+ u8 etags[MT7530_NUM_VLANS];
|
||||
+ u16 vids[MT7530_NUM_VLANS];
|
||||
+} mt7530_defaults[] = {
|
||||
+ {
|
||||
+ .name = "llllw",
|
||||
+ .pvids = { 1, 1, 1, 1, 2, 1, 1 },
|
||||
+ .members = { 0, 0x6f, 0x50 },
|
||||
+ .etags = { 0, 0x40, 0x40 },
|
||||
+ .vids = { 0, 1, 2 },
|
||||
+ }, {
|
||||
+ .name = "wllll",
|
||||
+ .pvids = { 2, 1, 1, 1, 1, 1, 1 },
|
||||
+ .members = { 0, 0x7e, 0x41 },
|
||||
+ .etags = { 0, 0x40, 0x40 },
|
||||
+ .vids = { 0, 1, 2 },
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+struct mt7530_mapping*
|
||||
+mt7530_find_mapping(struct device_node *np)
|
||||
+{
|
||||
+ const char *map;
|
||||
+ int i;
|
||||
+
|
||||
+ if (of_property_read_string(np, "mediatek,portmap", &map))
|
||||
+ return NULL;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(mt7530_defaults); i++)
|
||||
+ if (!strcmp(map, mt7530_defaults[i].name))
|
||||
+ return &mt7530_defaults[i];
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mt7530_apply_mapping(struct mt7530_priv *mt7530, struct mt7530_mapping *map)
|
||||
+{
|
||||
+ int i = 0;
|
||||
+
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
||||
+ mt7530->port_entries[i].pvid = map->pvids[i];
|
||||
+
|
||||
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
||||
+ mt7530->vlan_entries[i].member = map->members[i];
|
||||
+ mt7530->vlan_entries[i].etags = map->etags[i];
|
||||
+ mt7530->vlan_entries[i].vid = map->vids[i];
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_reset_switch(struct switch_dev *dev)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ int i;
|
||||
+
|
||||
+ memset(priv->port_entries, 0, sizeof(priv->port_entries));
|
||||
+ memset(priv->vlan_entries, 0, sizeof(priv->vlan_entries));
|
||||
+
|
||||
+ /* set default vid of each vlan to the same number of vlan, so the vid
|
||||
+ * won't need be set explicitly.
|
||||
+ */
|
||||
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
||||
+ priv->vlan_entries[i].vid = i;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_get_vlan_enable(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+
|
||||
+ val->value.i = priv->global_vlan_enable;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_set_vlan_enable(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+
|
||||
+ priv->global_vlan_enable = val->value.i != 0;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static u32
|
||||
+mt7530_r32(struct mt7530_priv *priv, u32 reg)
|
||||
+{
|
||||
+ u32 val;
|
||||
+ if (priv->bus) {
|
||||
+ u16 high, low;
|
||||
+
|
||||
+ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
||||
+ low = mdiobus_read(priv->bus, 0x1f, (reg >> 2) & 0xf);
|
||||
+ high = mdiobus_read(priv->bus, 0x1f, 0x10);
|
||||
+
|
||||
+ return (high << 16) | (low & 0xffff);
|
||||
+ }
|
||||
+
|
||||
+ val = ioread32(priv->base + reg);
|
||||
+ pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mt7530_w32(struct mt7530_priv *priv, u32 reg, u32 val)
|
||||
+{
|
||||
+ if (priv->bus) {
|
||||
+ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
|
||||
+ mdiobus_write(priv->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
|
||||
+ mdiobus_write(priv->bus, 0x1f, 0x10, val >> 16);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
|
||||
+ iowrite32(val, priv->base + reg);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+mt7530_vtcr(struct mt7530_priv *priv, u32 cmd, u32 val)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ mt7530_w32(priv, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
|
||||
+
|
||||
+ for (i = 0; i < 20; i++) {
|
||||
+ u32 val = mt7530_r32(priv, REG_ESW_VLAN_VTCR);
|
||||
+
|
||||
+ if ((val & BIT(31)) == 0)
|
||||
+ break;
|
||||
+
|
||||
+ udelay(1000);
|
||||
+ }
|
||||
+ if (i == 20)
|
||||
+ printk("mt7530: vtcr timeout\n");
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_get_port_pvid(struct switch_dev *dev, int port, int *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+
|
||||
+ if (port >= MT7530_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(port));
|
||||
+ *val &= 0xfff;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_set_port_pvid(struct switch_dev *dev, int port, int pvid)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+
|
||||
+ if (port >= MT7530_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (pvid < MT7530_MIN_VID || pvid > MT7530_MAX_VID)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ priv->port_entries[port].pvid = pvid;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ u32 member;
|
||||
+ u32 etags;
|
||||
+ int i;
|
||||
+
|
||||
+ val->len = 0;
|
||||
+
|
||||
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ mt7530_vtcr(priv, 0, val->port_vlan);
|
||||
+
|
||||
+ member = mt7530_r32(priv, REG_ESW_VLAN_VAWD1);
|
||||
+ member >>= 16;
|
||||
+ member &= 0xff;
|
||||
+
|
||||
+ etags = mt7530_r32(priv, REG_ESW_VLAN_VAWD2);
|
||||
+
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
|
||||
+ struct switch_port *p;
|
||||
+ int etag;
|
||||
+
|
||||
+ if (!(member & BIT(i)))
|
||||
+ continue;
|
||||
+
|
||||
+ p = &val->value.ports[val->len++];
|
||||
+ p->id = i;
|
||||
+
|
||||
+ etag = (etags >> (i * 2)) & 0x3;
|
||||
+
|
||||
+ if (etag == ETAG_CTRL_TAG)
|
||||
+ p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
|
||||
+ else if (etag != ETAG_CTRL_UNTAG)
|
||||
+ printk("vlan egress tag control neither untag nor tag.\n");
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ u8 member = 0;
|
||||
+ u8 etags = 0;
|
||||
+ int i;
|
||||
+
|
||||
+ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS ||
|
||||
+ val->len > MT7530_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ for (i = 0; i < val->len; i++) {
|
||||
+ struct switch_port *p = &val->value.ports[i];
|
||||
+
|
||||
+ if (p->id >= MT7530_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ member |= BIT(p->id);
|
||||
+
|
||||
+ if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
|
||||
+ etags |= BIT(p->id);
|
||||
+ }
|
||||
+ priv->vlan_entries[val->port_vlan].member = member;
|
||||
+ priv->vlan_entries[val->port_vlan].etags = etags;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ int vlan;
|
||||
+ u16 vid;
|
||||
+
|
||||
+ vlan = val->port_vlan;
|
||||
+ vid = (u16)val->value.i;
|
||||
+
|
||||
+ if (vlan < 0 || vlan >= MT7530_NUM_VLANS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (vid < MT7530_MIN_VID || vid > MT7530_MAX_VID)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ priv->vlan_entries[vlan].vid = vid;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ u32 vid;
|
||||
+ int vlan;
|
||||
+
|
||||
+ vlan = val->port_vlan;
|
||||
+
|
||||
+ vid = mt7530_r32(priv, REG_ESW_VLAN_VTIM(vlan));
|
||||
+ if (vlan & 1)
|
||||
+ vid = vid >> 12;
|
||||
+ vid &= 0xfff;
|
||||
+
|
||||
+ val->value.i = vid;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_apply_config(struct switch_dev *dev)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ int i, j;
|
||||
+ u8 tag_ports;
|
||||
+ u8 untag_ports;
|
||||
+
|
||||
+ if (!priv->global_vlan_enable) {
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
||||
+ mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0000);
|
||||
+
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
||||
+ mt7530_w32(priv, REG_ESW_PORT_PVC(i), 0x810000c0);
|
||||
+
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ /* set all ports as security mode */
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
|
||||
+ mt7530_w32(priv, REG_ESW_PORT_PCR(i), 0x00ff0003);
|
||||
+
|
||||
+ /* check if a port is used in tag/untag vlan egress mode */
|
||||
+ tag_ports = 0;
|
||||
+ untag_ports = 0;
|
||||
+
|
||||
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
||||
+ u8 member = priv->vlan_entries[i].member;
|
||||
+ u8 etags = priv->vlan_entries[i].etags;
|
||||
+
|
||||
+ if (!member)
|
||||
+ continue;
|
||||
+
|
||||
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
|
||||
+ if (!(member & BIT(j)))
|
||||
+ continue;
|
||||
+
|
||||
+ if (etags & BIT(j))
|
||||
+ tag_ports |= 1u << j;
|
||||
+ else
|
||||
+ untag_ports |= 1u << j;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* set all untag-only ports as transparent and the rest as user port */
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
|
||||
+ u32 pvc_mode = 0x81000000;
|
||||
+
|
||||
+ if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
|
||||
+ pvc_mode = 0x810000c0;
|
||||
+
|
||||
+ mt7530_w32(priv, REG_ESW_PORT_PVC(i), pvc_mode);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < MT7530_NUM_VLANS; i++) {
|
||||
+ u16 vid = priv->vlan_entries[i].vid;
|
||||
+ u8 member = priv->vlan_entries[i].member;
|
||||
+ u8 etags = priv->vlan_entries[i].etags;
|
||||
+ u32 val;
|
||||
+
|
||||
+ /* vid of vlan */
|
||||
+ val = mt7530_r32(priv, REG_ESW_VLAN_VTIM(i));
|
||||
+ if (i % 2 == 0) {
|
||||
+ val &= 0xfff000;
|
||||
+ val |= vid;
|
||||
+ } else {
|
||||
+ val &= 0xfff;
|
||||
+ val |= (vid << 12);
|
||||
+ }
|
||||
+ mt7530_w32(priv, REG_ESW_VLAN_VTIM(i), val);
|
||||
+
|
||||
+ /* vlan port membership */
|
||||
+ if (member)
|
||||
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, REG_ESW_VLAN_VAWD1_IVL_MAC |
|
||||
+ REG_ESW_VLAN_VAWD1_VTAG_EN | (member << 16) |
|
||||
+ REG_ESW_VLAN_VAWD1_VALID);
|
||||
+ else
|
||||
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, 0);
|
||||
+
|
||||
+ /* egress mode */
|
||||
+ val = 0;
|
||||
+ for (j = 0; j < MT7530_NUM_PORTS; j++) {
|
||||
+ if (etags & BIT(j))
|
||||
+ val |= ETAG_CTRL_TAG << (j * 2);
|
||||
+ else
|
||||
+ val |= ETAG_CTRL_UNTAG << (j * 2);
|
||||
+ }
|
||||
+ mt7530_w32(priv, REG_ESW_VLAN_VAWD2, val);
|
||||
+
|
||||
+ /* write to vlan table */
|
||||
+ mt7530_vtcr(priv, 1, i);
|
||||
+ }
|
||||
+
|
||||
+ /* Port Default PVID */
|
||||
+ for (i = 0; i < MT7530_NUM_PORTS; i++) {
|
||||
+ u32 val;
|
||||
+ val = mt7530_r32(priv, REG_ESW_PORT_PPBV1(i));
|
||||
+ val &= ~0xfff;
|
||||
+ val |= priv->port_entries[i].pvid;
|
||||
+ mt7530_w32(priv, REG_ESW_PORT_PPBV1(i), val);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+mt7530_get_port_link(struct switch_dev *dev, int port,
|
||||
+ struct switch_port_link *link)
|
||||
+{
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ u32 speed, pmsr;
|
||||
+
|
||||
+ if (port < 0 || port >= MT7530_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ pmsr = mt7530_r32(priv, 0x3008 + (0x100 * port));
|
||||
+
|
||||
+ link->link = pmsr & 1;
|
||||
+ link->duplex = (pmsr >> 1) & 1;
|
||||
+ speed = (pmsr >> 2) & 3;
|
||||
+
|
||||
+ switch (speed) {
|
||||
+ case 0:
|
||||
+ link->speed = SWITCH_PORT_SPEED_10;
|
||||
+ break;
|
||||
+ case 1:
|
||||
+ link->speed = SWITCH_PORT_SPEED_100;
|
||||
+ break;
|
||||
+ case 2:
|
||||
+ case 3: /* forced gige speed can be 2 or 3 */
|
||||
+ link->speed = SWITCH_PORT_SPEED_1000;
|
||||
+ break;
|
||||
+ default:
|
||||
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct switch_attr mt7530_global[] = {
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "enable_vlan",
|
||||
+ .description = "VLAN mode (1:enabled)",
|
||||
+ .max = 1,
|
||||
+ .id = MT7530_ATTR_ENABLE_VLAN,
|
||||
+ .get = mt7530_get_vlan_enable,
|
||||
+ .set = mt7530_set_vlan_enable,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static u64 get_mib_counter(struct mt7530_priv *priv, int i, int port)
|
||||
+{
|
||||
+ unsigned int port_base;
|
||||
+ u64 t;
|
||||
+
|
||||
+ port_base = MT7621_MIB_COUNTER_BASE +
|
||||
+ MT7621_MIB_COUNTER_PORT_OFFSET * port;
|
||||
+
|
||||
+ t = mt7530_r32(priv, port_base + mt7621_mibs[i].offset);
|
||||
+ if (mt7621_mibs[i].size == 2) {
|
||||
+ u64 hi;
|
||||
+
|
||||
+ hi = mt7530_r32(priv, port_base + mt7621_mibs[i].offset + 4);
|
||||
+ t |= hi << 32;
|
||||
+ }
|
||||
+
|
||||
+ return t;
|
||||
+}
|
||||
+
|
||||
+static int mt7621_sw_get_port_mib(struct switch_dev *dev,
|
||||
+ const struct switch_attr *attr,
|
||||
+ struct switch_val *val)
|
||||
+{
|
||||
+ static char buf[4096];
|
||||
+ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev);
|
||||
+ int i, len = 0;
|
||||
+
|
||||
+ if (val->port_vlan >= MT7530_NUM_PORTS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ len += snprintf(buf + len, sizeof(buf) - len,
|
||||
+ "Port %d MIB counters\n", val->port_vlan);
|
||||
+
|
||||
+ for (i = 0; i < sizeof(mt7621_mibs) / sizeof(*mt7621_mibs); ++i) {
|
||||
+ u64 counter;
|
||||
+ len += snprintf(buf + len, sizeof(buf) - len,
|
||||
+ "%-11s: ", mt7621_mibs[i].name);
|
||||
+ counter = get_mib_counter(priv, i, val->port_vlan);
|
||||
+ len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
|
||||
+ counter);
|
||||
+ }
|
||||
+
|
||||
+ val->value.s = buf;
|
||||
+ val->len = len;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct switch_attr mt7621_port[] = {
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_STRING,
|
||||
+ .name = "mib",
|
||||
+ .description = "Get MIB counters for port",
|
||||
+ .get = mt7621_sw_get_port_mib,
|
||||
+ .set = NULL,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct switch_attr mt7530_port[] = {
|
||||
+};
|
||||
+
|
||||
+static const struct switch_attr mt7530_vlan[] = {
|
||||
+ {
|
||||
+ .type = SWITCH_TYPE_INT,
|
||||
+ .name = "vid",
|
||||
+ .description = "VLAN ID (0-4094)",
|
||||
+ .set = mt7530_set_vid,
|
||||
+ .get = mt7530_get_vid,
|
||||
+ .max = 4094,
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+static const struct switch_dev_ops mt7621_ops = {
|
||||
+ .attr_global = {
|
||||
+ .attr = mt7530_global,
|
||||
+ .n_attr = ARRAY_SIZE(mt7530_global),
|
||||
+ },
|
||||
+ .attr_port = {
|
||||
+ .attr = mt7621_port,
|
||||
+ .n_attr = ARRAY_SIZE(mt7621_port),
|
||||
+ },
|
||||
+ .attr_vlan = {
|
||||
+ .attr = mt7530_vlan,
|
||||
+ .n_attr = ARRAY_SIZE(mt7530_vlan),
|
||||
+ },
|
||||
+ .get_vlan_ports = mt7530_get_vlan_ports,
|
||||
+ .set_vlan_ports = mt7530_set_vlan_ports,
|
||||
+ .get_port_pvid = mt7530_get_port_pvid,
|
||||
+ .set_port_pvid = mt7530_set_port_pvid,
|
||||
+ .get_port_link = mt7530_get_port_link,
|
||||
+ .apply_config = mt7530_apply_config,
|
||||
+ .reset_switch = mt7530_reset_switch,
|
||||
+};
|
||||
+
|
||||
+static const struct switch_dev_ops mt7530_ops = {
|
||||
+ .attr_global = {
|
||||
+ .attr = mt7530_global,
|
||||
+ .n_attr = ARRAY_SIZE(mt7530_global),
|
||||
+ },
|
||||
+ .attr_port = {
|
||||
+ .attr = mt7530_port,
|
||||
+ .n_attr = ARRAY_SIZE(mt7530_port),
|
||||
+ },
|
||||
+ .attr_vlan = {
|
||||
+ .attr = mt7530_vlan,
|
||||
+ .n_attr = ARRAY_SIZE(mt7530_vlan),
|
||||
+ },
|
||||
+ .get_vlan_ports = mt7530_get_vlan_ports,
|
||||
+ .set_vlan_ports = mt7530_set_vlan_ports,
|
||||
+ .get_port_pvid = mt7530_get_port_pvid,
|
||||
+ .set_port_pvid = mt7530_set_port_pvid,
|
||||
+ .get_port_link = mt7530_get_port_link,
|
||||
+ .apply_config = mt7530_apply_config,
|
||||
+ .reset_switch = mt7530_reset_switch,
|
||||
+};
|
||||
+
|
||||
+int
|
||||
+mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan)
|
||||
+{
|
||||
+ struct switch_dev *swdev;
|
||||
+ struct mt7530_priv *mt7530;
|
||||
+ struct mt7530_mapping *map;
|
||||
+ int ret;
|
||||
+
|
||||
+ mt7530 = devm_kzalloc(dev, sizeof(struct mt7530_priv), GFP_KERNEL);
|
||||
+ if (!mt7530)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ mt7530->base = base;
|
||||
+ mt7530->bus = bus;
|
||||
+ mt7530->global_vlan_enable = vlan;
|
||||
+
|
||||
+ swdev = &mt7530->swdev;
|
||||
+ if (bus) {
|
||||
+ swdev->alias = "mt7530";
|
||||
+ swdev->name = "mt7530";
|
||||
+ } else if (IS_ENABLED(CONFIG_SOC_MT7621)) {
|
||||
+ swdev->alias = "mt7621";
|
||||
+ swdev->name = "mt7621";
|
||||
+ } else {
|
||||
+ swdev->alias = "mt7620";
|
||||
+ swdev->name = "mt7620";
|
||||
+ }
|
||||
+ swdev->cpu_port = MT7530_CPU_PORT;
|
||||
+ swdev->ports = MT7530_NUM_PORTS;
|
||||
+ swdev->vlans = MT7530_NUM_VLANS;
|
||||
+ if (IS_ENABLED(CONFIG_SOC_MT7621))
|
||||
+ swdev->ops = &mt7621_ops;
|
||||
+ else
|
||||
+ swdev->ops = &mt7530_ops;
|
||||
+
|
||||
+ ret = register_switch(swdev, NULL);
|
||||
+ if (ret) {
|
||||
+ dev_err(dev, "failed to register mt7530\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ map = mt7530_find_mapping(dev->of_node);
|
||||
+ if (map)
|
||||
+ mt7530_apply_mapping(mt7530, map);
|
||||
+ mt7530_apply_config(swdev);
|
||||
+
|
||||
+ /* magic vodoo */
|
||||
+ if (!IS_ENABLED(CONFIG_SOC_MT7621) && bus && mt7530_r32(mt7530, REG_HWTRAP) != 0x1117edf) {
|
||||
+ dev_info(dev, "fixing up MHWTRAP register - bootloader probably played with it\n");
|
||||
+ mt7530_w32(mt7530, REG_HWTRAP, 0x1117edf);
|
||||
+ }
|
||||
+ dev_info(dev, "loaded %s driver\n", swdev->name);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/ethernet/mediatek/mt7530.h
|
||||
@@ -0,0 +1,20 @@
|
||||
+/*
|
||||
+ * This program is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU General Public License
|
||||
+ * as published by the Free Software Foundation; either version 2
|
||||
+ * of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This program is distributed in the hope that it will be useful,
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ * GNU General Public License for more details.
|
||||
+ *
|
||||
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
|
||||
+ */
|
||||
+
|
||||
+#ifndef _MT7530_H__
|
||||
+#define _MT7530_H__
|
||||
+
|
||||
+int mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan);
|
||||
+
|
||||
+#endif
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -1308,8 +1308,13 @@ static int __init fe_init(struct net_dev
|
||||
}
|
||||
|
||||
err = fe_hw_init(dev);
|
||||
- if (!err)
|
||||
- return 0;
|
||||
+ if (err)
|
||||
+ goto err_phy_disconnect;
|
||||
+
|
||||
+ if ((priv->flags & FE_FLAG_HAS_SWITCH) && priv->soc->switch_config)
|
||||
+ priv->soc->switch_config(priv);
|
||||
+
|
||||
+ return 0;
|
||||
|
||||
err_phy_disconnect:
|
||||
if (priv->phy)
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
||||
@@ -383,6 +383,7 @@ struct fe_soc_data {
|
||||
int (*fwd_config)(struct fe_priv *priv);
|
||||
void (*tx_dma)(struct fe_tx_dma *txd);
|
||||
int (*switch_init)(struct fe_priv *priv);
|
||||
+ int (*switch_config)(struct fe_priv *priv);
|
||||
void (*port_init)(struct fe_priv *priv, struct device_node *port);
|
||||
int (*has_carrier)(struct fe_priv *priv);
|
||||
int (*mdio_init)(struct fe_priv *priv);
|
||||
--- a/drivers/net/ethernet/mediatek/soc_mt7620.c
|
||||
+++ b/drivers/net/ethernet/mediatek/soc_mt7620.c
|
||||
@@ -313,6 +313,7 @@ static struct fe_soc_data mt7620_data =
|
||||
.fwd_config = mt7620_fwd_config,
|
||||
.tx_dma = mt7620_tx_dma,
|
||||
.switch_init = mtk_gsw_init,
|
||||
+ .switch_config = mt7620_gsw_config,
|
||||
.port_init = mt7620_port_init,
|
||||
.reg_table = mt7620_reg_table,
|
||||
.pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS,
|
|
@ -0,0 +1,48 @@
|
|||
--- a/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
+++ b/drivers/net/ethernet/mediatek/esw_rt3050.c
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <asm/mach-ralink/ralink_regs.h>
|
||||
+#include <linux/of_irq.h>
|
||||
|
||||
#include <linux/switch.h>
|
||||
|
||||
@@ -1332,7 +1333,6 @@ static int esw_probe(struct platform_dev
|
||||
const __be32 *port_map, *reg_init;
|
||||
struct switch_dev *swdev;
|
||||
struct rt305x_esw *esw;
|
||||
- struct resource *irq;
|
||||
int ret;
|
||||
|
||||
esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
|
||||
@@ -1340,7 +1340,7 @@ static int esw_probe(struct platform_dev
|
||||
return -ENOMEM;
|
||||
|
||||
esw->dev = &pdev->dev;
|
||||
- esw->irq = irq->start;
|
||||
+ esw->irq = irq_of_parse_and_map(np, 0);
|
||||
esw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!esw->base)
|
||||
return -EADDRNOTAVAIL;
|
||||
@@ -1365,7 +1365,7 @@ static int esw_probe(struct platform_dev
|
||||
ret = register_switch(swdev, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "register_switch failed\n");
|
||||
- goto unmap_base;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, esw);
|
||||
@@ -1383,11 +1383,6 @@ static int esw_probe(struct platform_dev
|
||||
}
|
||||
|
||||
return ret;
|
||||
-
|
||||
-unmap_base:
|
||||
- iounmap(esw->base);
|
||||
- kfree(esw);
|
||||
- return ret;
|
||||
}
|
||||
|
||||
static int esw_remove(struct platform_device *pdev)
|
|
@ -0,0 +1,22 @@
|
|||
From 3c550ae0359be5bc4d5daa86a62f711a91ac8dde Mon Sep 17 00:00:00 2001
|
||||
From: John Crispin <blogic@openwrt.org>
|
||||
Date: Sun, 13 Dec 2015 17:46:09 +0100
|
||||
Subject: [PATCH 2/2] net:mediatke: add phy_ethtool_ioctl() support
|
||||
|
||||
Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
---
|
||||
drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
||||
@@ -1344,6 +1344,9 @@ static int fe_do_ioctl(struct net_device
|
||||
return -ENODEV;
|
||||
|
||||
switch (cmd) {
|
||||
+ case SIOCETHTOOL:
|
||||
+ return phy_ethtool_ioctl(priv->phy_dev,
|
||||
+ (void *) ifr->ifr_data);
|
||||
case SIOCGMIIPHY:
|
||||
case SIOCGMIIREG:
|
||||
case SIOCSMIIREG:
|
|
@ -0,0 +1,43 @@
|
|||
From 8b61a1a33e41456ebeafa0ebe7ec0fccf859861e Mon Sep 17 00:00:00 2001
|
||||
From: Nikolay Martynov <mar.kolya@gmail.com>
|
||||
Date: Wed, 25 Nov 2015 20:43:46 -0500
|
||||
Subject: [PATCH] mtd: nand: Fix Spansion sparearea size detection
|
||||
|
||||
According to datasheet S34ML02G2 and S34ML04G2 have
|
||||
larger sparea area size than was detected.
|
||||
|
||||
Signed-off-by: Nikolay Martynov <mar.kolya@gmail.com>
|
||||
---
|
||||
drivers/mtd/nand/nand_base.c | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/nand/nand_base.c
|
||||
+++ b/drivers/mtd/nand/nand_base.c
|
||||
@@ -3568,6 +3568,7 @@ static void nand_decode_ext_id(struct mt
|
||||
/*
|
||||
* Field definitions are in the following datasheets:
|
||||
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
|
||||
+ * Spansion S34ML02G2 (p.33)
|
||||
* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
|
||||
* Hynix MLC (6 byte ID): Hynix H27UBG8T2B (p.22)
|
||||
*
|
||||
@@ -3665,6 +3666,19 @@ static void nand_decode_ext_id(struct mt
|
||||
*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
||||
|
||||
/*
|
||||
+ * Spansion S34ML0[24]G2 have oobsize twice as large
|
||||
+ * as S34ML01G2 encoded in the same bit. We
|
||||
+ * differinciate them by their ID length
|
||||
+ */
|
||||
+ if (id_data[0] == NAND_MFR_AMD
|
||||
+ && (id_data[1] == 0xda
|
||||
+ || id_data[1] == 0xdc
|
||||
+ || id_data[1] == 0xca
|
||||
+ || id_data[1] == 0xcc)) {
|
||||
+ mtd->oobsize *= 2;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
* Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
|
||||
* 512B page. For Toshiba SLC, we decode the 5th/6th byte as
|
||||
* follows:
|
Loading…
Reference in a new issue