preliminary support for the WRT1900AC (work in progress)

SVN-Revision: 41232
This commit is contained in:
Imre Kaloz 2014-06-17 15:13:10 +00:00
parent 099d998fcf
commit d775e4ef00
14 changed files with 1841 additions and 0 deletions

View file

@ -0,0 +1,294 @@
CONFIG_ALIGNMENT_TRAP=y
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
CONFIG_ARCH_HAS_TICK_BROADCAST=y
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=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_BIG_ENDIAN=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_ARM=y
CONFIG_ARMADA_370_CLK=y
CONFIG_ARMADA_370_XP_TIMER=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 is not set
CONFIG_ARM_L1_CACHE_SHIFT=6
CONFIG_ARM_L1_CACHE_SHIFT_6=y
# CONFIG_ARM_LPAE is not set
CONFIG_ARM_NR_BANKS=8
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_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_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_HAS_ASID=y
# CONFIG_CPU_ICACHE_DISABLE is not set
CONFIG_CPU_PABRT_V7=y
CONFIG_CPU_PJ4B=y
CONFIG_CPU_RMAP=y
CONFIG_CPU_TLB_V7=y
CONFIG_CPU_V7=y
CONFIG_CRC16=y
CONFIG_DCACHE_WORD_ACCESS=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_LL=y
CONFIG_DEBUG_LL_INCLUDE="debug/8250.S"
# CONFIG_DEBUG_LL_UART_8250 is not set
# CONFIG_DEBUG_LL_UART_PL01X is not set
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_EXT2_FS=y
CONFIG_EXT3_FS=y
CONFIG_FAT_FS=y
CONFIG_FIXED_PHY=y
CONFIG_FRAME_POINTER=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_NET_UTILS=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_SYSFS=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
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_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_I2C=m
CONFIG_I2C_BOARDINFO=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_ISO9660_FS=y
CONFIG_JBD=y
CONFIG_KTIME_SCALAR=y
CONFIG_LEDS_TLC59116=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_MACH_ARMADA_370=y
CONFIG_MACH_ARMADA_370_XP=y
CONFIG_MACH_ARMADA_XP=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_MARVELL_PHY=y
CONFIG_MDIO_BOARDINFO=y
CONFIG_MEMORY=y
CONFIG_MIGHT_HAVE_PCI=y
# CONFIG_MLX5_CORE is not set
CONFIG_MODULES_USE_ELF_REL=y
CONFIG_MSDOS_FS=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_SM_COMMON is not set
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_MV_XOR=y
CONFIG_NEED_DMA_MAP_STATE=y
# CONFIG_NEON is not set
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_RX_BUSY_POLL=y
CONFIG_NLS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_850=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_2=y
CONFIG_NLS_UTF8=y
CONFIG_NO_BOOTMEM=y
CONFIG_NR_CPUS=4
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_MDIO=y
CONFIG_OF_MTD=y
CONFIG_OF_NET=y
CONFIG_OF_PCI=y
CONFIG_OF_PCI_IRQ=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_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_PREEMPT_RCU is not set
CONFIG_PROC_DEVICETREE=y
CONFIG_RCU_STALL_COMMON=y
CONFIG_RFS_ACCEL=y
CONFIG_RPS=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_MV is not set
CONFIG_SCHED_HRTICK=y
# CONFIG_SCSI_DMA is not set
CONFIG_SERIAL_8250_DW=y
CONFIG_SMP=y
CONFIG_SMP_ON_UP=y
CONFIG_SPARSE_IRQ=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_ORION=y
CONFIG_STOP_MACHINE=y
CONFIG_SWIOTLB=y
# CONFIG_SWP_EMULATE is not set
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_UID16=y
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
CONFIG_USB_SUPPORT=y
CONFIG_USE_OF=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_VFAT_FS=y
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_ZBUD is not set
CONFIG_ZONE_DMA_FLAG=0

View file

@ -0,0 +1,242 @@
/*
* Device Tree file for the Linksys WRT1900AC (Mamba).
*
* Note: this board is shipped with a new generation boot loader that
* remaps internal registers at 0xf1000000. Therefore, if earlyprintk
* is used, the CONFIG_DEBUG_MVEBU_UART_ALTERNATE option should be
* used.
*
* Copyright (C) 2013 Marvell
*
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
*
* 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.
*/
/dts-v1/;
#include "armada-xp-mv78230.dtsi"
/ {
model = "Linksys WRT1900AC (Mamba)";
compatible = "linksys,mamba", "marvell,armadaxp-mv78230", "marvell,armadaxp", "marvell,armada-370-xp";
chosen {
bootargs = "console=ttyS0,115200 earlyprintk";
};
memory {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x10000000>; /* 256MB */
};
soc {
ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000>;
pcie-controller {
status = "okay";
/* Etron EJ168 USB 3.0 controller */
pcie@1,0 {
/* Port 0, Lane 0 */
status = "okay";
};
/* First mini-PCIe port */
pcie@2,0 {
/* Port 0, Lane 1 */
status = "okay";
};
/* Second mini-PCIe port */
pcie@3,0 {
/* Port 0, Lane 3 */
status = "okay";
};
};
internal-regs {
pinctrl {
pinctrl-0 = <&pmx_phy_int>;
pinctrl-names = "default";
pmx_ge0: pmx-ge0 {
marvell,pins = "mpp0", "mpp1", "mpp2", "mpp3",
"mpp4", "mpp5", "mpp6", "mpp7",
"mpp8", "mpp9", "mpp10", "mpp11";
marvell,function = "ge0";
};
pmx_ge1: pmx-ge1 {
marvell,pins = "mpp12", "mpp13", "mpp14", "mpp15",
"mpp16", "mpp17", "mpp18", "mpp19",
"mpp20", "mpp21", "mpp22", "mpp23";
marvell,function = "ge1";
};
pmx_keys: pmx-keys {
marvell,pins = "mpp33";
marvell,function = "gpio";
};
pmx_spi: pmx-spi {
marvell,pins = "mpp36", "mpp37", "mpp38", "mpp39";
marvell,function = "spi";
};
pmx_phy_int: pmx-phy-int {
marvell,pins = "mpp32";
marvell,function = "gpio";
};
};
serial@12000 {
clock-frequency = <250000000>;
status = "okay";
};
serial@12100 {
clock-frequency = <250000000>;
status = "okay";
};
sata@a0000 {
nr-ports = <1>;
status = "okay";
};
mdio {
disabled;
};
ethernet@70000 {
pinctrl-0 = <&pmx_ge0>;
pinctrl-names = "default";
status = "okay";
phy-mode = "rgmii-id";
fixed-link {
speed = <1000>;
full-duplex;
};
};
ethernet@74000 {
pinctrl-0 = <&pmx_ge1>;
pinctrl-names = "default";
status = "okay";
phy-mode = "rgmii-id";
fixed-link {
speed = <1000>;
full-duplex;
};
};
/* USB part of the eSATA/USB 2.0 port */
usb@50000 {
status = "okay";
};
i2c@11000 {
status = "okay";
clock-frequency = <100000>;
tlc59116@68 {
#gpio-cells = <2>;
compatible = "gpio,tlc59116";
reg = <0x68>;
gpio-controller;
};
};
nand@d0000 {
status = "okay";
num-cs = <1>;
marvell,nand-keep-config;
marvell,nand-enable-arbiter;
nand-on-flash-bbt;
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
partition@0 {
label = "u-boot";
reg = <0x0000000 0x100000>; /* 1MB */
read-only;
};
partition@100000 {
label = "u_env"; //u-boot-env?
reg = <0x100000 0x40000>; /* 256KB */
read-only;
};
partition@140000 {
label = "s_env";
reg = <0x140000 0x40000>; /* 256KB */
read-only;
};
partition@900000 {
label = "devinfo";
reg = <0x900000 0x100000>; /* 1MB */
};
partition@a00000 {
label = "kernel";
reg = <0xa00000 0x2800000>; /* 40MB */
};
partition@d00000 {
label = "rootfs";
reg = <0xd00000 0x2500000>; /* 37MB */
};
partition@3200000 {
label = "alt_kernel";
reg = <0x3200000 0x2800000>; /* 40MB */
};
partition@3500000 {
label = "alt_rootfs";
reg = <0x3500000 0x2500000>; /* 37MB */
};
/* Last MB is for the BBT, i.e. not writable */
partition@5a00000 {
label = "syscfg";
reg = <0x5a00000 0x2600000>; /* ?MB */
};
};
spi0: spi@10600 {
status = "okay";
pinctrl-0 = <&pmx_spi>;
pinctrl-names = "default";
spi-flash@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "mr25h256";
reg = <0>; /* Chip select 0 */
spi-max-frequency = <108000000>;
};
};
};
};
gpio_keys {
compatible = "gpio-keys";
#address-cells = <1>;
#size-cells = <0>;
pinctrl-0 = <&pmx_keys>;
pinctrl-names = "default";
button@1 {
label = "Factory Reset Button";
linux,code = <141>; /* KEY_SETUP */
gpios = <&gpio1 1 1>;
};
};
};

View file

@ -0,0 +1,10 @@
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -131,6 +131,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += armada-370-d
armada-xp-db.dtb \
armada-xp-gp.dtb \
armada-xp-netgear-rn2120.dtb \
+ armada-xp-mamba.dtb \
armada-xp-matrix.dtb \
armada-xp-openblocks-ax3-4.dtb
dtb-$(CONFIG_ARCH_MXC) += \

View file

@ -0,0 +1,15 @@
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -43,12 +43,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>;
};

View file

@ -0,0 +1,529 @@
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -487,6 +487,15 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
+config LEDS_TLC59116
+ tristate "LED driver for TLC59116F dimmer"
+ depends on LEDS_CLASS
+ depends on I2C
+ help
+ This option enables support for Texas Instruments TLC59116F
+ LED controller. It is generally only useful
+ as a platform driver
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_TLC59116) += leds-tlc59116.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
--- /dev/null
+++ b/drivers/leds/leds-tlc59116.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2014 Belkin Inc.
+ *
+ * Author: Belkin Inc.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for various TLC59116 I2C LED drivers
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+/* LED select registers determine the source that drives LED outputs */
+#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 TLC59116_PINS 16
+#define TLC59116_REG_MODE1 0x00 /* Mode register 0 */
+#define MODE1_RESPON_ADDR_MASK 0xF0
+#define MODE1_NORMAL_MODE (0 << 4)
+#define MODE1_SPEED_MODE (1 << 4)
+
+#define TLC59116_REG_MODE2 0x01 /* Mode register 1 */
+#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_PWM0 0x02
+#define TLC59116_REG_PWM1 0x03
+#define TLC59116_REG_PWM2 0x04
+#define TLC59116_REG_PWM3 0x05
+#define TLC59116_REG_PWM4 0x06
+#define TLC59116_REG_PWM5 0x07
+#define TLC59116_REG_PWM6 0x08
+#define TLC59116_REG_PWM7 0x09
+#define TLC59116_REG_PWM8 0x0a
+#define TLC59116_REG_PWM9 0x0b
+#define TLC59116_REG_PWM10 0x0c
+#define TLC59116_REG_PWM11 0x0d
+#define TLC59116_REG_PWM12 0x0e
+#define TLC59116_REG_PWM13 0x0f
+#define TLC59116_REG_PWM14 0x10
+#define TLC59116_REG_PWM15 0x01
+
+#define TLC59116_REG_GRPPWM 0x12
+#define TLC59116_REG_GRPFREQ 0x13
+
+#define TLC59116_PERIOD_MIN 41 /* 41ms */
+#define TLC59116_PERIOD_MAX 10730 /* 10.73s */
+
+#define TLC59116_REG_LEDOUT0 0x14 /* LED [3:0] driver output state registers */
+#define TLC59116_REG_LEDOUT1 0x15 /* LED [7:4] driver output state registers */
+#define TLC59116_REG_LEDOUT2 0x16 /* LED [11:8] driver output state registers */
+#define TLC59116_REG_LEDOUT3 0x17 /* LED [15:12] driver output state registers */
+
+#define GPIO0_MASK 0x3
+
+#define DEBUG 0
+
+#if DEBUG > 1
+#define led_dbg(fmt, arg...) printk(KERN_DEBUG "tlc59116:%s " fmt "\n", __func__ , ## arg)
+#else
+#define led_dbg(fmt, arg...)
+#endif
+
+
+#if DEBUG > 0
+#define led_info(fmt, arg...) printk("tlc59116:%s " fmt "\n", __func__ , ## arg)
+#else
+#define led_info(fmt, arg...)
+#endif
+
+enum tlc59116_type {
+ tlc59116,
+};
+
+struct tlc59116_chipdef {
+ int bits;
+ u8 slv_addr; /* 7-bit slave address mask */
+ int slv_addr_shift; /* Number of bits to ignore */
+};
+
+static struct tlc59116_chipdef tlc59116_chipdefs[] = {
+ [tlc59116] = {
+ .bits = 16,
+ .slv_addr = /* 1100xxx */ 0x68,
+ .slv_addr_shift = 3,
+ },
+};
+
+static const struct i2c_device_id tlc59116_id[] = {
+ { "tlc59116", tlc59116 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tlc59116_id);
+
+struct tlc59116_led {
+ struct tlc59116_chipdef *chipdef;
+ struct i2c_client *client;
+ struct work_struct work;
+ spinlock_t lock;
+ enum led_brightness brightness;
+ struct led_classdev led_cdev;
+ int led_num; /* 0 .. 8 potentially */
+ char name[32];
+};
+
+//#define DUMP_REGS
+
+#ifdef DUMP_REGS
+void dump_regs(struct i2c_client *client)
+{
+ int i, j = 0;
+ u8 data;
+ printk ("\n-------------------------------------\n");
+ for (i = 0; i< 0x20; i++)
+ {
+ data = i2c_smbus_read_byte_data(client, i);
+ printk ("[0x%x] = 0x%x ",i, data);
+ j++;
+ if (j == 5)
+ {
+ printk ("\n");
+ j = 0;
+ }
+ }
+ printk ("\n");
+}
+#endif
+
+static int tlc59116_set_mode(struct i2c_client *client, uint8_t mode)
+{
+ uint8_t val = 0;
+
+ if ((mode != MODE2_DIM) && (mode != MODE2_BLINK))
+ mode = MODE2_DIM;
+
+ /* Configure MODE1 register */
+ val &= 0x0;
+ val &= MODE1_RESPON_ADDR_MASK;
+ val |= MODE1_NORMAL_MODE;
+ i2c_smbus_write_byte_data(client, TLC59116_REG_MODE1, val);
+
+ /* Configure MODE2 Reg */
+ val &= 0x00;
+ val |= MODE2_OCH_STOP;
+
+ val |= mode;
+
+ i2c_smbus_write_byte_data(client, TLC59116_REG_MODE2, val);
+ mdelay(100);
+
+ return 0;
+}
+
+static int tlc59116_set_gpio_act(struct i2c_client *client, u8 gpio_no, u8 act_mode)
+{
+ char data, addr = 0, i;
+
+ if ((gpio_no >= 0) && (gpio_no < 4))
+ addr = TLC59116_REG_LEDOUT0;
+ else if ((gpio_no >= 4) && (gpio_no < 8))
+ addr = TLC59116_REG_LEDOUT1;
+ else if ((gpio_no >= 8) && (gpio_no < 12))
+ addr = TLC59116_REG_LEDOUT2;
+ else if ((gpio_no >=12 ) && (gpio_no < 16))
+ addr = TLC59116_REG_LEDOUT3;
+
+ data = i2c_smbus_read_byte_data(client, addr);
+
+ i = (gpio_no % 4) * 2;
+
+ data &= ~(GPIO0_MASK << i);
+ act_mode = act_mode << i;
+ data |= act_mode;
+
+ if(i2c_smbus_write_byte_data(client, addr, data) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int tlc59116_set_gpio(struct i2c_client *client, uint8_t gpio_no, uint8_t val)
+{
+ val &= 0x03;
+ tlc59116_set_gpio_act(client, gpio_no, val);
+#ifdef DUMP_REGS
+ dump_regs(client);
+#endif
+ return 0;
+}
+
+static int tlc59116_get_gpio(struct i2c_client *client, uint8_t gpio_no)
+{
+ uint8_t val, reg, data;
+
+ led_dbg("gpio = %d\n", gpio_no);
+ reg = TLC59116_REG_LEDOUT0;
+
+ if ((gpio_no >= 0) && (gpio_no < 4))
+ reg = TLC59116_REG_LEDOUT0;
+ else if ((gpio_no >= 4) && (gpio_no < 8)) {
+ reg = TLC59116_REG_LEDOUT1;
+ gpio_no = gpio_no - 4;
+ }
+ else if ((gpio_no >= 8) && (gpio_no < 12)) {
+ reg = TLC59116_REG_LEDOUT2;
+ gpio_no = gpio_no - 8;
+ }
+ else if ((gpio_no >=12 ) && (gpio_no < 16)) {
+ reg = TLC59116_REG_LEDOUT3;
+ gpio_no = gpio_no - 12;
+ }
+
+ val = i2c_smbus_read_byte_data(client, reg);
+
+ data = (val >> (gpio_no * 2)) & 0x03;
+ return data;
+}
+
+/*
+ * gpio_no [0..7]
+ * duty_cycle [0..99]%
+ *
+ * */
+static int tlc59116_individual_brighness_control(struct i2c_client *client, uint8_t gpio_no, uint8_t brightness)
+{
+ uint8_t pwm;
+
+ pwm = gpio_no + TLC59116_REG_PWM0;
+ i2c_smbus_write_byte_data(client, pwm, brightness);
+
+ return 0;
+}
+
+static void tlc59116_led_work(struct work_struct *work)
+{
+ struct tlc59116_led *tlc59116;
+
+ tlc59116 = container_of(work, struct tlc59116_led, work);
+
+ led_dbg("\nbrighness = %d \n", tlc59116->brightness);
+ switch (tlc59116->brightness) {
+ case LED_OFF:
+ led_info("\nLed off\n");
+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_LED_OFF);
+ break;
+
+ case LED_FULL:
+ led_info("\nLed on\n");
+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_LED_ON);
+ break;
+ default:
+ led_info("\nBrightness is %d\n", tlc59116->brightness);
+ if (TLC59116_BLINK != tlc59116_get_gpio(tlc59116->client, tlc59116->led_num))
+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_DIM);
+
+ tlc59116_individual_brighness_control(tlc59116->client, tlc59116->led_num, tlc59116->brightness);
+ break;
+ }
+
+}
+
+static void tlc59116_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ struct tlc59116_led *tlc59116;
+
+ tlc59116 = container_of(led_cdev, struct tlc59116_led, led_cdev);
+
+ spin_lock(&tlc59116->lock);
+ tlc59116->brightness = value;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&tlc59116->work);
+
+ spin_unlock(&tlc59116->lock);
+}
+
+/*
+ * delay_on, delay_off: units are in ms
+ *
+ */
+
+static int tlc59116_set_blink(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct tlc59116_led *tlc59116;
+ uint16_t period;
+ uint16_t duty_cycle;
+ uint8_t gdc;
+ uint8_t gfrq;
+
+ tlc59116 = container_of(led_cdev, struct tlc59116_led, led_cdev);
+ led_info ("Blinking: delay_on = %ldms, delay_off = %ldms, brightness = %d\n",
+ *delay_on, *delay_off, tlc59116->brightness);
+
+ // Hardware blinking only for tricolor leds . The rest will have software blinking
+ if (tlc59116->led_num > 2)
+ return 1;
+
+ if ((*delay_on == 0) && (*delay_off ==0)) {
+ spin_lock(&tlc59116->lock);
+
+ /* MODE2[DMBLNK] = 1 */
+ tlc59116_set_mode(tlc59116->client, MODE2_BLINK);
+
+ /* Set LDRx = 11 */
+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_BLINK);
+
+ tlc59116_individual_brighness_control(tlc59116->client, tlc59116->led_num, tlc59116->brightness);
+ spin_unlock(&tlc59116->lock);
+ return 0;
+ }
+
+
+ if ((*delay_on + *delay_off) > TLC59116_PERIOD_MAX)
+ {
+ led_dbg ("Max period is %dms\n", TLC59116_PERIOD_MAX);
+ return -EINVAL;
+ }
+
+ if ((*delay_on + *delay_off) < TLC59116_PERIOD_MIN)
+ {
+ *delay_on = TLC59116_PERIOD_MIN/2 + 1;
+ *delay_off = TLC59116_PERIOD_MIN/2 + 1;
+ }
+ period = (*delay_on) + (*delay_off);
+
+ duty_cycle = (100 * (*delay_on)) / period;
+
+ spin_lock(&tlc59116->lock);
+
+ /* MODE2[DMBLNK] = 1 */
+ tlc59116_set_mode(tlc59116->client, MODE2_BLINK);
+
+ /* Set LDRx = 11 */
+ tlc59116_set_gpio(tlc59116->client, tlc59116->led_num, TLC59116_BLINK);
+
+ tlc59116_individual_brighness_control(tlc59116->client, tlc59116->led_num, tlc59116->brightness);
+
+ gdc = (duty_cycle * 256)/100;
+ i2c_smbus_write_byte_data(tlc59116->client, TLC59116_REG_GRPPWM, gdc);
+
+ gfrq = (24 * period)/1000 - 1; /* unit is in second (convert from ms to second) */
+ i2c_smbus_write_byte_data(tlc59116->client, TLC59116_REG_GRPFREQ, gfrq);
+
+
+#ifdef DUMP_REGS
+ dump_regs(tlc59116->client);
+#endif
+
+ spin_unlock(&tlc59116->lock);
+ return 0;
+}
+
+static int tlc59116_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tlc59116_led *tlc59116;
+ struct tlc59116_chipdef *chip;
+ struct i2c_adapter *adapter;
+ struct led_platform_data *pdata;
+ int i, err;
+
+ chip = &tlc59116_chipdefs[id->driver_data];
+ adapter = to_i2c_adapter(client->dev.parent);
+ pdata = client->dev.platform_data;
+
+ /* Make sure the slave address / chip type combo given is possible */
+ if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) !=
+ chip->slv_addr) {
+ dev_err(&client->dev, "invalid slave address %02x\n",
+ client->addr);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "leds-tlc59116: Using %s %d-bit LED driver at "
+ "slave address 0x%02x\n",
+ id->name, chip->bits, client->addr);
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ if (pdata) {
+ if (pdata->num_leds != chip->bits) {
+ dev_err(&client->dev, "board info claims %d LEDs"
+ " on a %d-bit chip\n",
+ pdata->num_leds, chip->bits);
+ return -ENODEV;
+ }
+ }
+
+ tlc59116 = devm_kzalloc(&client->dev, sizeof(*tlc59116) * chip->bits, GFP_KERNEL);
+ if (!tlc59116)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, tlc59116);
+
+ for (i = 0; i < chip->bits; i++) {
+ tlc59116[i].chipdef = chip;
+ tlc59116[i].client = client;
+ tlc59116[i].led_num = i;
+
+ /* Platform data can specify LED names and default triggers */
+ if (pdata) {
+ if (pdata->leds[i].name)
+ snprintf(tlc59116[i].name,
+ sizeof(tlc59116[i].name), "tlc59116:%s",
+ pdata->leds[i].name);
+ if (pdata->leds[i].default_trigger)
+ tlc59116[i].led_cdev.default_trigger =
+ pdata->leds[i].default_trigger;
+ } else {
+ snprintf(tlc59116[i].name, sizeof(tlc59116[i].name),
+ "tlc59116:%d", i);
+ }
+
+ spin_lock_init(&tlc59116[i].lock);
+
+ tlc59116[i].led_cdev.name = tlc59116[i].name;
+ tlc59116[i].led_cdev.brightness_set = tlc59116_led_set;
+ tlc59116[i].led_cdev.blink_set = tlc59116_set_blink;
+ tlc59116[i].led_cdev.brightness = 0;
+
+ INIT_WORK(&tlc59116[i].work, tlc59116_led_work);
+
+ err = led_classdev_register(&client->dev, &tlc59116[i].led_cdev);
+ if (err < 0)
+ goto exit;
+ }
+
+ tlc59116_set_mode(client, MODE2_DIM);
+
+ /* Turn off LEDs */
+ for (i = 0; i < chip->bits; i++)
+ tlc59116_set_gpio(client, i, TLC59116_LED_OFF);
+
+ return 0;
+
+exit:
+ while (i--) {
+ led_classdev_unregister(&tlc59116[i].led_cdev);
+ cancel_work_sync(&tlc59116[i].work);
+ }
+
+ devm_kfree(&client->dev, tlc59116);
+ i2c_set_clientdata(client, NULL);
+
+ return err;
+}
+
+static int tlc59116_remove(struct i2c_client *client)
+{
+ struct tlc59116_led *tlc59116 = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < tlc59116->chipdef->bits; i++) {
+ led_classdev_unregister(&tlc59116[i].led_cdev);
+ cancel_work_sync(&tlc59116[i].work);
+ }
+
+ devm_kfree(&client->dev, tlc59116);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+static struct i2c_driver tlc59116_driver = {
+ .driver = {
+ .name = "leds-tlc59116",
+ .owner = THIS_MODULE,
+ },
+ .probe = tlc59116_probe,
+ .remove = tlc59116_remove,
+ .id_table = tlc59116_id,
+};
+
+module_i2c_driver(tlc59116_driver);
+
+MODULE_AUTHOR("Belkin Inc.");
+MODULE_DESCRIPTION("TLC59116 LED driver");
+MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,64 @@
From 67a9ad9b8a6f6ea76ef8fc484ae49970d72d5534 Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Date: Wed, 14 May 2014 14:58:06 -0300
Subject: mtd: nand: Warn the user if the selected ECC strength is too weak
This commit makes use of the chip->ecc_strength_ds and chip->ecc_step_ds which
contain the datasheet minimum requested ECC strength to produce a noisy warning
if the configured ECC strength is weaker.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3682,6 +3682,39 @@ int nand_scan_ident(struct mtd_info *mtd
}
EXPORT_SYMBOL(nand_scan_ident);
+/*
+ * Check if the chip configuration meet the datasheet requirements.
+
+ * If our configuration corrects A bits per B bytes and the minimum
+ * required correction level is X bits per Y bytes, then we must ensure
+ * both of the following are true:
+ *
+ * (1) A / B >= X / Y
+ * (2) A >= X
+ *
+ * Requirement (1) ensures we can correct for the required bitflip density.
+ * Requirement (2) ensures we can correct even when all bitflips are clumped
+ * in the same sector.
+ */
+static bool nand_ecc_strength_good(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int corr, ds_corr;
+
+ if (ecc->size == 0 || chip->ecc_step_ds == 0)
+ /* Not enough information */
+ return true;
+
+ /*
+ * We get the number of corrected bits per page to compare
+ * the correction density.
+ */
+ corr = (mtd->writesize * ecc->strength) / ecc->size;
+ ds_corr = (mtd->writesize * chip->ecc_strength_ds) / chip->ecc_step_ds;
+
+ return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds;
+}
/**
* nand_scan_tail - [NAND Interface] Scan for the NAND device
@@ -3891,6 +3924,9 @@ int nand_scan_tail(struct mtd_info *mtd)
ecc->layout->oobavail += ecc->layout->oobfree[i].length;
mtd->oobavail = ecc->layout->oobavail;
+ /* ECC sanity check: warn noisily if it's too weak */
+ WARN_ON(!nand_ecc_strength_good(mtd));
+
/*
* Set the number of read / write steps for one page depending on ECC
* mode.

View file

@ -0,0 +1,84 @@
From 6d9434ebb76157071164b32ad03fbed165c74382 Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Date: Mon, 24 Feb 2014 19:24:48 -0300
Subject: of_mtd: Add helpers to get ECC strength and ECC step size
This commit adds simple helpers to obtain the devicetree properties
that specify the ECC strength and ECC step size to use on a given
NAND controller.
Acked-by: Boris BREZILLON <b.brezillon.dev@gmail.com>
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -50,6 +50,40 @@ int of_get_nand_ecc_mode(struct device_n
EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
/**
+ * of_get_nand_ecc_step_size - Get ECC step size associated to
+ * the required ECC strength (see below).
+ * @np: Pointer to the given device_node
+ *
+ * return the ECC step size, or errno in error case.
+ */
+int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+ return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
+
+/**
+ * of_get_nand_ecc_strength - Get required ECC strength over the
+ * correspnding step size as defined by 'nand-ecc-size'
+ * @np: Pointer to the given device_node
+ *
+ * return the ECC strength, or errno in error case.
+ */
+int of_get_nand_ecc_strength(struct device_node *np)
+{
+ int ret;
+ u32 val;
+
+ ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+ return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
+
+/**
* of_get_nand_bus_width - Get nand bus witdh for given device_node
* @np: Pointer to the given device_node
*
--- a/include/linux/of_mtd.h
+++ b/include/linux/of_mtd.h
@@ -13,6 +13,8 @@
#include <linux/of.h>
int of_get_nand_ecc_mode(struct device_node *np);
+int of_get_nand_ecc_step_size(struct device_node *np);
+int of_get_nand_ecc_strength(struct device_node *np);
int of_get_nand_bus_width(struct device_node *np);
bool of_get_nand_on_flash_bbt(struct device_node *np);
@@ -22,6 +24,16 @@ static inline int of_get_nand_ecc_mode(s
{
return -ENOSYS;
}
+
+static inline int of_get_nand_ecc_step_size(struct device_node *np)
+{
+ return -ENOSYS;
+}
+
+static inline int of_get_nand_ecc_strength(struct device_node *np)
+{
+ return -ENOSYS;
+}
static inline int of_get_nand_bus_width(struct device_node *np)
{

View file

@ -0,0 +1,22 @@
From e634ce51baa52c131e4a35c01bba9e596a0eb86d Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Date: Thu, 27 Feb 2014 14:13:26 -0300
Subject: mtd: nand: pxa3xx: Print actual ECC strength in error message
The actual ECC strength used to select the ECC scheme is 'ecc_strength'.
Use it in the error message.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1531,7 +1531,7 @@ KEEP_CONFIG:
if (!ret) {
dev_err(&info->pdev->dev,
"ECC strength %d at page size %d is not supported\n",
- chip->ecc_strength_ds, mtd->writesize);
+ ecc_strength, mtd->writesize);
return -ENODEV;
}

View file

@ -0,0 +1,78 @@
From eee0166d8ead9d719d794df3e66acd8f83630e05 Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Date: Wed, 14 May 2014 14:58:07 -0300
Subject: mtd: nand: pxa3xx: Clean pxa_ecc_init() error handling
Let's make pxa_ecc_init() return a negative errno on error or zero
if succesful, which is standard kernel practice. Also, report the
selected ECC strength and step size, which is important information.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1355,7 +1355,6 @@ static int pxa_ecc_init(struct pxa3xx_na
ecc->mode = NAND_ECC_HW;
ecc->size = 512;
ecc->strength = 1;
- return 1;
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) {
info->chunk_size = 512;
@@ -1364,7 +1363,6 @@ static int pxa_ecc_init(struct pxa3xx_na
ecc->mode = NAND_ECC_HW;
ecc->size = 512;
ecc->strength = 1;
- return 1;
/*
* Required ECC: 4-bit correction per 512 bytes
@@ -1379,7 +1377,6 @@ static int pxa_ecc_init(struct pxa3xx_na
ecc->size = info->chunk_size;
ecc->layout = &ecc_layout_2KB_bch4bit;
ecc->strength = 16;
- return 1;
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) {
info->ecc_bch = 1;
@@ -1390,7 +1387,6 @@ static int pxa_ecc_init(struct pxa3xx_na
ecc->size = info->chunk_size;
ecc->layout = &ecc_layout_4KB_bch4bit;
ecc->strength = 16;
- return 1;
/*
* Required ECC: 8-bit correction per 512 bytes
@@ -1405,8 +1401,15 @@ static int pxa_ecc_init(struct pxa3xx_na
ecc->size = info->chunk_size;
ecc->layout = &ecc_layout_4KB_bch8bit;
ecc->strength = 16;
- return 1;
+ } else {
+ dev_err(&info->pdev->dev,
+ "ECC strength %d at page size %d is not supported\n",
+ strength, page_size);
+ return -ENODEV;
}
+
+ dev_info(&info->pdev->dev, "ECC strength %d, ECC step size %d\n",
+ ecc->strength, ecc->size);
return 0;
}
@@ -1528,12 +1531,8 @@ KEEP_CONFIG:
ret = pxa_ecc_init(info, &chip->ecc, ecc_strength,
ecc_step, mtd->writesize);
- if (!ret) {
- dev_err(&info->pdev->dev,
- "ECC strength %d at page size %d is not supported\n",
- ecc_strength, mtd->writesize);
- return -ENODEV;
- }
+ if (ret)
+ return ret;
/* calculate addressing information */
if (mtd->writesize >= 2048)

View file

@ -0,0 +1,57 @@
From 5b3e507820c6e120bc2680c0d35f9d9d81fcb98d Mon Sep 17 00:00:00 2001
From: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Date: Wed, 14 May 2014 14:58:08 -0300
Subject: mtd: nand: pxa3xx: Use ECC strength and step size devicetree binding
This commit adds support for the user to specify the ECC strength
and step size through the devicetree. We keep the previous behavior,
when there is no DT parameter provided.
Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -1520,8 +1520,13 @@ KEEP_CONFIG:
}
}
- ecc_strength = chip->ecc_strength_ds;
- ecc_step = chip->ecc_step_ds;
+ if (pdata->ecc_strength && pdata->ecc_step_size) {
+ ecc_strength = pdata->ecc_strength;
+ ecc_step = pdata->ecc_step_size;
+ } else {
+ ecc_strength = chip->ecc_strength_ds;
+ ecc_step = chip->ecc_step_ds;
+ }
/* Set default ECC strength requirements on non-ONFI devices */
if (ecc_strength < 1 && ecc_step < 1) {
@@ -1730,6 +1735,14 @@ static int pxa3xx_nand_probe_dt(struct p
of_property_read_u32(np, "num-cs", &pdata->num_cs);
pdata->flash_bbt = of_get_nand_on_flash_bbt(np);
+ pdata->ecc_strength = of_get_nand_ecc_strength(np);
+ if (pdata->ecc_strength < 0)
+ pdata->ecc_strength = 0;
+
+ pdata->ecc_step_size = of_get_nand_ecc_step_size(np);
+ if (pdata->ecc_step_size < 0)
+ pdata->ecc_step_size = 0;
+
pdev->dev.platform_data = pdata;
return 0;
--- a/include/linux/platform_data/mtd-nand-pxa3xx.h
+++ b/include/linux/platform_data/mtd-nand-pxa3xx.h
@@ -58,6 +58,9 @@ struct pxa3xx_nand_platform_data {
/* use an flash-based bad block table */
bool flash_bbt;
+ /* requested ECC strength and ECC step size */
+ int ecc_strength, ecc_step_size;
+
const struct mtd_partition *parts[NUM_CHIP_SELECT];
unsigned int nr_parts[NUM_CHIP_SELECT];

View file

@ -0,0 +1,102 @@
From 9b744942290c168287013f0a19ff7392f65c8107 Mon Sep 17 00:00:00 2001
From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Date: Fri, 16 May 2014 16:14:03 +0200
Subject: net: phy: decouple PHY id and PHY address in fixed PHY driver
Until now, the fixed_phy_add() function was taking as argument
'phy_id', which was used both as the PHY address on the fake fixed
MDIO bus, and as the PHY id, as available in the MII_PHYSID1 and
MII_PHYSID2 registers. However, those two informations are completely
unrelated.
This patch decouples them. The PHY id of fixed PHYs is hardcoded to be
0x0. Ideally, a really reserved value would be nicer, but there
doesn't seem to be an easy of making sure a dummy value can be
assigned to the Linux kernel for such usage.
The PHY address remains passed by the caller of phy_fixed_add().
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
--- a/drivers/net/phy/fixed.c
+++ b/drivers/net/phy/fixed.c
@@ -31,7 +31,7 @@ struct fixed_mdio_bus {
};
struct fixed_phy {
- int id;
+ int addr;
u16 regs[MII_REGS_NUM];
struct phy_device *phydev;
struct fixed_phy_status status;
@@ -104,8 +104,8 @@ static int fixed_phy_update_regs(struct
if (fp->status.asym_pause)
lpa |= LPA_PAUSE_ASYM;
- fp->regs[MII_PHYSID1] = fp->id >> 16;
- fp->regs[MII_PHYSID2] = fp->id;
+ fp->regs[MII_PHYSID1] = 0;
+ fp->regs[MII_PHYSID2] = 0;
fp->regs[MII_BMSR] = bmsr;
fp->regs[MII_BMCR] = bmcr;
@@ -115,7 +115,7 @@ static int fixed_phy_update_regs(struct
return 0;
}
-static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
+static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
{
struct fixed_mdio_bus *fmb = bus->priv;
struct fixed_phy *fp;
@@ -124,7 +124,7 @@ static int fixed_mdio_read(struct mii_bu
return -1;
list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->id == phy_id) {
+ if (fp->addr == phy_addr) {
/* Issue callback if user registered it. */
if (fp->link_update) {
fp->link_update(fp->phydev->attached_dev,
@@ -138,7 +138,7 @@ static int fixed_mdio_read(struct mii_bu
return 0xFFFF;
}
-static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
+static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
u16 val)
{
return 0;
@@ -160,7 +160,7 @@ int fixed_phy_set_link_update(struct phy
return -EINVAL;
list_for_each_entry(fp, &fmb->phys, node) {
- if (fp->id == phydev->phy_id) {
+ if (fp->addr == phydev->addr) {
fp->link_update = link_update;
fp->phydev = phydev;
return 0;
@@ -171,7 +171,7 @@ int fixed_phy_set_link_update(struct phy
}
EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
-int fixed_phy_add(unsigned int irq, int phy_id,
+int fixed_phy_add(unsigned int irq, int phy_addr,
struct fixed_phy_status *status)
{
int ret;
@@ -184,9 +184,9 @@ int fixed_phy_add(unsigned int irq, int
memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM);
- fmb->irqs[phy_id] = irq;
+ fmb->irqs[phy_addr] = irq;
- fp->id = phy_id;
+ fp->addr = phy_addr;
fp->status = *status;
ret = fixed_phy_update_regs(fp);

View file

@ -0,0 +1,134 @@
From a75951217472c522c324adb0a4de3ba69d656ef5 Mon Sep 17 00:00:00 2001
From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Date: Fri, 16 May 2014 16:14:04 +0200
Subject: net: phy: extend fixed driver with fixed_phy_register()
The existing fixed_phy_add() function has several drawbacks that
prevents it from being used as is for OF-based declaration of fixed
PHYs:
* The address of the PHY on the fake bus needs to be passed, while a
dynamic allocation is desired.
* Since the phy_device instantiation is post-poned until the next
mdiobus scan, there is no way to associate the fixed PHY with its
OF node, which later prevents of_phy_connect() from finding this
fixed PHY from a given OF node.
To solve this, this commit introduces fixed_phy_register(), which will
allocate an available PHY address, add the PHY using fixed_phy_add()
and instantiate the phy_device structure associated with the provided
OF node.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Grant Likely <grant.likely@linaro.org>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
--- a/drivers/net/phy/fixed.c
+++ b/drivers/net/phy/fixed.c
@@ -21,6 +21,7 @@
#include <linux/phy_fixed.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/of.h>
#define MII_REGS_NUM 29
@@ -203,6 +204,66 @@ err_regs:
}
EXPORT_SYMBOL_GPL(fixed_phy_add);
+void fixed_phy_del(int phy_addr)
+{
+ struct fixed_mdio_bus *fmb = &platform_fmb;
+ struct fixed_phy *fp, *tmp;
+
+ list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
+ if (fp->addr == phy_addr) {
+ list_del(&fp->node);
+ kfree(fp);
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(fixed_phy_del);
+
+static int phy_fixed_addr;
+static DEFINE_SPINLOCK(phy_fixed_addr_lock);
+
+int fixed_phy_register(unsigned int irq,
+ struct fixed_phy_status *status,
+ struct device_node *np)
+{
+ struct fixed_mdio_bus *fmb = &platform_fmb;
+ struct phy_device *phy;
+ int phy_addr;
+ int ret;
+
+ /* Get the next available PHY address, up to PHY_MAX_ADDR */
+ spin_lock(&phy_fixed_addr_lock);
+ if (phy_fixed_addr == PHY_MAX_ADDR) {
+ spin_unlock(&phy_fixed_addr_lock);
+ return -ENOSPC;
+ }
+ phy_addr = phy_fixed_addr++;
+ spin_unlock(&phy_fixed_addr_lock);
+
+ ret = fixed_phy_add(PHY_POLL, phy_addr, status);
+ if (ret < 0)
+ return ret;
+
+ phy = get_phy_device(fmb->mii_bus, phy_addr, false);
+ if (!phy || IS_ERR(phy)) {
+ fixed_phy_del(phy_addr);
+ return -EINVAL;
+ }
+
+ of_node_get(np);
+ phy->dev.of_node = np;
+
+ ret = phy_device_register(phy);
+ if (ret) {
+ phy_device_free(phy);
+ of_node_put(np);
+ fixed_phy_del(phy_addr);
+ return ret;
+ }
+
+ return 0;
+}
+
static int __init fixed_mdio_bus_init(void)
{
struct fixed_mdio_bus *fmb = &platform_fmb;
--- a/include/linux/phy_fixed.h
+++ b/include/linux/phy_fixed.h
@@ -9,15 +9,26 @@ struct fixed_phy_status {
int asym_pause;
};
+struct device_node;
+
#ifdef CONFIG_FIXED_PHY
extern int fixed_phy_add(unsigned int irq, int phy_id,
struct fixed_phy_status *status);
+extern int fixed_phy_register(unsigned int irq,
+ struct fixed_phy_status *status,
+ struct device_node *np);
#else
static inline int fixed_phy_add(unsigned int irq, int phy_id,
struct fixed_phy_status *status)
{
return -ENODEV;
}
+static inline int fixed_phy_register(unsigned int irq,
+ struct fixed_phy_status *status,
+ struct device_node *np)
+{
+ return -ENODEV;
+}
#endif /* CONFIG_FIXED_PHY */
/*

View file

@ -0,0 +1,170 @@
From 3be2a49e5c08d268f8af0dd4fe89a24ea8cdc339 Mon Sep 17 00:00:00 2001
From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Date: Fri, 16 May 2014 16:14:05 +0200
Subject: of: provide a binding for fixed link PHYs
Some Ethernet MACs have a "fixed link", and are not connected to a
normal MDIO-managed PHY device. For those situations, a Device Tree
binding allows to describe a "fixed link" using a special PHY node.
This patch adds:
* A documentation for the fixed PHY Device Tree binding.
* An of_phy_is_fixed_link() function that an Ethernet driver can call
on its PHY phandle to find out whether it's a fixed link PHY or
not. It should typically be used to know if
of_phy_register_fixed_link() should be called.
* An of_phy_register_fixed_link() function that instantiates the
fixed PHY into the PHY subsystem, so that when the driver calls
of_phy_connect(), the PHY device associated to the OF node will be
found.
These two additional functions also support the old fixed-link Device
Tree binding used on PowerPC platforms, so that ultimately, the
network device drivers for those platforms could be converted to use
of_phy_is_fixed_link() and of_phy_register_fixed_link() instead of
of_phy_connect_fixed_link(), while keeping compatibility with their
respective Device Tree bindings.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/fixed-link.txt
@@ -0,0 +1,30 @@
+Fixed link Device Tree binding
+------------------------------
+
+Some Ethernet MACs have a "fixed link", and are not connected to a
+normal MDIO-managed PHY device. For those situations, a Device Tree
+binding allows to describe a "fixed link".
+
+Such a fixed link situation is described by creating a 'fixed-link'
+sub-node of the Ethernet MAC device node, with the following
+properties:
+
+* 'speed' (integer, mandatory), to indicate the link speed. Accepted
+ values are 10, 100 and 1000
+* 'full-duplex' (boolean, optional), to indicate that full duplex is
+ used. When absent, half duplex is assumed.
+* 'pause' (boolean, optional), to indicate that pause should be
+ enabled.
+* 'asym-pause' (boolean, optional), to indicate that asym_pause should
+ be enabled.
+
+Example:
+
+ethernet@0 {
+ ...
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ ...
+};
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -14,6 +14,7 @@
#include <linux/netdevice.h>
#include <linux/err.h>
#include <linux/phy.h>
+#include <linux/phy_fixed.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_mdio.h>
@@ -280,3 +281,69 @@ struct phy_device *of_phy_attach(struct
return phy_attach_direct(dev, phy, flags, iface) ? NULL : phy;
}
EXPORT_SYMBOL(of_phy_attach);
+
+#if defined(CONFIG_FIXED_PHY)
+/*
+ * of_phy_is_fixed_link() and of_phy_register_fixed_link() must
+ * support two DT bindings:
+ * - the old DT binding, where 'fixed-link' was a property with 5
+ * cells encoding various informations about the fixed PHY
+ * - the new DT binding, where 'fixed-link' is a sub-node of the
+ * Ethernet device.
+ */
+bool of_phy_is_fixed_link(struct device_node *np)
+{
+ struct device_node *dn;
+ int len;
+
+ /* New binding */
+ dn = of_get_child_by_name(np, "fixed-link");
+ if (dn) {
+ of_node_put(dn);
+ return true;
+ }
+
+ /* Old binding */
+ if (of_get_property(np, "fixed-link", &len) &&
+ len == (5 * sizeof(__be32)))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL(of_phy_is_fixed_link);
+
+int of_phy_register_fixed_link(struct device_node *np)
+{
+ struct fixed_phy_status status = {};
+ struct device_node *fixed_link_node;
+ const __be32 *fixed_link_prop;
+ int len;
+
+ /* New binding */
+ fixed_link_node = of_get_child_by_name(np, "fixed-link");
+ if (fixed_link_node) {
+ status.link = 1;
+ status.duplex = of_property_read_bool(np, "full-duplex");
+ if (of_property_read_u32(fixed_link_node, "speed", &status.speed))
+ return -EINVAL;
+ status.pause = of_property_read_bool(np, "pause");
+ status.asym_pause = of_property_read_bool(np, "asym-pause");
+ of_node_put(fixed_link_node);
+ return fixed_phy_register(PHY_POLL, &status, np);
+ }
+
+ /* Old binding */
+ fixed_link_prop = of_get_property(np, "fixed-link", &len);
+ if (fixed_link_prop && len == (5 * sizeof(__be32))) {
+ status.link = 1;
+ status.duplex = be32_to_cpu(fixed_link_prop[1]);
+ status.speed = be32_to_cpu(fixed_link_prop[2]);
+ status.pause = be32_to_cpu(fixed_link_prop[3]);
+ status.asym_pause = be32_to_cpu(fixed_link_prop[4]);
+ return fixed_phy_register(PHY_POLL, &status, np);
+ }
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL(of_phy_register_fixed_link);
+#endif
--- a/include/linux/of_mdio.h
+++ b/include/linux/of_mdio.h
@@ -67,4 +67,19 @@ static inline struct mii_bus *of_mdio_fi
}
#endif /* CONFIG_OF */
+#if defined(CONFIG_OF) && defined(CONFIG_FIXED_PHY)
+extern int of_phy_register_fixed_link(struct device_node *np);
+extern bool of_phy_is_fixed_link(struct device_node *np);
+#else
+static inline int of_phy_register_fixed_link(struct device_node *np)
+{
+ return -ENOSYS;
+}
+static inline bool of_phy_is_fixed_link(struct device_node *np)
+{
+ return false;
+}
+#endif
+
+
#endif /* __LINUX_OF_MDIO_H */

View file

@ -0,0 +1,40 @@
From 83895bedeee6fbf56d887af4280bf9edcc80da60 Mon Sep 17 00:00:00 2001
From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Date: Fri, 16 May 2014 16:14:06 +0200
Subject: net: mvneta: add support for fixed links
Following the introduction of of_phy_register_fixed_link(), this patch
introduces fixed link support in the mvneta driver, for Marvell Armada
370/XP SOCs.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -2808,9 +2808,22 @@ static int mvneta_probe(struct platform_
phy_node = of_parse_phandle(dn, "phy", 0);
if (!phy_node) {
- dev_err(&pdev->dev, "no associated PHY\n");
- err = -ENODEV;
- goto err_free_irq;
+ if (!of_phy_is_fixed_link(dn)) {
+ dev_err(&pdev->dev, "no PHY specified\n");
+ err = -ENODEV;
+ goto err_free_irq;
+ }
+
+ err = of_phy_register_fixed_link(dn);
+ if (err < 0) {
+ dev_err(&pdev->dev, "cannot register fixed PHY\n");
+ goto err_free_irq;
+ }
+
+ /* In the case of a fixed PHY, the DT node associated
+ * to the PHY is the Ethernet MAC DT node.
+ */
+ phy_node = dn;
}
phy_mode = of_get_phy_mode(dn);