From 0da3abcaa4bcb5254a421b0f74960753daf1be3b Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 7 Oct 2012 23:25:31 +0000 Subject: [PATCH] cns3xxx: merge fixes and improvements from trunk SVN-Revision: 33652 --- .../linux/cns3xxx/base-files/lib/cns3xxx.sh | 22 + .../base-files/lib/upgrade/platform.sh | 122 ++++ target/linux/cns3xxx/image/Makefile | 19 +- .../patches-3.3/300-laguna_support.patch | 18 +- .../cns3xxx/patches-3.3/470-gpio_irq.patch | 536 ++++++++++++++++++ 5 files changed, 712 insertions(+), 5 deletions(-) create mode 100644 target/linux/cns3xxx/base-files/lib/cns3xxx.sh create mode 100644 target/linux/cns3xxx/base-files/lib/upgrade/platform.sh create mode 100644 target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch diff --git a/target/linux/cns3xxx/base-files/lib/cns3xxx.sh b/target/linux/cns3xxx/base-files/lib/cns3xxx.sh new file mode 100644 index 0000000000..d446777c02 --- /dev/null +++ b/target/linux/cns3xxx/base-files/lib/cns3xxx.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (C) 2012 OpenWrt.org +# + +cns3xxx_board_name() { + local machine + local name + + machine=$(awk 'BEGIN{FS="[ \t]+:[ \t]"} /Hardware/ {print $2}' /proc/cpuinfo) + + case "$machine" in + "Gateworks Corporation Laguna"*) + name="laguna" + ;; + *) + name="generic"; + ;; + esac + + echo $name +} diff --git a/target/linux/cns3xxx/base-files/lib/upgrade/platform.sh b/target/linux/cns3xxx/base-files/lib/upgrade/platform.sh new file mode 100644 index 0000000000..599099181d --- /dev/null +++ b/target/linux/cns3xxx/base-files/lib/upgrade/platform.sh @@ -0,0 +1,122 @@ +. /lib/cns3xxx.sh + +RAMFS_COPY_DATA="/lib/cns3xxx.sh" + +CI_BLKSZ=65536 + +platform_find_partitions() { + local first dev size erasesize name + while read dev size erasesize name; do + name=${name#'"'}; name=${name%'"'} + case "$name" in + vmlinux.bin.l7|kernel|linux|rootfs) + if [ -z "$first" ]; then + first="$name" + else + echo "$erasesize:$first:$name" + break + fi + ;; + esac + done < /proc/mtd +} + +platform_find_kernelpart() { + local part + for part in "${1%:*}" "${1#*:}"; do + case "$part" in + vmlinux.bin.l7|kernel|linux) + echo "$part" + break + ;; + esac + done +} + +platform_do_upgrade_combined() { + local partitions=$(platform_find_partitions) + local kernelpart=$(platform_find_kernelpart "${partitions#*:}") + local erase_size=$((0x${partitions%%:*})); partitions="${partitions#*:}" + local kern_length=0x$(dd if="$1" bs=2 skip=1 count=4 2>/dev/null) + local kern_blocks=$(($kern_length / $CI_BLKSZ)) + local root_blocks=$((0x$(dd if="$1" bs=2 skip=5 count=4 2>/dev/null) / $CI_BLKSZ)) + + v "platform_do_upgrade_combined" + v "partitions=$partitions" + v "kernelpart=$kernelpart" + v "erase_size=$erase_size" + v "kern_blocks=$kern_blocks" + v "root_blocks=$root_blocks" + + if [ -n "$partitions" ] && [ -n "$kernelpart" ] && \ + [ ${kern_blocks:-0} -gt 0 ] && \ + [ ${root_blocks:-0} -gt 0 ] && \ + [ ${erase_size:-0} -gt 0 ]; + then + local append="" + [ -f "$CONF_TAR" -a "$SAVE_CONFIG" -eq 1 ] && append="-j $CONF_TAR" + + dd if="$1" bs=$CI_BLKSZ skip=1 count=$kern_blocks 2>/dev/null | mtd write - kernel + dd if="$1" bs=$CI_BLKSZ skip=$((1+$kern_blocks)) count=$root_blocks 2>/dev/null | \ + mtd -r $append write - rootfs + else + echo "invalid image" + fi +} + +platform_check_image() { + local board=$(cns3xxx_board_name) + local magic="$(get_magic_word "$1")" + local magic_long="$(get_magic_long "$1")" + + [ "$ARGC" -gt 1 ] && return 1 + + case "$board" in + laguna) + [ "$magic" != "4349" ] && { + echo "Invalid image. Use *-sysupgrade.bin files on this board" + return 1 + } + + local md5_img=$(dd if="$1" bs=2 skip=9 count=16 2>/dev/null) + local md5_chk=$(dd if="$1" bs=$CI_BLKSZ skip=1 2>/dev/null | md5sum -); md5_chk="${md5_chk%% *}" + + if [ -n "$md5_img" -a -n "$md5_chk" ] && [ "$md5_img" = "$md5_chk" ]; then + return 0 + else + echo "Invalid image. Contents do not match checksum (image:$md5_img calculated:$md5_chk)" + return 1 + fi + return 0 + ;; + esac + + echo "Sysupgrade is not yet supported on $board." + return 1 +} + +platform_do_upgrade() { + local board=$(cns3xxx_board_name) + + v "board=$board" + case "$board" in + laguna) + platform_do_upgrade_combined "$ARGV" + ;; + *) + default_do_upgrade "$ARGV" + ;; + esac +} + +disable_watchdog() { + v "killing watchdog" + killall watchdog + ( ps | grep -v 'grep' | grep '/dev/watchdog' ) && { + echo 'Could not disable watchdog' + return 1 + } +} + +# CONFIG_WATCHDOG_NOWAYOUT=y - can't kill watchdog unless kernel cmdline has a mpcore_wdt.nowayout=0 +#append sysupgrade_pre_upgrade disable_watchdog diff --git a/target/linux/cns3xxx/image/Makefile b/target/linux/cns3xxx/image/Makefile index 271d5ea198..05a0f4f7a1 100644 --- a/target/linux/cns3xxx/image/Makefile +++ b/target/linux/cns3xxx/image/Makefile @@ -21,21 +21,32 @@ define Image/BuildKernel cp $(KDIR)/uImage-new $(BIN_DIR)/openwrt-$(BOARD)-uImage endef +# Build sysupgrade image +define BuildFirmware/Generic + dd if=$(BIN_DIR)/openwrt-$(BOARD)-uImage of=$(KDIR)/uImage.pad bs=64k conv=sync; \ + dd if=$(KDIR)/root.$(1) of=$(KDIR)/root.$(1).pad bs=128k conv=sync; \ + sh $(TOPDIR)/scripts/combined-image.sh \ + $(KDIR)/uImage.pad \ + $(KDIR)/root.$(1).pad \ + $(BIN_DIR)/$(IMG_PREFIX)-$(patsubst jffs2-%,jffs2,$(patsubst squashfs-%,squashfs,$(1)))-sysupgrade.bin +endef + define Image/Build $(call Image/Build/$(1),$(1)) + $(call BuildFirmware/Generic,$(1)) endef define Image/Build/jffs2-64k - dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=65536 conv=sync + dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=64k conv=sync endef define Image/Build/jffs2-128k - dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=131072 conv=sync + dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=128k conv=sync endef define Image/Build/squashfs - $(call prepare_generic_squashfs,$(KDIR)/root.squashfs) - dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=131072 conv=sync + $(call prepare_generic_squashfs,$(KDIR)/root.$(1)) + dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/openwrt-$(BOARD)-$(1).img bs=128k conv=sync endef $(eval $(call BuildImage)) diff --git a/target/linux/cns3xxx/patches-3.3/300-laguna_support.patch b/target/linux/cns3xxx/patches-3.3/300-laguna_support.patch index 06f302a0d4..7fe970a4dd 100644 --- a/target/linux/cns3xxx/patches-3.3/300-laguna_support.patch +++ b/target/linux/cns3xxx/patches-3.3/300-laguna_support.patch @@ -1,6 +1,6 @@ --- /dev/null +++ b/arch/arm/mach-cns3xxx/laguna.c -@@ -0,0 +1,919 @@ +@@ -0,0 +1,935 @@ +/* + * Gateworks Corporation Laguna Platform + * @@ -660,6 +660,16 @@ + { 108, GPIOF_IN , "J9_DIOGSC0" }, +}; + ++static struct gpio laguna_gpio_gw2383[] = { ++ { 0, GPIOF_IN , "*GPS_PPS" }, ++ { 1, GPIOF_IN , "*GSC_IRQ#" }, ++ { 2, GPIOF_OUT_INIT_HIGH, "*PCIE_RST#" }, ++ { 3, GPIOF_IN , "GPIO0" }, ++ { 8, GPIOF_IN , "GPIO1" }, ++ { 100, GPIOF_IN , "DIO0" }, ++ { 101, GPIOF_IN , "DIO1" }, ++}; ++ +static struct gpio laguna_gpio_gw2382[] = { + { 0, GPIOF_IN , "*GPS_PPS" }, + { 1, GPIOF_IN , "*GSC_IRQ#" }, @@ -882,6 +892,12 @@ + laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2384)); + // configure LED's + laguna_gpio_leds_data.num_leds = 1; ++ } else if (strncmp(laguna_info.model, "GW2383", 6) == 0) { ++ // configure GPIO's ++ laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2383)); ++ // configure LED's ++ laguna_gpio_leds[0].gpio = 107; ++ laguna_gpio_leds_data.num_leds = 1; + } else if (strncmp(laguna_info.model, "GW2382", 6) == 0) { + // configure GPIO's + laguna_register_gpio(ARRAY_AND_SIZE(laguna_gpio_gw2382)); diff --git a/target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch b/target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch new file mode 100644 index 0000000000..c5de35fe42 --- /dev/null +++ b/target/linux/cns3xxx/patches-3.3/470-gpio_irq.patch @@ -0,0 +1,536 @@ +--- a/arch/arm/mach-cns3xxx/Makefile ++++ b/arch/arm/mach-cns3xxx/Makefile +@@ -1,4 +1,4 @@ +-obj-$(CONFIG_ARCH_CNS3XXX) += core.o pm.o devices.o ++obj-$(CONFIG_ARCH_CNS3XXX) += core.o gpio.o pm.o devices.o + obj-$(CONFIG_PCI) += pcie.o + obj-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o + obj-$(CONFIG_MACH_GW2388) += laguna.o +--- a/arch/arm/mach-cns3xxx/cns3420vb.c ++++ b/arch/arm/mach-cns3xxx/cns3420vb.c +@@ -199,7 +199,10 @@ static void __init cns3420_init(void) + + cns3xxx_ahci_init(); + cns3xxx_sdhci_init(); +- ++ cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, ++ NR_IRQS_CNS3XXX); ++ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, IRQ_CNS3XXX_GPIOB, ++ NR_IRQS_CNS3XXX + 32); + cns3xxx_pcie_init(0x3); + + pm_power_off = cns3xxx_power_off; +--- a/arch/arm/mach-cns3xxx/core.c ++++ b/arch/arm/mach-cns3xxx/core.c +@@ -21,7 +21,6 @@ + #include + #include + #include +-#include + #include + #include "core.h" + +@@ -83,73 +82,12 @@ static struct map_desc cns3xxx_io_desc[] + }, + }; + +-static inline void gpio_line_config(u8 line, u32 direction) +-{ +- u32 reg; +- if (direction) { +- if (line < 32) { +- reg = __raw_readl(CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg |= (1 << line); +- __raw_writel(reg, CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } else { +- reg = __raw_readl(CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg |= (1 << (line - 32)); +- __raw_writel(reg, CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } +- } else { +- if (line < 32) { +- reg = __raw_readl(CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg &= ~(1 << line); +- __raw_writel(reg, CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } else { +- reg = __raw_readl(CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- reg &= ~(1 << (line - 32)); +- __raw_writel(reg, CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_DIR); +- } +- } +-} +- +-static int cns3xxx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +-{ +- gpio_line_config(gpio, CNS3XXX_GPIO_IN); +- return 0; +-} +- +-static int cns3xxx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) +-{ +- gpio_line_set(gpio, level); +- gpio_line_config(gpio, CNS3XXX_GPIO_OUT); +- return 0; +-} +- +-static int cns3xxx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +-{ +- return gpio_get_value(gpio); +-} +- +-static void cns3xxx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) +-{ +- gpio_set_value(gpio, value); +-} +- +-static struct gpio_chip cns3xxx_gpio_chip = { +- .label = "CNS3XXX_GPIO_CHIP", +- .direction_input = cns3xxx_gpio_direction_input, +- .direction_output = cns3xxx_gpio_direction_output, +- .get = cns3xxx_gpio_get_value, +- .set = cns3xxx_gpio_set_value, +- .base = 0, +- .ngpio = 64, +-}; +- + void __init cns3xxx_common_init(void) + { + #ifdef CONFIG_LOCAL_TIMERS + twd_base = (void __iomem *) CNS3XXX_TC11MP_TWD_BASE_VIRT; + #endif + iotable_init(cns3xxx_io_desc, ARRAY_SIZE(cns3xxx_io_desc)); +- +- gpiochip_add(&cns3xxx_gpio_chip); + } + + /* used by entry-macro.S */ +--- /dev/null ++++ b/arch/arm/mach-cns3xxx/gpio.c +@@ -0,0 +1,277 @@ ++/* ++ * Copyright 2012 Gateworks Corporation ++ * Chris Lang ++ * Tim Harvey ++ * ++ * This file is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License, Version 2, as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ * Registers ++ */ ++#define GPIO_INPUT 0x04 ++#define GPIO_DIR 0x08 ++#define GPIO_SET 0x10 ++#define GPIO_CLEAR 0x14 ++#define GPIO_INTERRUPT_ENABLE 0x20 ++#define GPIO_INTERRUPT_RAW_STATUS 0x24 ++#define GPIO_INTERRUPT_MASKED_STATUS 0x28 ++#define GPIO_INTERRUPT_MASK 0x2C ++#define GPIO_INTERRUPT_CLEAR 0x30 ++#define GPIO_INTERRUPT_TRIGGER_METHOD 0x34 ++#define GPIO_INTERRUPT_TRIGGER_BOTH_EDGES 0x38 ++#define GPIO_INTERRUPT_TRIGGER_TYPE 0x3C ++ ++#define GPIO_INTERRUPT_TRIGGER_METHOD_EDGE 0 ++#define GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL 1 ++#define GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE 0 ++#define GPIO_INTERRUPT_TRIGGER_EDGE_BOTH 1 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_RISING 0 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_FALLING 1 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_HIGH 0 ++#define GPIO_INTERRUPT_TRIGGER_TYPE_LOW 1 ++ ++struct cns3xxx_gpio_chip { ++ struct gpio_chip chip; ++ spinlock_t lock; ++ void __iomem *base; ++ int secondary_irq_base; ++}; ++ ++static struct cns3xxx_gpio_chip cns3xxx_gpio_chips[2]; ++static int cns3xxx_gpio_chip_count; ++ ++static inline void ++__set_direction(struct cns3xxx_gpio_chip *cchip, unsigned pin, int input) ++{ ++ u32 reg; ++ ++ reg = __raw_readl(cchip->base + GPIO_DIR); ++ if (input) ++ reg |= 1 << pin; ++ else ++ reg &= !(1 << pin); ++ __raw_writel(reg, cchip->base + GPIO_DIR); ++} ++ ++/* ++ * GENERIC_GPIO primatives ++ */ ++static int cns3xxx_gpio_direction_input(struct gpio_chip *chip, unsigned pin) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cchip->lock, flags); ++ __set_direction(cchip, pin, 1); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ ++ return 0; ++} ++ ++static int cns3xxx_gpio_get(struct gpio_chip *chip, unsigned pin) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ int val; ++ ++ val = ((__raw_readl(cchip->base + GPIO_INPUT) >> pin) & 0x1); ++ ++ return val; ++} ++ ++static int cns3xxx_gpio_direction_output(struct gpio_chip *chip, unsigned pin, int level) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cchip->lock, flags); ++ if (level) ++ __raw_writel(1 << pin, cchip->base + GPIO_SET); ++ else ++ __raw_writel(1 << pin, cchip->base + GPIO_CLEAR); ++ __set_direction(cchip, pin, 0); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ ++ return 0; ++} ++ ++static void cns3xxx_gpio_set(struct gpio_chip *chip, unsigned pin, ++ int level) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ ++ if (level) ++ __raw_writel(1 << pin, cchip->base + GPIO_SET); ++ else ++ __raw_writel(1 << pin, cchip->base + GPIO_CLEAR); ++} ++ ++static int cns3xxx_gpio_to_irq(struct gpio_chip *chip, unsigned pin) ++{ ++ struct cns3xxx_gpio_chip *cchip = ++ container_of(chip, struct cns3xxx_gpio_chip, chip); ++ ++ return cchip->secondary_irq_base + pin; ++} ++ ++ ++/* ++ * IRQ support ++ */ ++ ++/* one interrupt per GPIO controller (GPIOA/GPIOB) ++ * this is called in task context, with IRQs enabled ++ */ ++static void cns3xxx_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct cns3xxx_gpio_chip *cchip = irq_get_handler_data(irq); ++ struct irq_chip *chip = irq_get_chip(irq); ++ struct irq_chip_generic *gc = irq_desc_get_chip_data(desc); ++ struct irq_chip_type *ct = gc->chip_types; ++ u16 i; ++ u32 reg; ++ ++ chained_irq_enter(chip, desc); /* mask and ack the base interrupt */ ++ ++ /* see which pin(s) triggered the interrupt */ ++ reg = __raw_readl(cchip->base + GPIO_INTERRUPT_RAW_STATUS); ++ for (i = 0; i < 32; i++) { ++ if (reg & (1 << i)) { ++ /* let the generic IRQ layer handle an interrupt */ ++ generic_handle_irq(cchip->secondary_irq_base + i); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); /* unmask the base interrupt */ ++} ++ ++static int cns3xxx_gpio_irq_set_type(struct irq_data *d, u32 irqtype) ++{ ++ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); ++ struct cns3xxx_gpio_chip *cchip = gc->private; ++ u32 gpio = d->irq - cchip->secondary_irq_base; ++ unsigned long flags; ++ u32 method, edges, type; ++ ++ spin_lock_irqsave(&cchip->lock, flags); ++ method = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD); ++ edges = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES); ++ type = __raw_readl(cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE); ++ method &= ~(1 << gpio); ++ edges &= ~(1 << gpio); ++ type &= ~(1 << gpio); ++ ++ switch(irqtype) { ++ case IRQ_TYPE_EDGE_RISING: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); ++ edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_RISING << gpio); ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); ++ edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_SINGLE << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_FALLING << gpio); ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_EDGE << gpio); ++ edges |= (GPIO_INTERRUPT_TRIGGER_EDGE_BOTH << gpio); ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_LOW << gpio); ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ method |= (GPIO_INTERRUPT_TRIGGER_METHOD_LEVEL << gpio); ++ type |= (GPIO_INTERRUPT_TRIGGER_TYPE_HIGH << gpio); ++ break; ++ default: ++ printk(KERN_WARNING "No irq type\n"); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ return -EINVAL; ++ } ++ ++ __raw_writel(method, cchip->base + GPIO_INTERRUPT_TRIGGER_METHOD); ++ __raw_writel(edges, cchip->base + GPIO_INTERRUPT_TRIGGER_BOTH_EDGES); ++ __raw_writel(type, cchip->base + GPIO_INTERRUPT_TRIGGER_TYPE); ++ spin_unlock_irqrestore(&cchip->lock, flags); ++ ++ if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) ++ __irq_set_handler_locked(d->irq, handle_level_irq); ++ else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) ++ __irq_set_handler_locked(d->irq, handle_edge_irq); ++ ++ return 0; ++} ++ ++void __init cns3xxx_gpio_init(int gpio_base, int ngpio, ++ u32 base, int irq, int secondary_irq_base) ++{ ++ struct cns3xxx_gpio_chip *cchip; ++ struct irq_chip_generic *gc; ++ struct irq_chip_type *ct; ++ char gc_label[16]; ++ ++ if (cns3xxx_gpio_chip_count == ARRAY_SIZE(cns3xxx_gpio_chips)) ++ return; ++ ++ snprintf(gc_label, sizeof(gc_label), "cns3xxx_gpio%d", ++ cns3xxx_gpio_chip_count); ++ ++ cchip = cns3xxx_gpio_chips + cns3xxx_gpio_chip_count; ++ cchip->chip.label = kstrdup(gc_label, GFP_KERNEL); ++ cchip->chip.direction_input = cns3xxx_gpio_direction_input; ++ cchip->chip.get = cns3xxx_gpio_get; ++ cchip->chip.direction_output = cns3xxx_gpio_direction_output; ++ cchip->chip.set = cns3xxx_gpio_set; ++ cchip->chip.to_irq = cns3xxx_gpio_to_irq; ++ cchip->chip.base = gpio_base; ++ cchip->chip.ngpio = ngpio; ++ cchip->chip.can_sleep = 0; ++ spin_lock_init(&cchip->lock); ++ cchip->base = (void __iomem *)base; ++ cchip->secondary_irq_base = secondary_irq_base; ++ ++ BUG_ON(gpiochip_add(&cchip->chip) < 0); ++ cns3xxx_gpio_chip_count++; ++ ++ /* clear GPIO interrupts */ ++ __raw_writel(0xffff, cchip->base + GPIO_INTERRUPT_CLEAR); ++ ++ /* ++ * IRQ chip init ++ */ ++ gc = irq_alloc_generic_chip("cns3xxx_gpio_irq", 1, secondary_irq_base, ++ cchip->base, handle_edge_irq); ++ gc->private = cchip; ++ ++ ct = gc->chip_types; ++ ct->type = IRQ_TYPE_EDGE_FALLING; ++ ct->regs.ack = GPIO_INTERRUPT_CLEAR; ++ ct->regs.enable = GPIO_INTERRUPT_ENABLE; ++ ct->chip.irq_ack = irq_gc_ack_set_bit; ++ ct->chip.irq_enable = irq_gc_unmask_enable_reg; ++ ct->chip.irq_disable = irq_gc_mask_disable_reg; ++ ct->chip.irq_set_type = cns3xxx_gpio_irq_set_type; ++ ct->handler = handle_edge_irq; ++ ++ irq_setup_generic_chip(gc, IRQ_MSK(ngpio), IRQ_GC_INIT_MASK_CACHE, ++ IRQ_NOREQUEST, 0); ++ ++ irq_set_chained_handler(irq, cns3xxx_gpio_irq_handler); ++ irq_set_handler_data(irq, cchip); ++} +--- a/arch/arm/mach-cns3xxx/include/mach/gpio.h ++++ b/arch/arm/mach-cns3xxx/include/mach/gpio.h +@@ -1,98 +1,17 @@ + /* + * arch/arm/mach-cns3xxx/include/mach/gpio.h + * +- * CNS3xxx GPIO wrappers for arch-neutral GPIO calls +- * +- * Copyright 2011 Gateworks Corporation +- * Chris Lang +- * +- * Based on IXP implementation by Milan Svoboda +- * Based on PXA implementation by Philipp Zabel +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * 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. + * + */ +- + #ifndef __ASM_ARCH_CNS3XXX_GPIO_H + #define __ASM_ARCH_CNS3XXX_GPIO_H + + #include +-#include +-#include +-#include /* cansleep wrappers */ +- +-#define NR_BUILTIN_GPIO 64 +- +-#define CNS3XXX_GPIO_IN 0x0 +-#define CNS3XXX_GPIO_OUT 0x1 +- +-#define CNS3XXX_GPIO_LO 0 +-#define CNS3XXX_GPIO_HI 1 +- +-#define CNS3XXX_GPIO_OUTPUT 0x00 +-#define CNS3XXX_GPIO_INPUT 0x04 +-#define CNS3XXX_GPIO_DIR 0x08 +-#define CNS3XXX_GPIO_SET 0x10 +-#define CNS3XXX_GPIO_CLEAR 0x14 +- +-static inline void gpio_line_get(u8 line, int *value) +-{ +- if (line < 32) +- *value = ((__raw_readl(CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_INPUT) >> line) & 0x1); +- else +- *value = ((__raw_readl(CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_INPUT) >> (line - 32)) & 0x1); +-} +- +-static inline void gpio_line_set(u8 line, int value) +-{ +- if (line < 32) { +- if (value) +- __raw_writel((1 << line), CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_SET); +- else +- __raw_writel((1 << line), CNS3XXX_GPIOA_BASE_VIRT + CNS3XXX_GPIO_CLEAR); +- } else { +- if (value) +- __raw_writel((1 << line), CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_SET); +- else +- __raw_writel((1 << line), CNS3XXX_GPIOB_BASE_VIRT + CNS3XXX_GPIO_CLEAR); +- } +-} +- +-static inline int gpio_get_value(unsigned gpio) +-{ +- if (gpio < NR_BUILTIN_GPIO) +- { +- int value; +- gpio_line_get(gpio, &value); +- return value; +- } +- else +- return __gpio_get_value(gpio); +-} +- +-static inline void gpio_set_value(unsigned gpio, int value) +-{ +- if (gpio < NR_BUILTIN_GPIO) +- gpio_line_set(gpio, value); +- else +- __gpio_set_value(gpio, value); +-} +- +-#define gpio_cansleep __gpio_cansleep + +-extern int gpio_to_irq(int gpio); +-extern int irq_to_gpio(int gpio); ++extern void __init cns3xxx_gpio_init(int gpio_base, int ngpio, ++ u32 base, int irq, int secondary_irq_base); + + #endif +--- a/arch/arm/mach-cns3xxx/laguna.c ++++ b/arch/arm/mach-cns3xxx/laguna.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include "core.h" + #include "devices.h" +@@ -759,6 +760,10 @@ static int __init laguna_model_setup(voi + u8 pcie_bitmap = 0; + + printk("Running on Gateworks Laguna %s\n", laguna_info.model); ++ cns3xxx_gpio_init( 0, 32, CNS3XXX_GPIOA_BASE_VIRT, IRQ_CNS3XXX_GPIOA, ++ NR_IRQS_CNS3XXX); ++ cns3xxx_gpio_init(32, 32, CNS3XXX_GPIOB_BASE_VIRT, IRQ_CNS3XXX_GPIOB, ++ NR_IRQS_CNS3XXX + 32); + + if (strncmp(laguna_info.model, "GW", 2) == 0) { + if (laguna_info.config_bitmap & ETH0_LOAD) +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -366,7 +366,8 @@ config ARCH_CLPS711X + config ARCH_CNS3XXX + bool "Cavium Networks CNS3XXX family" + select CPU_V6K +- select ARCH_WANT_OPTIONAL_GPIOLIB ++ select ARCH_REQUIRE_GPIOLIB ++ select GENERIC_IRQ_CHIP + select GENERIC_CLOCKEVENTS + select ARM_GIC + select CLKDEV_LOOKUP +--- a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h ++++ b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h +@@ -627,7 +627,7 @@ int cns3xxx_cpu_clock(void); + + #if !defined(NR_IRQS) || (NR_IRQS < NR_IRQS_CNS3XXX) + #undef NR_IRQS +-#define NR_IRQS NR_IRQS_CNS3XXX ++#define NR_IRQS (NR_IRQS_CNS3XXX + 64) + #endif + + #endif /* __MACH_BOARD_CNS3XXX_H */ -- 2.30.2