mirror of https://github.com/hak5/openwrt.git
mvebu: preliminary 3.19 support
Signed-off-by: Imre Kaloz <kaloz@openwrt.org> SVN-Revision: 44130lede-17.01
parent
d9c6ff8c25
commit
18012a4198
|
@ -0,0 +1,344 @@
|
|||
CONFIG_ALIGNMENT_TRAP=y
|
||||
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
|
||||
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
|
||||
CONFIG_ARCH_HAS_SG_CHAIN=y
|
||||
CONFIG_ARCH_HAS_TICK_BROADCAST=y
|
||||
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
||||
CONFIG_ARCH_MULTIPLATFORM=y
|
||||
# CONFIG_ARCH_MULTI_CPU_AUTO is not set
|
||||
CONFIG_ARCH_MULTI_V6_V7=y
|
||||
CONFIG_ARCH_MULTI_V7=y
|
||||
CONFIG_ARCH_MVEBU=y
|
||||
# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_REQUIRE_GPIOLIB=y
|
||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
|
||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
|
||||
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
|
||||
CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
|
||||
CONFIG_ARCH_SUPPORTS_UPROBES=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
|
||||
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
|
||||
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
|
||||
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
|
||||
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARMADA_370_CLK=y
|
||||
CONFIG_ARMADA_370_XP_TIMER=y
|
||||
CONFIG_ARMADA_38X_CLK=y
|
||||
CONFIG_ARMADA_XP_CLK=y
|
||||
CONFIG_ARM_APPENDED_DTB=y
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT=y
|
||||
# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
|
||||
CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y
|
||||
CONFIG_ARM_CPU_SUSPEND=y
|
||||
CONFIG_ARM_ERRATA_720789=y
|
||||
CONFIG_ARM_GIC=y
|
||||
CONFIG_ARM_HAS_SG_CHAIN=y
|
||||
# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=6
|
||||
CONFIG_ARM_L1_CACHE_SHIFT_6=y
|
||||
# CONFIG_ARM_LPAE is not set
|
||||
CONFIG_ARM_MVEBU_V7_CPUIDLE=y
|
||||
CONFIG_ARM_PATCH_PHYS_VIRT=y
|
||||
CONFIG_ARM_THUMB=y
|
||||
# CONFIG_ARM_THUMBEE is not set
|
||||
CONFIG_ARM_VIRT_EXT=y
|
||||
CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=y
|
||||
CONFIG_ATAGS=y
|
||||
CONFIG_AUTO_ZRELADDR=y
|
||||
CONFIG_BOUNCE=y
|
||||
# CONFIG_CACHE_FEROCEON_L2 is not set
|
||||
CONFIG_CACHE_L2X0=y
|
||||
CONFIG_CACHE_PL310=y
|
||||
CONFIG_CLKDEV_LOOKUP=y
|
||||
CONFIG_CLKSRC_MMIO=y
|
||||
CONFIG_CLKSRC_OF=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_CPUFREQ_DT=y
|
||||
CONFIG_CPU_32v6K=y
|
||||
CONFIG_CPU_32v7=y
|
||||
CONFIG_CPU_ABRT_EV7=y
|
||||
# CONFIG_CPU_BIG_ENDIAN is not set
|
||||
# CONFIG_CPU_BPREDICT_DISABLE is not set
|
||||
CONFIG_CPU_CACHE_V7=y
|
||||
CONFIG_CPU_CACHE_VIPT=y
|
||||
CONFIG_CPU_COPY_V6=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
|
||||
CONFIG_CPU_FREQ_GOV_COMMON=y
|
||||
# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
|
||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
|
||||
# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
# CONFIG_CPU_FREQ_STAT_DETAILS is not set
|
||||
CONFIG_CPU_HAS_ASID=y
|
||||
# CONFIG_CPU_ICACHE_DISABLE is not set
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_CPU_IDLE_GOV_LADDER=y
|
||||
CONFIG_CPU_PABRT_V7=y
|
||||
CONFIG_CPU_PJ4B=y
|
||||
CONFIG_CPU_PM=y
|
||||
CONFIG_CPU_RMAP=y
|
||||
# CONFIG_CPU_THERMAL is not set
|
||||
CONFIG_CPU_TLB_V7=y
|
||||
CONFIG_CPU_V7=y
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_XZ=y
|
||||
CONFIG_DCACHE_WORD_ACCESS=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_LL=y
|
||||
CONFIG_DEBUG_LL_INCLUDE="debug/8250.S"
|
||||
CONFIG_DEBUG_MVEBU_UART=y
|
||||
# CONFIG_DEBUG_MVEBU_UART_ALTERNATE is not set
|
||||
CONFIG_DEBUG_UART_8250=y
|
||||
# CONFIG_DEBUG_UART_8250_FLOW_CONTROL is not set
|
||||
CONFIG_DEBUG_UART_8250_SHIFT=2
|
||||
# CONFIG_DEBUG_UART_8250_WORD is not set
|
||||
CONFIG_DEBUG_UART_PHYS=0xd0012000
|
||||
# CONFIG_DEBUG_UART_PL01X is not set
|
||||
CONFIG_DEBUG_UART_VIRT=0xfec12000
|
||||
CONFIG_DEBUG_UNCOMPRESS=y
|
||||
CONFIG_DEBUG_USER=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
CONFIG_DMA_ENGINE_RAID=y
|
||||
CONFIG_DMA_OF=y
|
||||
CONFIG_DTC=y
|
||||
# CONFIG_DW_DMAC_CORE is not set
|
||||
# CONFIG_DW_DMAC_PCI is not set
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_FRAME_POINTER=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
|
||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
||||
CONFIG_GENERIC_IO=y
|
||||
CONFIG_GENERIC_IRQ_CHIP=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
||||
CONFIG_GENERIC_STRNLEN_USER=y
|
||||
CONFIG_GPIOLIB=y
|
||||
CONFIG_GPIO_DEVRES=y
|
||||
CONFIG_GPIO_GENERIC=y
|
||||
CONFIG_GPIO_MVEBU=y
|
||||
CONFIG_GPIO_MVEBU_PWM=y
|
||||
CONFIG_GPIO_SYSFS=y
|
||||
CONFIG_HANDLE_DOMAIN_IRQ=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
|
||||
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
|
||||
CONFIG_HAVE_ARCH_JUMP_LABEL=y
|
||||
CONFIG_HAVE_ARCH_KGDB=y
|
||||
CONFIG_HAVE_ARCH_PFN_VALID=y
|
||||
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
|
||||
CONFIG_HAVE_ARCH_TRACEHOOK=y
|
||||
CONFIG_HAVE_ARM_SCU=y
|
||||
CONFIG_HAVE_ARM_TWD=y
|
||||
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
|
||||
CONFIG_HAVE_BPF_JIT=y
|
||||
CONFIG_HAVE_CC_STACKPROTECTOR=y
|
||||
CONFIG_HAVE_CLK=y
|
||||
CONFIG_HAVE_CLK_PREPARE=y
|
||||
CONFIG_HAVE_CONTEXT_TRACKING=y
|
||||
CONFIG_HAVE_C_RECORDMCOUNT=y
|
||||
CONFIG_HAVE_DEBUG_KMEMLEAK=y
|
||||
CONFIG_HAVE_DMA_API_DEBUG=y
|
||||
CONFIG_HAVE_DMA_ATTRS=y
|
||||
CONFIG_HAVE_DMA_CONTIGUOUS=y
|
||||
CONFIG_HAVE_DYNAMIC_FTRACE=y
|
||||
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
|
||||
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
|
||||
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
|
||||
CONFIG_HAVE_FUNCTION_TRACER=y
|
||||
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
|
||||
CONFIG_HAVE_IDE=y
|
||||
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
|
||||
CONFIG_HAVE_KERNEL_GZIP=y
|
||||
CONFIG_HAVE_KERNEL_LZ4=y
|
||||
CONFIG_HAVE_KERNEL_LZMA=y
|
||||
CONFIG_HAVE_KERNEL_LZO=y
|
||||
CONFIG_HAVE_KERNEL_XZ=y
|
||||
CONFIG_HAVE_MEMBLOCK=y
|
||||
CONFIG_HAVE_NET_DSA=y
|
||||
CONFIG_HAVE_OPROFILE=y
|
||||
CONFIG_HAVE_PERF_EVENTS=y
|
||||
CONFIG_HAVE_PERF_REGS=y
|
||||
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
|
||||
CONFIG_HAVE_PROC_CPU=y
|
||||
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
|
||||
CONFIG_HAVE_SMP=y
|
||||
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
|
||||
CONFIG_HAVE_UID16=y
|
||||
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
|
||||
CONFIG_HIGHMEM=y
|
||||
# CONFIG_HIGHPTE is not set
|
||||
CONFIG_HZ_FIXED=0
|
||||
CONFIG_HZ_PERIODIC=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_IOMMU_HELPER=y
|
||||
CONFIG_IRQCHIP=y
|
||||
CONFIG_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_DOMAIN_DEBUG=y
|
||||
CONFIG_IRQ_FORCED_THREADING=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
# CONFIG_IWMMXT is not set
|
||||
CONFIG_KERNFS=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LIBFDT=y
|
||||
CONFIG_LOG_BUF_SHIFT=14
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_MACH_ARMADA_370=y
|
||||
# CONFIG_MACH_ARMADA_375 is not set
|
||||
CONFIG_MACH_ARMADA_38X=y
|
||||
CONFIG_MACH_ARMADA_XP=y
|
||||
# CONFIG_MACH_DOVE is not set
|
||||
CONFIG_MACH_MVEBU_ANY=y
|
||||
CONFIG_MACH_MVEBU_V7=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_MARVELL_PHY=y
|
||||
CONFIG_MDIO_BOARDINFO=y
|
||||
CONFIG_MEMORY=y
|
||||
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
|
||||
CONFIG_MIGHT_HAVE_PCI=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
CONFIG_MTD_CFI_STAA=y
|
||||
CONFIG_MTD_M25P80=y
|
||||
CONFIG_MTD_NAND=y
|
||||
CONFIG_MTD_NAND_ECC=y
|
||||
CONFIG_MTD_NAND_PXA3xx=y
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_SPLIT_SUPPORT=y
|
||||
CONFIG_MTD_UBI=y
|
||||
CONFIG_MTD_UBI_BEB_LIMIT=20
|
||||
CONFIG_MTD_UBI_BLOCK=y
|
||||
# CONFIG_MTD_UBI_FASTMAP is not set
|
||||
# CONFIG_MTD_UBI_GLUEBI is not set
|
||||
CONFIG_MTD_UBI_WL_THRESHOLD=4096
|
||||
CONFIG_MULTI_IRQ_HANDLER=y
|
||||
CONFIG_MUTEX_SPIN_ON_OWNER=y
|
||||
CONFIG_MVEBU_CLK_COMMON=y
|
||||
CONFIG_MVEBU_CLK_COREDIV=y
|
||||
CONFIG_MVEBU_CLK_CPU=y
|
||||
CONFIG_MVEBU_DEVBUS=y
|
||||
CONFIG_MVEBU_MBUS=y
|
||||
CONFIG_MVMDIO=y
|
||||
CONFIG_MVNETA=y
|
||||
CONFIG_MVSW61XX_PHY=y
|
||||
CONFIG_MV_XOR=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
# CONFIG_NEON is not set
|
||||
CONFIG_NET_FLOW_LIMIT=y
|
||||
CONFIG_NO_BOOTMEM=y
|
||||
CONFIG_NR_CPUS=4
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_ADDRESS=y
|
||||
CONFIG_OF_ADDRESS_PCI=y
|
||||
CONFIG_OF_EARLY_FLATTREE=y
|
||||
CONFIG_OF_FLATTREE=y
|
||||
CONFIG_OF_GPIO=y
|
||||
CONFIG_OF_IRQ=y
|
||||
CONFIG_OF_MDIO=y
|
||||
CONFIG_OF_MTD=y
|
||||
CONFIG_OF_NET=y
|
||||
CONFIG_OF_PCI=y
|
||||
CONFIG_OF_PCI_IRQ=y
|
||||
CONFIG_OF_RESERVED_MEM=y
|
||||
CONFIG_OLD_SIGACTION=y
|
||||
CONFIG_OLD_SIGSUSPEND3=y
|
||||
CONFIG_OUTER_CACHE=y
|
||||
CONFIG_OUTER_CACHE_SYNC=y
|
||||
CONFIG_PAGEFLAGS_EXTENDED=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
CONFIG_PCI=y
|
||||
CONFIG_PCI_MSI=y
|
||||
CONFIG_PCI_MVEBU=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PHYLIB=y
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_PINCTRL_ARMADA_370=y
|
||||
CONFIG_PINCTRL_ARMADA_38X=y
|
||||
CONFIG_PINCTRL_ARMADA_XP=y
|
||||
CONFIG_PINCTRL_MVEBU=y
|
||||
# CONFIG_PINCTRL_SINGLE is not set
|
||||
CONFIG_PJ4B_ERRATA_4742=y
|
||||
# CONFIG_PL310_ERRATA_588369 is not set
|
||||
# CONFIG_PL310_ERRATA_727915 is not set
|
||||
# CONFIG_PL310_ERRATA_753970 is not set
|
||||
# CONFIG_PL310_ERRATA_769419 is not set
|
||||
CONFIG_PLAT_ORION=y
|
||||
CONFIG_PM_OPP=y
|
||||
CONFIG_PWM=y
|
||||
# CONFIG_PWM_FSL_FTM is not set
|
||||
# CONFIG_PREEMPT_RCU is not set
|
||||
CONFIG_RCU_STALL_COMMON=y
|
||||
CONFIG_RFS_ACCEL=y
|
||||
CONFIG_RPS=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
# CONFIG_RTC_DRV_MV is not set
|
||||
CONFIG_RWSEM_SPIN_ON_OWNER=y
|
||||
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
|
||||
CONFIG_SCHED_HRTICK=y
|
||||
# CONFIG_SCSI_DMA is not set
|
||||
CONFIG_SERIAL_8250_DW=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_SMP_ON_UP=y
|
||||
CONFIG_SOC_BUS=y
|
||||
CONFIG_SPARSE_IRQ=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_ORION=y
|
||||
CONFIG_STOP_MACHINE=y
|
||||
CONFIG_SWCONFIG=y
|
||||
CONFIG_SWIOTLB=y
|
||||
CONFIG_SWP_EMULATE=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
# CONFIG_THUMB2_KERNEL is not set
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_TIMER_STATS=y
|
||||
CONFIG_TREE_RCU=y
|
||||
CONFIG_UBIFS_FS=y
|
||||
# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
|
||||
CONFIG_UBIFS_FS_LZO=y
|
||||
CONFIG_UBIFS_FS_XZ=y
|
||||
CONFIG_UBIFS_FS_ZLIB=y
|
||||
CONFIG_UID16=y
|
||||
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USE_OF=y
|
||||
CONFIG_VECTORS_BASE=0xffff0000
|
||||
CONFIG_VFP=y
|
||||
CONFIG_VFPv3=y
|
||||
# CONFIG_XEN is not set
|
||||
CONFIG_XPS=y
|
||||
CONFIG_XZ_DEC_ARM=y
|
||||
CONFIG_XZ_DEC_BCJ=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_ZLIB_INFLATE=y
|
||||
CONFIG_ZONE_DMA_FLAG=0
|
|
@ -0,0 +1,10 @@
|
|||
--- a/arch/arm/boot/dts/Makefile
|
||||
+++ b/arch/arm/boot/dts/Makefile
|
||||
@@ -541,6 +541,7 @@ dtb-$(CONFIG_MACH_ARMADA_XP) += \
|
||||
armada-xp-db.dtb \
|
||||
armada-xp-gp.dtb \
|
||||
armada-xp-lenovo-ix4-300d.dtb \
|
||||
+ armada-xp-mamba.dtb \
|
||||
armada-xp-matrix.dtb \
|
||||
armada-xp-netgear-rn2120.dtb \
|
||||
armada-xp-openblocks-ax3-4.dtb \
|
|
@ -0,0 +1,51 @@
|
|||
Document the binding for the TLC59116 LED driver.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
.../devicetree/bindings/leds/leds-tlc59116.txt | 40 ++++++++++++++++++++++
|
||||
1 file changed, 40 insertions(+)
|
||||
create mode 100644 Documentation/devicetree/bindings/leds/leds-tlc59116.txt
|
||||
|
||||
--- /dev/null
|
||||
+++ b/Documentation/devicetree/bindings/leds/leds-tlc59116.txt
|
||||
@@ -0,0 +1,40 @@
|
||||
+LEDs connected to tcl59116
|
||||
+
|
||||
+Required properties
|
||||
+- compatible: should be "ti,tlc59116"
|
||||
+- #address-cells: must be 1
|
||||
+- #size-cells: must be 0
|
||||
+- reg: typically 0x68
|
||||
+
|
||||
+Each led is represented as a sub-node of the ti,,tlc59116.
|
||||
+See Documentation/devicetree/bindings/leds/common.txt
|
||||
+
|
||||
+LED sub-node properties:
|
||||
+- reg: number of LED line, 0 to 15
|
||||
+- label: (optional) name of LED
|
||||
+- linux,default-trigger : (optional)
|
||||
+
|
||||
+Examples:
|
||||
+
|
||||
+tlc59116@68 {
|
||||
+ #address-cells = <1>;
|
||||
+ #size-cells = <0>;
|
||||
+ compatible = "ti,tlc59116";
|
||||
+ reg = <0x68>;
|
||||
+
|
||||
+ wan@0 {
|
||||
+ label = "wrt1900ac:amber:wan";
|
||||
+ reg = <0x0>;
|
||||
+ };
|
||||
+
|
||||
+ 2g@2 {
|
||||
+ label = "wrt1900ac:white:2g";
|
||||
+ reg = <0x2>;
|
||||
+ };
|
||||
+
|
||||
+ alive@9 {
|
||||
+ label = "wrt1900ac:green:alive";
|
||||
+ reg = <0x9>;
|
||||
+ linux,default_trigger = "heartbeat";
|
||||
+ };
|
||||
+};
|
|
@ -0,0 +1,297 @@
|
|||
The TLC59116 is an I2C bus controlled 16-channel LED driver. Each LED
|
||||
output has its own 8-bit fixed-frequency PWM controller to control the
|
||||
brightness of the LED.
|
||||
|
||||
This is based on a driver from Belkin, but has been extensively
|
||||
rewritten.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
drivers/leds/Kconfig | 8 ++
|
||||
drivers/leds/Makefile | 1 +
|
||||
drivers/leds/leds-tlc59116.c | 252 +++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 261 insertions(+)
|
||||
create mode 100644 drivers/leds/leds-tlc59116.c
|
||||
|
||||
--- a/drivers/leds/Kconfig
|
||||
+++ b/drivers/leds/Kconfig
|
||||
@@ -457,6 +457,14 @@ config LEDS_TCA6507
|
||||
LED driver chips accessed via the I2C bus.
|
||||
Driver support brightness control and hardware-assisted blinking.
|
||||
|
||||
+config LEDS_TLC59116
|
||||
+ tristate "LED driver for TLC59116F controllers"
|
||||
+ depends on LEDS_CLASS && I2C
|
||||
+ select REGMAP_I2C
|
||||
+ help
|
||||
+ This option enables support for Texas Instruments TLC59116F
|
||||
+ LED controller.
|
||||
+
|
||||
config LEDS_MAX8997
|
||||
tristate "LED support for MAX8997 PMIC"
|
||||
depends on LEDS_CLASS && MFD_MAX8997
|
||||
--- a/drivers/leds/Makefile
|
||||
+++ b/drivers/leds/Makefile
|
||||
@@ -30,6 +30,7 @@ obj-$(CONFIG_LEDS_LP8501) += leds-lp850
|
||||
obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o
|
||||
obj-$(CONFIG_LEDS_LP8860) += leds-lp8860.o
|
||||
obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o
|
||||
+obj-$(CONFIG_LEDS_TLC59116) += leds-tlc59116.o
|
||||
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
|
||||
obj-$(CONFIG_LEDS_IPAQ_MICRO) += leds-ipaq-micro.o
|
||||
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/leds/leds-tlc59116.c
|
||||
@@ -0,0 +1,252 @@
|
||||
+/*
|
||||
+ * Copyright 2014 Belkin Inc.
|
||||
+ * Copyright 2014 Andrew Lunn <andrew@lunn.ch>
|
||||
+ *
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/leds.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/regmap.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/workqueue.h>
|
||||
+
|
||||
+#define TLC59116_LEDS 16
|
||||
+
|
||||
+#define TLC59116_REG_MODE1 0x00
|
||||
+#define MODE1_RESPON_ADDR_MASK 0xF0
|
||||
+#define MODE1_NORMAL_MODE (0 << 4)
|
||||
+#define MODE1_SPEED_MODE (1 << 4)
|
||||
+
|
||||
+#define TLC59116_REG_MODE2 0x01
|
||||
+#define MODE2_DIM (0 << 5)
|
||||
+#define MODE2_BLINK (1 << 5)
|
||||
+#define MODE2_OCH_STOP (0 << 3)
|
||||
+#define MODE2_OCH_ACK (1 << 3)
|
||||
+
|
||||
+#define TLC59116_REG_PWM(x) (0x02 + (x))
|
||||
+
|
||||
+#define TLC59116_REG_GRPPWM 0x12
|
||||
+#define TLC59116_REG_GRPFREQ 0x13
|
||||
+
|
||||
+/* LED Driver Output State, determine the source that drives LED outputs */
|
||||
+#define TLC59116_REG_LEDOUT(x) (0x14 + ((x) >> 2))
|
||||
+#define TLC59116_LED_OFF 0x0 /* Output LOW */
|
||||
+#define TLC59116_LED_ON 0x1 /* Output HI-Z */
|
||||
+#define TLC59116_DIM 0x2 /* Dimming */
|
||||
+#define TLC59116_BLINK 0x3 /* Blinking */
|
||||
+#define LED_MASK 0x3
|
||||
+
|
||||
+#define ldev_to_led(c) container_of(c, struct tlc59116_led, ldev)
|
||||
+#define work_to_led(work) container_of(work, struct tlc59116_led, work)
|
||||
+
|
||||
+struct tlc59116_led {
|
||||
+ bool active;
|
||||
+ struct regmap *regmap;
|
||||
+ unsigned int led_no;
|
||||
+ struct led_classdev ldev;
|
||||
+ struct work_struct work;
|
||||
+};
|
||||
+
|
||||
+struct tlc59116_priv {
|
||||
+ struct tlc59116_led leds[TLC59116_LEDS];
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+tlc59116_set_mode(struct regmap *regmap, u8 mode)
|
||||
+{
|
||||
+ int err;
|
||||
+ u8 val;
|
||||
+
|
||||
+ if ((mode != MODE2_DIM) && (mode != MODE2_BLINK))
|
||||
+ mode = MODE2_DIM;
|
||||
+
|
||||
+ /* Configure MODE1 register */
|
||||
+ err = regmap_write(regmap, TLC59116_REG_MODE1, MODE1_NORMAL_MODE);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ /* Configure MODE2 Reg */
|
||||
+ val = MODE2_OCH_STOP | mode;
|
||||
+
|
||||
+ return regmap_write(regmap, TLC59116_REG_MODE2, val);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tlc59116_set_led(struct tlc59116_led *led, u8 val)
|
||||
+{
|
||||
+ struct regmap *regmap = led->regmap;
|
||||
+ unsigned int i = (led->led_no % 4) * 2;
|
||||
+ unsigned int addr = TLC59116_REG_LEDOUT(led->led_no);
|
||||
+ unsigned int mask = LED_MASK << i;
|
||||
+
|
||||
+ val = val << i;
|
||||
+
|
||||
+ return regmap_update_bits(regmap, addr, mask, val);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+tlc59116_led_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct tlc59116_led *led = work_to_led(work);
|
||||
+ struct regmap *regmap = led->regmap;
|
||||
+ int err;
|
||||
+ u8 pwm;
|
||||
+
|
||||
+ pwm = TLC59116_REG_PWM(led->led_no);
|
||||
+ err = regmap_write(regmap, pwm, led->ldev.brightness);
|
||||
+ if (err)
|
||||
+ dev_err(led->ldev.dev, "Failed setting brightness\n");
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+tlc59116_led_set(struct led_classdev *led_cdev, enum led_brightness value)
|
||||
+{
|
||||
+ struct tlc59116_led *led = ldev_to_led(led_cdev);
|
||||
+
|
||||
+ led->ldev.brightness = value;
|
||||
+ schedule_work(&led->work);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+tlc59116_destroy_devices(struct tlc59116_priv *priv, unsigned int i)
|
||||
+{
|
||||
+ while (--i >= 0) {
|
||||
+ if (priv->leds[i].active) {
|
||||
+ led_classdev_unregister(&priv->leds[i].ldev);
|
||||
+ cancel_work_sync(&priv->leds[i].work);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tlc59116_configure(struct device *dev,
|
||||
+ struct tlc59116_priv *priv,
|
||||
+ struct regmap *regmap)
|
||||
+{
|
||||
+ unsigned int i;
|
||||
+ int err = 0;
|
||||
+
|
||||
+ tlc59116_set_mode(regmap, MODE2_DIM);
|
||||
+ for (i = 0; i < TLC59116_LEDS; i++) {
|
||||
+ struct tlc59116_led *led = &priv->leds[i];
|
||||
+
|
||||
+ if (!led->active)
|
||||
+ continue;
|
||||
+
|
||||
+ led->regmap = regmap;
|
||||
+ led->led_no = i;
|
||||
+ led->ldev.brightness_set = tlc59116_led_set;
|
||||
+ led->ldev.max_brightness = LED_FULL;
|
||||
+ INIT_WORK(&led->work, tlc59116_led_work);
|
||||
+ err = led_classdev_register(dev, &led->ldev);
|
||||
+ if (err < 0) {
|
||||
+ dev_err(dev, "couldn't register LED %s\n",
|
||||
+ led->ldev.name);
|
||||
+ goto exit;
|
||||
+ }
|
||||
+ tlc59116_set_led(led, TLC59116_DIM);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+exit:
|
||||
+ tlc59116_destroy_devices(priv, i);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static const struct regmap_config tlc59116_regmap = {
|
||||
+ .reg_bits = 8,
|
||||
+ .val_bits = 8,
|
||||
+ .max_register = 0x1e,
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+tlc59116_probe(struct i2c_client *client,
|
||||
+ const struct i2c_device_id *id)
|
||||
+{
|
||||
+ struct tlc59116_priv *priv = i2c_get_clientdata(client);
|
||||
+ struct device *dev = &client->dev;
|
||||
+ struct device_node *np = client->dev.of_node, *child;
|
||||
+ struct regmap *regmap;
|
||||
+ int err, count, reg;
|
||||
+
|
||||
+ count = of_get_child_count(np);
|
||||
+ if (!count || count > TLC59116_LEDS)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!i2c_check_functionality(client->adapter,
|
||||
+ I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
+ return -EIO;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ regmap = devm_regmap_init_i2c(client, &tlc59116_regmap);
|
||||
+ if (IS_ERR(regmap)) {
|
||||
+ err = PTR_ERR(regmap);
|
||||
+ dev_err(dev, "Failed to allocate register map: %d\n", err);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ i2c_set_clientdata(client, priv);
|
||||
+
|
||||
+ for_each_child_of_node(np, child) {
|
||||
+ err = of_property_read_u32(child, "reg", ®);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ if (reg < 0 || reg >= TLC59116_LEDS)
|
||||
+ return -EINVAL;
|
||||
+ if (priv->leds[reg].active)
|
||||
+ return -EINVAL;
|
||||
+ priv->leds[reg].active = true;
|
||||
+ priv->leds[reg].ldev.name =
|
||||
+ of_get_property(child, "label", NULL) ? : child->name;
|
||||
+ priv->leds[reg].ldev.default_trigger =
|
||||
+ of_get_property(child, "linux,default-trigger", NULL);
|
||||
+ }
|
||||
+ return tlc59116_configure(dev, priv, regmap);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+tlc59116_remove(struct i2c_client *client)
|
||||
+{
|
||||
+ struct tlc59116_priv *priv = i2c_get_clientdata(client);
|
||||
+
|
||||
+ tlc59116_destroy_devices(priv, TLC59116_LEDS);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id of_tlc59116_leds_match[] = {
|
||||
+ { .compatible = "ti,tlc59116", },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, of_tlc59116_leds_match);
|
||||
+
|
||||
+static const struct i2c_device_id tlc59116_id[] = {
|
||||
+ { "tlc59116" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(i2c, tlc59116_id);
|
||||
+
|
||||
+static struct i2c_driver tlc59116_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "tlc59116",
|
||||
+ .of_match_table = of_match_ptr(of_tlc59116_leds_match),
|
||||
+ },
|
||||
+ .probe = tlc59116_probe,
|
||||
+ .remove = tlc59116_remove,
|
||||
+ .id_table = tlc59116_id,
|
||||
+};
|
||||
+
|
||||
+module_i2c_driver(tlc59116_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
|
||||
+MODULE_LICENSE("GPL");
|
||||
+MODULE_DESCRIPTION("TLC59116 LED driver");
|
|
@ -0,0 +1,153 @@
|
|||
The WRT1900AC among other Linksys routers uses a dual-firmware layout.
|
||||
The bootloader passes the active rootfs in bootargs and also sets the
|
||||
rootfstype to jffs2 - which is clearly something we don't want.
|
||||
|
||||
Rename both root= and rootfstype= variables to avoid issues and also
|
||||
use the former to dynamically rename the active partition to "ubi".
|
||||
|
||||
|
||||
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
|
||||
|
||||
--- a/arch/arm/boot/compressed/atags_to_fdt.c
|
||||
+++ b/arch/arm/boot/compressed/atags_to_fdt.c
|
||||
@@ -66,6 +66,18 @@ static uint32_t get_cell_size(const void
|
||||
return cell_size;
|
||||
}
|
||||
|
||||
+static void mangle_bootargs(void *fdt, const char *fdt_cmdline)
|
||||
+{
|
||||
+ char *rootdev;
|
||||
+ char *rootfs;
|
||||
+
|
||||
+ rootdev = strstr(fdt_cmdline, "root=/dev/mtdblock");
|
||||
+ strncpy(rootdev, "uboot_active_root=", 18);
|
||||
+
|
||||
+ rootfs = strstr(fdt_cmdline, "rootfstype");
|
||||
+ strncpy(rootfs, "origfstype", 10);
|
||||
+}
|
||||
+
|
||||
static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
|
||||
{
|
||||
char cmdline[COMMAND_LINE_SIZE];
|
||||
@@ -135,6 +147,9 @@ int atags_to_fdt(void *atag_list, void *
|
||||
|
||||
for_each_tag(atag, atag_list) {
|
||||
if (atag->hdr.tag == ATAG_CMDLINE) {
|
||||
+ /* Rename the original root= and rootfstype= options */
|
||||
+ mangle_bootargs(fdt,
|
||||
+ atag->u.cmdline.cmdline);
|
||||
/* Append the ATAGS command line to the device tree
|
||||
* command line.
|
||||
* NB: This means that if the same parameter is set in
|
||||
--- a/arch/arm/boot/compressed/string.c
|
||||
+++ b/arch/arm/boot/compressed/string.c
|
||||
@@ -111,6 +111,53 @@ char *strchr(const char *s, int c)
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * strncpy - Copy a length-limited, %NUL-terminated string
|
||||
+ * @dest: Where to copy the string to
|
||||
+ * @src: Where to copy the string from
|
||||
+ * @count: The maximum number of bytes to copy
|
||||
+ *
|
||||
+ * The result is not %NUL-terminated if the source exceeds
|
||||
+ * @count bytes.
|
||||
+ *
|
||||
+ * In the case where the length of @src is less than that of
|
||||
+ * count, the remainder of @dest will be padded with %NUL.
|
||||
+ *
|
||||
+ */
|
||||
+char *strncpy(char *dest, const char *src, size_t count)
|
||||
+{
|
||||
+ char *tmp = dest;
|
||||
+
|
||||
+ while (count) {
|
||||
+ if ((*tmp = *src) != 0)
|
||||
+ src++;
|
||||
+ tmp++;
|
||||
+ count--;
|
||||
+ }
|
||||
+ return dest;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * strstr - Find the first substring in a %NUL terminated string
|
||||
+ * @s1: The string to be searched
|
||||
+ * @s2: The string to search for
|
||||
+ */
|
||||
+char *strstr(const char *s1, const char *s2)
|
||||
+{
|
||||
+ size_t l1, l2;
|
||||
+
|
||||
+ l2 = strlen(s2);
|
||||
+ if (!l2)
|
||||
+ return (char *)s1;
|
||||
+ l1 = strlen(s1);
|
||||
+ while (l1 >= l2) {
|
||||
+ l1--;
|
||||
+ if (!memcmp(s1, s2, l2))
|
||||
+ return (char *)s1;
|
||||
+ s1++;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
#undef memset
|
||||
|
||||
void *memset(void *s, int c, size_t count)
|
||||
--- a/drivers/mtd/ofpart.c
|
||||
+++ b/drivers/mtd/ofpart.c
|
||||
@@ -25,12 +25,15 @@ static bool node_has_compatible(struct d
|
||||
return of_get_property(pp, "compatible", NULL);
|
||||
}
|
||||
|
||||
+static int uboot_active_root;
|
||||
+
|
||||
static int parse_ofpart_partitions(struct mtd_info *master,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct device_node *node;
|
||||
const char *partname;
|
||||
+ const char *owrtpart = "ubi";
|
||||
struct device_node *pp;
|
||||
int nr_parts, i;
|
||||
|
||||
@@ -78,9 +81,15 @@ static int parse_ofpart_partitions(struc
|
||||
(*pparts)[i].offset = of_read_number(reg, a_cells);
|
||||
(*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
|
||||
|
||||
- partname = of_get_property(pp, "label", &len);
|
||||
- if (!partname)
|
||||
- partname = of_get_property(pp, "name", &len);
|
||||
+ if (uboot_active_root && (i == uboot_active_root)) {
|
||||
+ partname = owrtpart;
|
||||
+ } else {
|
||||
+ partname = of_get_property(pp, "label", &len);
|
||||
+
|
||||
+ if (!partname)
|
||||
+ partname = of_get_property(pp, "name", &len);
|
||||
+ }
|
||||
+
|
||||
(*pparts)[i].name = partname;
|
||||
|
||||
if (of_get_property(pp, "read-only", &len))
|
||||
@@ -178,6 +187,18 @@ static int __init ofpart_parser_init(voi
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int __init active_root(char *str)
|
||||
+{
|
||||
+ get_option(&str, &uboot_active_root);
|
||||
+
|
||||
+ if (!uboot_active_root)
|
||||
+ return 1;
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+__setup("uboot_active_root=", active_root);
|
||||
+
|
||||
static void __exit ofpart_parser_exit(void)
|
||||
{
|
||||
deregister_mtd_parser(&ofpart_parser);
|
|
@ -0,0 +1,15 @@
|
|||
--- a/arch/arm/boot/dts/armada-xp.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp.dtsi
|
||||
@@ -54,12 +54,10 @@
|
||||
};
|
||||
|
||||
i2c0: i2c@11000 {
|
||||
- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
|
||||
reg = <0x11000 0x100>;
|
||||
};
|
||||
|
||||
i2c1: i2c@11100 {
|
||||
- compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
|
||||
reg = <0x11100 0x100>;
|
||||
};
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
Wrap some long lines.
|
||||
Prefer seq_puts() over seq_printf().
|
||||
space to tab conversions.
|
||||
Spelling error fix.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
drivers/gpio/gpio-mvebu.c | 77 ++++++++++++++++++++++++++---------------------
|
||||
1 file changed, 42 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/drivers/gpio/gpio-mvebu.c
|
||||
+++ b/drivers/gpio/gpio-mvebu.c
|
||||
@@ -59,7 +59,7 @@
|
||||
#define GPIO_LEVEL_MASK_OFF 0x001c
|
||||
|
||||
/* The MV78200 has per-CPU registers for edge mask and level mask */
|
||||
-#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
|
||||
+#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18)
|
||||
#define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C)
|
||||
|
||||
/* The Armada XP has per-CPU registers for interrupt cause, interrupt
|
||||
@@ -69,11 +69,11 @@
|
||||
#define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4)
|
||||
#define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4)
|
||||
|
||||
-#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
|
||||
-#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
|
||||
+#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1
|
||||
+#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2
|
||||
#define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3
|
||||
|
||||
-#define MVEBU_MAX_GPIO_PER_BANK 32
|
||||
+#define MVEBU_MAX_GPIO_PER_BANK 32
|
||||
|
||||
struct mvebu_gpio_chip {
|
||||
struct gpio_chip chip;
|
||||
@@ -82,9 +82,9 @@ struct mvebu_gpio_chip {
|
||||
void __iomem *percpu_membase;
|
||||
int irqbase;
|
||||
struct irq_domain *domain;
|
||||
- int soc_variant;
|
||||
+ int soc_variant;
|
||||
|
||||
- /* Used to preserve GPIO registers accross suspend/resume */
|
||||
+ /* Used to preserve GPIO registers across suspend/resume */
|
||||
u32 out_reg;
|
||||
u32 io_conf_reg;
|
||||
u32 blink_en_reg;
|
||||
@@ -107,7 +107,8 @@ static inline void __iomem *mvebu_gpiore
|
||||
return mvchip->membase + GPIO_BLINK_EN_OFF;
|
||||
}
|
||||
|
||||
-static inline void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
|
||||
+static inline void __iomem *
|
||||
+mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip)
|
||||
{
|
||||
return mvchip->membase + GPIO_IO_CONF_OFF;
|
||||
}
|
||||
@@ -117,12 +118,14 @@ static inline void __iomem *mvebu_gpiore
|
||||
return mvchip->membase + GPIO_IN_POL_OFF;
|
||||
}
|
||||
|
||||
-static inline void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
|
||||
+static inline void __iomem *
|
||||
+mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip)
|
||||
{
|
||||
return mvchip->membase + GPIO_DATA_IN_OFF;
|
||||
}
|
||||
|
||||
-static inline void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
|
||||
+static inline void __iomem *
|
||||
+mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
@@ -132,13 +135,15 @@ static inline void __iomem *mvebu_gpiore
|
||||
return mvchip->membase + GPIO_EDGE_CAUSE_OFF;
|
||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||
cpu = smp_processor_id();
|
||||
- return mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
|
||||
+ return mvchip->percpu_membase +
|
||||
+ GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu);
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
-static inline void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
|
||||
+static inline void __iomem *
|
||||
+mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
@@ -150,7 +155,8 @@ static inline void __iomem *mvebu_gpiore
|
||||
return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu);
|
||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||
cpu = smp_processor_id();
|
||||
- return mvchip->percpu_membase + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
|
||||
+ return mvchip->percpu_membase +
|
||||
+ GPIO_EDGE_MASK_ARMADAXP_OFF(cpu);
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@@ -168,7 +174,8 @@ static void __iomem *mvebu_gpioreg_level
|
||||
return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu);
|
||||
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
|
||||
cpu = smp_processor_id();
|
||||
- return mvchip->percpu_membase + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
|
||||
+ return mvchip->percpu_membase +
|
||||
+ GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu);
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@@ -364,22 +371,22 @@ static void mvebu_gpio_level_irq_unmask(
|
||||
* value of the line or the opposite value.
|
||||
*
|
||||
* Level IRQ handlers: DATA_IN is used directly as cause register.
|
||||
- * Interrupt are masked by LEVEL_MASK registers.
|
||||
+ * Interrupt are masked by LEVEL_MASK registers.
|
||||
* Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE.
|
||||
- * Interrupt are masked by EDGE_MASK registers.
|
||||
+ * Interrupt are masked by EDGE_MASK registers.
|
||||
* Both-edge handlers: Similar to regular Edge handlers, but also swaps
|
||||
- * the polarity to catch the next line transaction.
|
||||
- * This is a race condition that might not perfectly
|
||||
- * work on some use cases.
|
||||
+ * the polarity to catch the next line transaction.
|
||||
+ * This is a race condition that might not perfectly
|
||||
+ * work on some use cases.
|
||||
*
|
||||
* Every eight GPIO lines are grouped (OR'ed) before going up to main
|
||||
* cause register.
|
||||
*
|
||||
- * EDGE cause mask
|
||||
- * data-in /--------| |-----| |----\
|
||||
- * -----| |----- ---- to main cause reg
|
||||
- * X \----------------| |----/
|
||||
- * polarity LEVEL mask
|
||||
+ * EDGE cause mask
|
||||
+ * data-in /--------| |-----| |----\
|
||||
+ * -----| |----- ---- to main cause reg
|
||||
+ * X \----------------| |----/
|
||||
+ * polarity LEVEL mask
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
@@ -394,9 +401,8 @@ static int mvebu_gpio_irq_set_type(struc
|
||||
pin = d->hwirq;
|
||||
|
||||
u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin);
|
||||
- if (!u) {
|
||||
+ if (!u)
|
||||
return -EINVAL;
|
||||
- }
|
||||
|
||||
type &= IRQ_TYPE_SENSE_MASK;
|
||||
if (type == IRQ_TYPE_NONE)
|
||||
@@ -529,13 +535,13 @@ static void mvebu_gpio_dbg_show(struct s
|
||||
(data_in ^ in_pol) & msk ? "hi" : "lo",
|
||||
in_pol & msk ? "lo" : "hi");
|
||||
if (!((edg_msk | lvl_msk) & msk)) {
|
||||
- seq_printf(s, " disabled\n");
|
||||
+ seq_puts(s, " disabled\n");
|
||||
continue;
|
||||
}
|
||||
if (edg_msk & msk)
|
||||
- seq_printf(s, " edge ");
|
||||
+ seq_puts(s, " edge ");
|
||||
if (lvl_msk & msk)
|
||||
- seq_printf(s, " level");
|
||||
+ seq_puts(s, " level");
|
||||
seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear ");
|
||||
}
|
||||
}
|
||||
@@ -546,15 +552,15 @@ static void mvebu_gpio_dbg_show(struct s
|
||||
static const struct of_device_id mvebu_gpio_of_match[] = {
|
||||
{
|
||||
.compatible = "marvell,orion-gpio",
|
||||
- .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
|
||||
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION,
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,mv78200-gpio",
|
||||
- .data = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200,
|
||||
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200,
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,armadaxp-gpio",
|
||||
- .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
|
||||
+ .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP,
|
||||
},
|
||||
{
|
||||
/* sentinel */
|
||||
@@ -668,7 +674,8 @@ static int mvebu_gpio_probe(struct platf
|
||||
else
|
||||
soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION;
|
||||
|
||||
- mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL);
|
||||
+ mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip),
|
||||
+ GFP_KERNEL);
|
||||
if (!mvchip)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -767,8 +774,8 @@ static int mvebu_gpio_probe(struct platf
|
||||
* interrupt handlers, with each handler dealing with 8 GPIO
|
||||
* pins. */
|
||||
for (i = 0; i < 4; i++) {
|
||||
- int irq;
|
||||
- irq = platform_get_irq(pdev, i);
|
||||
+ int irq = platform_get_irq(pdev, i);
|
||||
+
|
||||
if (irq < 0)
|
||||
continue;
|
||||
irq_set_handler_data(irq, mvchip);
|
||||
@@ -827,7 +834,7 @@ static int mvebu_gpio_probe(struct platf
|
||||
|
||||
static struct platform_driver mvebu_gpio_driver = {
|
||||
.driver = {
|
||||
- .name = "mvebu-gpio",
|
||||
+ .name = "mvebu-gpio",
|
||||
.of_match_table = mvebu_gpio_of_match,
|
||||
},
|
||||
.probe = mvebu_gpio_probe,
|
|
@ -0,0 +1,63 @@
|
|||
Ensure that when there is an error during probe that the gpiochip is
|
||||
removed and the generic irq chip is removed.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
drivers/gpio/gpio-mvebu.c | 23 +++++++++++++++++------
|
||||
1 file changed, 17 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/gpio/gpio-mvebu.c
|
||||
+++ b/drivers/gpio/gpio-mvebu.c
|
||||
@@ -667,6 +667,7 @@ static int mvebu_gpio_probe(struct platf
|
||||
unsigned int ngpios;
|
||||
int soc_variant;
|
||||
int i, cpu, id;
|
||||
+ int err;
|
||||
|
||||
match = of_match_device(mvebu_gpio_of_match, &pdev->dev);
|
||||
if (match)
|
||||
@@ -785,14 +786,16 @@ static int mvebu_gpio_probe(struct platf
|
||||
mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1);
|
||||
if (mvchip->irqbase < 0) {
|
||||
dev_err(&pdev->dev, "no irqs\n");
|
||||
- return mvchip->irqbase;
|
||||
+ err = mvchip->irqbase;
|
||||
+ goto err_gpiochip_add;
|
||||
}
|
||||
|
||||
gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase,
|
||||
mvchip->membase, handle_level_irq);
|
||||
if (!gc) {
|
||||
dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n");
|
||||
- return -ENOMEM;
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_gpiochip_add;
|
||||
}
|
||||
|
||||
gc->private = mvchip;
|
||||
@@ -823,13 +826,21 @@ static int mvebu_gpio_probe(struct platf
|
||||
if (!mvchip->domain) {
|
||||
dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n",
|
||||
mvchip->chip.label);
|
||||
- irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
|
||||
- IRQ_LEVEL | IRQ_NOPROBE);
|
||||
- kfree(gc);
|
||||
- return -ENODEV;
|
||||
+ err = -ENODEV;
|
||||
+ goto err_generic_chip;
|
||||
}
|
||||
|
||||
return 0;
|
||||
+
|
||||
+err_generic_chip:
|
||||
+ irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
|
||||
+ IRQ_LEVEL | IRQ_NOPROBE);
|
||||
+ kfree(gc);
|
||||
+
|
||||
+err_gpiochip_add:
|
||||
+ gpiochip_remove(&mvchip->chip);
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static struct platform_driver mvebu_gpio_driver = {
|
|
@ -0,0 +1,433 @@
|
|||
Armada 370/XP devices can 'blink' gpio lines with a configurable on
|
||||
and off period. This can be modelled as a PWM.
|
||||
|
||||
However, there are only two sets of PWM configuration registers for
|
||||
all the gpio lines. This driver simply allows a single gpio line per
|
||||
gpio chip of 32 lines to be used as a PWM. Attempts to use more return
|
||||
EBUSY.
|
||||
|
||||
Due to the interleaving of registers it is not simple to separate the
|
||||
PWM driver from the gpio driver. Thus the gpio driver has been
|
||||
extended with a PWM driver.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
drivers/gpio/Kconfig | 5 ++
|
||||
drivers/gpio/Makefile | 1 +
|
||||
drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++
|
||||
drivers/gpio/gpio-mvebu.c | 37 +++-----
|
||||
drivers/gpio/gpio-mvebu.h | 79 +++++++++++++++++
|
||||
5 files changed, 299 insertions(+), 25 deletions(-)
|
||||
create mode 100644 drivers/gpio/gpio-mvebu-pwm.c
|
||||
create mode 100644 drivers/gpio/gpio-mvebu.h
|
||||
|
||||
--- a/drivers/gpio/Kconfig
|
||||
+++ b/drivers/gpio/Kconfig
|
||||
@@ -239,6 +239,11 @@ config GPIO_MVEBU
|
||||
select GPIO_GENERIC
|
||||
select GENERIC_IRQ_CHIP
|
||||
|
||||
+config GPIO_MVEBU_PWM
|
||||
+ def_bool y
|
||||
+ depends on GPIO_MVEBU
|
||||
+ depends on PWM
|
||||
+
|
||||
config GPIO_MXC
|
||||
def_bool y
|
||||
depends on ARCH_MXC
|
||||
--- a/drivers/gpio/Makefile
|
||||
+++ b/drivers/gpio/Makefile
|
||||
@@ -60,6 +60,7 @@ obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o
|
||||
obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o
|
||||
obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o
|
||||
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
|
||||
+obj-$(CONFIG_GPIO_MVEBU_PWM) += gpio-mvebu-pwm.o
|
||||
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
|
||||
obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o
|
||||
obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/gpio/gpio-mvebu-pwm.c
|
||||
@@ -0,0 +1,202 @@
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/gpio.h>
|
||||
+#include <linux/pwm.h>
|
||||
+#include <linux/clk.h>
|
||||
+#include <linux/platform_device.h>
|
||||
+#include "gpio-mvebu.h"
|
||||
+#include "gpiolib.h"
|
||||
+
|
||||
+static void __iomem *mvebu_gpioreg_blink_select(struct mvebu_gpio_chip *mvchip)
|
||||
+{
|
||||
+ return mvchip->membase + GPIO_BLINK_CNT_SELECT;
|
||||
+}
|
||||
+
|
||||
+static inline struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip)
|
||||
+{
|
||||
+ return container_of(chip, struct mvebu_pwm, chip);
|
||||
+}
|
||||
+
|
||||
+static inline struct mvebu_gpio_chip *to_mvchip(struct mvebu_pwm *pwm)
|
||||
+{
|
||||
+ return container_of(pwm, struct mvebu_gpio_chip, pwm);
|
||||
+}
|
||||
+
|
||||
+static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwmd)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
|
||||
+ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
|
||||
+ struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
|
||||
+ unsigned long flags;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ spin_lock_irqsave(&pwm->lock, flags);
|
||||
+ if (pwm->used) {
|
||||
+ ret = -EBUSY;
|
||||
+ } else {
|
||||
+ if (!desc) {
|
||||
+ ret = -ENODEV;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ ret = gpiod_request(desc, "mvebu-pwm");
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+
|
||||
+ ret = gpiod_direction_output(desc, 0);
|
||||
+ if (ret) {
|
||||
+ gpiod_free(desc);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ pwm->pin = pwmd->pwm - mvchip->chip.base;
|
||||
+ pwm->used = true;
|
||||
+ }
|
||||
+
|
||||
+out:
|
||||
+ spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwmd)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
|
||||
+ struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
|
||||
+ unsigned long flags;
|
||||
+
|
||||
+ spin_lock_irqsave(&pwm->lock, flags);
|
||||
+ gpiod_free(desc);
|
||||
+ pwm->used = false;
|
||||
+ spin_unlock_irqrestore(&pwm->lock, flags);
|
||||
+}
|
||||
+
|
||||
+static int mvebu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwmd,
|
||||
+ int duty_ns, int period_ns)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
|
||||
+ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
|
||||
+ unsigned int on, off;
|
||||
+ unsigned long long val;
|
||||
+ u32 u;
|
||||
+
|
||||
+ val = (unsigned long long) pwm->clk_rate * duty_ns;
|
||||
+ do_div(val, NSEC_PER_SEC);
|
||||
+ if (val > UINT_MAX)
|
||||
+ return -EINVAL;
|
||||
+ if (val)
|
||||
+ on = val;
|
||||
+ else
|
||||
+ on = 1;
|
||||
+
|
||||
+ val = (unsigned long long) pwm->clk_rate * (period_ns - duty_ns);
|
||||
+ do_div(val, NSEC_PER_SEC);
|
||||
+ if (val > UINT_MAX)
|
||||
+ return -EINVAL;
|
||||
+ if (val)
|
||||
+ off = val;
|
||||
+ else
|
||||
+ off = 1;
|
||||
+
|
||||
+ u = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
|
||||
+ u &= ~(1 << pwm->pin);
|
||||
+ u |= (pwm->id << pwm->pin);
|
||||
+ writel_relaxed(u, mvebu_gpioreg_blink_select(mvchip));
|
||||
+
|
||||
+ writel_relaxed(on, pwm->membase + BLINK_ON_DURATION);
|
||||
+ writel_relaxed(off, pwm->membase + BLINK_OFF_DURATION);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int mvebu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwmd)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
|
||||
+ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
|
||||
+
|
||||
+ mvebu_gpio_blink(&mvchip->chip, pwm->pin, 1);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void mvebu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwmd)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
|
||||
+ struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
|
||||
+
|
||||
+ mvebu_gpio_blink(&mvchip->chip, pwm->pin, 0);
|
||||
+}
|
||||
+
|
||||
+static const struct pwm_ops mvebu_pwm_ops = {
|
||||
+ .request = mvebu_pwm_request,
|
||||
+ .free = mvebu_pwm_free,
|
||||
+ .config = mvebu_pwm_config,
|
||||
+ .enable = mvebu_pwm_enable,
|
||||
+ .disable = mvebu_pwm_disable,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = &mvchip->pwm;
|
||||
+
|
||||
+ pwm->blink_select = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
|
||||
+ pwm->blink_on_duration =
|
||||
+ readl_relaxed(pwm->membase + BLINK_ON_DURATION);
|
||||
+ pwm->blink_off_duration =
|
||||
+ readl_relaxed(pwm->membase + BLINK_OFF_DURATION);
|
||||
+}
|
||||
+
|
||||
+void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
|
||||
+{
|
||||
+ struct mvebu_pwm *pwm = &mvchip->pwm;
|
||||
+
|
||||
+ writel_relaxed(pwm->blink_select, mvebu_gpioreg_blink_select(mvchip));
|
||||
+ writel_relaxed(pwm->blink_on_duration,
|
||||
+ pwm->membase + BLINK_ON_DURATION);
|
||||
+ writel_relaxed(pwm->blink_off_duration,
|
||||
+ pwm->membase + BLINK_OFF_DURATION);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Armada 370/XP has simple PWM support for gpio lines. Other SoCs
|
||||
+ * don't have this hardware. So if we don't have the necessary
|
||||
+ * resource, it is not an error.
|
||||
+ */
|
||||
+int mvebu_pwm_probe(struct platform_device *pdev,
|
||||
+ struct mvebu_gpio_chip *mvchip,
|
||||
+ int id)
|
||||
+{
|
||||
+ struct device *dev = &pdev->dev;
|
||||
+ struct mvebu_pwm *pwm = &mvchip->pwm;
|
||||
+ struct resource *res;
|
||||
+
|
||||
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
|
||||
+ if (!res)
|
||||
+ return 0;
|
||||
+
|
||||
+ mvchip->pwm.membase = devm_ioremap_resource(&pdev->dev, res);
|
||||
+ if (IS_ERR(mvchip->pwm.membase))
|
||||
+ return PTR_ERR(mvchip->percpu_membase);
|
||||
+
|
||||
+ if (id < 0 || id > 1)
|
||||
+ return -EINVAL;
|
||||
+ pwm->id = id;
|
||||
+
|
||||
+ if (IS_ERR(mvchip->clk))
|
||||
+ return PTR_ERR(mvchip->clk);
|
||||
+
|
||||
+ pwm->clk_rate = clk_get_rate(mvchip->clk);
|
||||
+ if (!pwm->clk_rate) {
|
||||
+ dev_err(dev, "failed to get clock rate\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ pwm->chip.dev = dev;
|
||||
+ pwm->chip.ops = &mvebu_pwm_ops;
|
||||
+ pwm->chip.base = mvchip->chip.base;
|
||||
+ pwm->chip.npwm = mvchip->chip.ngpio;
|
||||
+ pwm->chip.can_sleep = false;
|
||||
+
|
||||
+ spin_lock_init(&pwm->lock);
|
||||
+
|
||||
+ return pwmchip_add(&pwm->chip);
|
||||
+}
|
||||
--- a/drivers/gpio/gpio-mvebu.c
|
||||
+++ b/drivers/gpio/gpio-mvebu.c
|
||||
@@ -42,10 +42,11 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
+#include <linux/pwm.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
-
|
||||
+#include "gpio-mvebu.h"
|
||||
/*
|
||||
* GPIO unit register offsets.
|
||||
*/
|
||||
@@ -75,24 +76,6 @@
|
||||
|
||||
#define MVEBU_MAX_GPIO_PER_BANK 32
|
||||
|
||||
-struct mvebu_gpio_chip {
|
||||
- struct gpio_chip chip;
|
||||
- spinlock_t lock;
|
||||
- void __iomem *membase;
|
||||
- void __iomem *percpu_membase;
|
||||
- int irqbase;
|
||||
- struct irq_domain *domain;
|
||||
- int soc_variant;
|
||||
-
|
||||
- /* Used to preserve GPIO registers across suspend/resume */
|
||||
- u32 out_reg;
|
||||
- u32 io_conf_reg;
|
||||
- u32 blink_en_reg;
|
||||
- u32 in_pol_reg;
|
||||
- u32 edge_mask_regs[4];
|
||||
- u32 level_mask_regs[4];
|
||||
-};
|
||||
-
|
||||
/*
|
||||
* Functions returning addresses of individual registers for a given
|
||||
* GPIO controller.
|
||||
@@ -228,7 +211,7 @@ static int mvebu_gpio_get(struct gpio_ch
|
||||
return (u >> pin) & 1;
|
||||
}
|
||||
|
||||
-static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
|
||||
+void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
|
||||
{
|
||||
struct mvebu_gpio_chip *mvchip =
|
||||
container_of(chip, struct mvebu_gpio_chip, chip);
|
||||
@@ -609,6 +592,8 @@ static int mvebu_gpio_suspend(struct pla
|
||||
BUG();
|
||||
}
|
||||
|
||||
+ mvebu_pwm_suspend(mvchip);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -652,6 +637,8 @@ static int mvebu_gpio_resume(struct plat
|
||||
BUG();
|
||||
}
|
||||
|
||||
+ mvebu_pwm_resume(mvchip);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -663,7 +650,6 @@ static int mvebu_gpio_probe(struct platf
|
||||
struct resource *res;
|
||||
struct irq_chip_generic *gc;
|
||||
struct irq_chip_type *ct;
|
||||
- struct clk *clk;
|
||||
unsigned int ngpios;
|
||||
int soc_variant;
|
||||
int i, cpu, id;
|
||||
@@ -693,10 +679,10 @@ static int mvebu_gpio_probe(struct platf
|
||||
return id;
|
||||
}
|
||||
|
||||
- clk = devm_clk_get(&pdev->dev, NULL);
|
||||
+ mvchip->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
/* Not all SoCs require a clock.*/
|
||||
- if (!IS_ERR(clk))
|
||||
- clk_prepare_enable(clk);
|
||||
+ if (!IS_ERR(mvchip->clk))
|
||||
+ clk_prepare_enable(mvchip->clk);
|
||||
|
||||
mvchip->soc_variant = soc_variant;
|
||||
mvchip->chip.label = dev_name(&pdev->dev);
|
||||
@@ -830,7 +816,8 @@ static int mvebu_gpio_probe(struct platf
|
||||
goto err_generic_chip;
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ /* Armada 370/XP has simple PWM support for gpio lines */
|
||||
+ return mvebu_pwm_probe(pdev, mvchip, id);
|
||||
|
||||
err_generic_chip:
|
||||
irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST,
|
||||
--- /dev/null
|
||||
+++ b/drivers/gpio/gpio-mvebu.h
|
||||
@@ -0,0 +1,79 @@
|
||||
+/*
|
||||
+ * Interface between MVEBU GPIO driver and PWM driver for GPIO pins
|
||||
+ *
|
||||
+ * Copyright (C) 2015, Andrew Lunn <andrew@lunn.ch>
|
||||
+ *
|
||||
+ * 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 MVEBU_GPIO_PWM_H
|
||||
+#define MVEBU_GPIO_PWM_H
|
||||
+
|
||||
+#define BLINK_ON_DURATION 0x0
|
||||
+#define BLINK_OFF_DURATION 0x4
|
||||
+#define GPIO_BLINK_CNT_SELECT 0x0020
|
||||
+
|
||||
+struct mvebu_pwm {
|
||||
+ void __iomem *membase;
|
||||
+ unsigned long clk_rate;
|
||||
+ bool used;
|
||||
+ unsigned pin;
|
||||
+ struct pwm_chip chip;
|
||||
+ int id;
|
||||
+ spinlock_t lock;
|
||||
+
|
||||
+ /* Used to preserve GPIO/PWM registers across suspend /
|
||||
+ * resume */
|
||||
+ u32 blink_select;
|
||||
+ u32 blink_on_duration;
|
||||
+ u32 blink_off_duration;
|
||||
+};
|
||||
+
|
||||
+struct mvebu_gpio_chip {
|
||||
+ struct gpio_chip chip;
|
||||
+ spinlock_t lock;
|
||||
+ void __iomem *membase;
|
||||
+ void __iomem *percpu_membase;
|
||||
+ int irqbase;
|
||||
+ struct irq_domain *domain;
|
||||
+ int soc_variant;
|
||||
+ struct clk *clk;
|
||||
+#ifdef CONFIG_PWM
|
||||
+ struct mvebu_pwm pwm;
|
||||
+#endif
|
||||
+ /* Used to preserve GPIO registers across suspend/resume */
|
||||
+ u32 out_reg;
|
||||
+ u32 io_conf_reg;
|
||||
+ u32 blink_en_reg;
|
||||
+ u32 in_pol_reg;
|
||||
+ u32 edge_mask_regs[4];
|
||||
+ u32 level_mask_regs[4];
|
||||
+};
|
||||
+
|
||||
+void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value);
|
||||
+
|
||||
+#ifdef CONFIG_PWM
|
||||
+int mvebu_pwm_probe(struct platform_device *pdev,
|
||||
+ struct mvebu_gpio_chip *mvchip,
|
||||
+ int id);
|
||||
+void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip);
|
||||
+void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip);
|
||||
+#else
|
||||
+int mvebu_pwm_probe(struct platform_device *pdev,
|
||||
+ struct mvebu_gpio_chip *mvchip,
|
||||
+ int id)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
|
||||
+{
|
||||
+}
|
||||
+#endif
|
||||
+#endif
|
|
@ -0,0 +1,52 @@
|
|||
Document the optional parameters needed for PWM operation of gpio
|
||||
lines.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
.../devicetree/bindings/gpio/gpio-mvebu.txt | 31 ++++++++++++++++++++++
|
||||
1 file changed, 31 insertions(+)
|
||||
|
||||
--- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
|
||||
+++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
|
||||
@@ -38,6 +38,23 @@ Required properties:
|
||||
- #gpio-cells: Should be two. The first cell is the pin number. The
|
||||
second cell is reserved for flags, unused at the moment.
|
||||
|
||||
+Optional properties:
|
||||
+
|
||||
+In order to use the gpio lines in PWM mode, some additional optional
|
||||
+properties are required. Only Armada 370 and XP supports these
|
||||
+properties.
|
||||
+
|
||||
+- reg: an additional register set is needed, for the GPIO Blink
|
||||
+ Counter on/off registers.
|
||||
+
|
||||
+- reg-names: Must contain an entry "pwm" corresponding to the
|
||||
+ additional register range needed for pwm operation.
|
||||
+
|
||||
+- #pwm-cells: Should be two. The first cell is the pin number. The
|
||||
+ second cell is reserved for flags, unused at the moment.
|
||||
+
|
||||
+- clocks: Must be a phandle to the clock for the gpio controller.
|
||||
+
|
||||
Example:
|
||||
|
||||
gpio0: gpio@d0018100 {
|
||||
@@ -51,3 +68,17 @@ Example:
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <16>, <17>, <18>, <19>;
|
||||
};
|
||||
+
|
||||
+ gpio1: gpio@18140 {
|
||||
+ compatible = "marvell,orion-gpio";
|
||||
+ reg = <0x18140 0x40>, <0x181c8 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
+ ngpios = <17>;
|
||||
+ gpio-controller;
|
||||
+ #gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
+ interrupt-controller;
|
||||
+ #interrupt-cells = <2>;
|
||||
+ interrupts = <87>, <88>, <89>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
+ };
|
|
@ -0,0 +1,149 @@
|
|||
Add properties to the gpio nodes to allow them to be also used
|
||||
as pwm lines.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
arch/arm/boot/dts/armada-370.dtsi | 10 ++++++++--
|
||||
arch/arm/boot/dts/armada-xp-mv78230.dtsi | 10 ++++++++--
|
||||
arch/arm/boot/dts/armada-xp-mv78260.dtsi | 8 ++++++--
|
||||
arch/arm/boot/dts/armada-xp-mv78460.dtsi | 10 ++++++++--
|
||||
4 files changed, 30 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-370.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-370.dtsi
|
||||
@@ -123,24 +123,30 @@
|
||||
|
||||
gpio0: gpio@18100 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18100 0x40>;
|
||||
+ reg = <0x18100 0x40>, <0x181c0 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <82>, <83>, <84>, <85>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio1: gpio@18140 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18140 0x40>;
|
||||
+ reg = <0x18140 0x40>, <0x181c8 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <87>, <88>, <89>, <90>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio2: gpio@18180 {
|
||||
--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
|
||||
@@ -169,24 +169,30 @@
|
||||
internal-regs {
|
||||
gpio0: gpio@18100 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18100 0x40>;
|
||||
+ reg = <0x18100 0x40>, <0x181c0 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <82>, <83>, <84>, <85>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio1: gpio@18140 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18140 0x40>;
|
||||
+ reg = <0x18140 0x40>, <0x181c8 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <17>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <87>, <88>, <89>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
|
||||
@@ -253,24 +253,28 @@
|
||||
internal-regs {
|
||||
gpio0: gpio@18100 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18100 0x40>;
|
||||
+ reg = <0x18100 0x40>, <0x181c0 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <82>, <83>, <84>, <85>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio1: gpio@18140 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18140 0x40>;
|
||||
+ reg = <0x18140 0x40>, <0x181c8 0x08>;
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <87>, <88>, <89>, <90>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio2: gpio@18180 {
|
||||
--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
|
||||
@@ -291,24 +291,30 @@
|
||||
internal-regs {
|
||||
gpio0: gpio@18100 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18100 0x40>;
|
||||
+ reg = <0x18100 0x40>, <0x181c0 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <82>, <83>, <84>, <85>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio1: gpio@18140 {
|
||||
compatible = "marvell,orion-gpio";
|
||||
- reg = <0x18140 0x40>;
|
||||
+ reg = <0x18140 0x40>, <0x181c8 0x08>;
|
||||
+ reg-names = "gpio", "pwm";
|
||||
ngpios = <32>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
+ #pwm-cells = <2>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
interrupts = <87>, <88>, <89>, <90>;
|
||||
+ clocks = <&coreclk 0>;
|
||||
};
|
||||
|
||||
gpio2: gpio@18180 {
|
|
@ -0,0 +1,18 @@
|
|||
Now that the gpio driver also supports PWM operation, enable
|
||||
the PWM framework in mvebu_v7_defconfig.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
arch/arm/configs/mvebu_v7_defconfig | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/arch/arm/configs/mvebu_v7_defconfig
|
||||
+++ b/arch/arm/configs/mvebu_v7_defconfig
|
||||
@@ -116,6 +116,7 @@ CONFIG_DMADEVICES=y
|
||||
CONFIG_MV_XOR=y
|
||||
# CONFIG_IOMMU_SUPPORT is not set
|
||||
CONFIG_MEMORY=y
|
||||
+CONFIG_PWM=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_ISO9660_FS=y
|
||||
CONFIG_JOLIET=y
|
|
@ -0,0 +1,28 @@
|
|||
The mvebu gpio driver can also perform PWM on some pins. Us the
|
||||
pwm-fan driver to control the fan of the WRT1900AC, giving us fine
|
||||
grain control over its speed and hence noise.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
---
|
||||
arch/arm/boot/dts/armada-xp-wrt1900ac.dts | 8 +++-----
|
||||
1 file changed, 3 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/arch/arm/boot/dts/armada-xp-mamba.dts
|
||||
+++ b/arch/arm/boot/dts/armada-xp-mamba.dts
|
||||
@@ -302,13 +302,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
- gpio_fan {
|
||||
+ pwm_fan {
|
||||
/* SUNON HA4010V4-0000-C99 */
|
||||
- compatible = "gpio-fan";
|
||||
- gpios = <&gpio0 24 0>;
|
||||
|
||||
- gpio-fan,speed-map = <0 0
|
||||
- 4500 1>;
|
||||
+ compatible = "pwm-fan";
|
||||
+ pwms = <&gpio0 24 4000 0>;
|
||||
};
|
||||
|
||||
mvsw61xx {
|
Loading…
Reference in New Issue