diff --git a/arch/arm/boot/dts/celestial-cnc1800l.dts b/arch/arm/boot/dts/celestial-cnc1800l.dts index 9f8e66976..1d5e514d1 100644 --- a/arch/arm/boot/dts/celestial-cnc1800l.dts +++ b/arch/arm/boot/dts/celestial-cnc1800l.dts @@ -106,4 +106,13 @@ reg = <0x80100000 0x48>, //SMC <0xa4000000 0x2000000>; //NAND }; + + gpio@80260000 { + compatible = "cavium,cnc1800l-gpio"; + reg = <0x80260000 0x100>; + gpio-controller; + #gpio-cells = <2>; + // interrupts = <6>; + cavium,nrdevs = <14>; + }; }; \ No newline at end of file diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 947474f6a..9753e0191 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -135,6 +135,13 @@ config GPIO_ALTERA If driver is built as a module it will be called gpio-altera. +config GPIO_CELESTIAL_CNC1800L + bool "CNC1800L GPIO" + depends on OF_GPIO && MACH_CELESTIAL + select GPIOLIB_IRQCHIP + help + Support for the CNC1800L platform GPIO. + config GPIO_AMDPT tristate "AMD Promontory GPIO support" depends on ACPI diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fbcda637d..c34946b27 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_AGGREGATOR) += gpio-aggregator.o obj-$(CONFIG_GPIO_ALTERA_A10SR) += gpio-altera-a10sr.o obj-$(CONFIG_GPIO_ALTERA) += gpio-altera.o +obj-$(CONFIG_GPIO_CELESTIAL_CNC1800L) += gpio-cnc1800l.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o diff --git a/drivers/gpio/gpio-cnc1800l.c b/drivers/gpio/gpio-cnc1800l.c new file mode 100644 index 000000000..e34808975 --- /dev/null +++ b/drivers/gpio/gpio-cnc1800l.c @@ -0,0 +1,234 @@ +/**************************************************************************** + * Copyright (C) 2008-2010 Celestial Semiconductor Inc. + * All rights reserved + * + * [RELEASE HISTORY] + * VERSION DATE AUTHOR DESCRIPTION + * 0.1 10-04-? Hao.Ran Original + **************************************************************************** +*/ +/* + * Copyright (C) 2013 Altera Corporation + * Based on gpio-mpc8xxx.c + */ + +#include +#include +#include +#include /* For of_mm_gpio_chip */ +#include + +#define REG_GPIO_SWPORTA_DR 0x20 +#define REG_GPIO_SWPORTA_DDR 0x24 +#define REG_GPIO_EXT_PORTA 0x60 + + +#define REG_GPIO_SWPORTB_DR 0x34 +#define REG_GPIO_SWPORTB_DDR 0x38 +#define REG_GPIO_EXT_PORTB 0x64 + +#define GPIO_MAJOR 0 +#define GPIO_NR_DEVS 64 + +#define GPIO_PINMUXA 0x30 +#define GPIO_PINMUXB 0x44 + +//IRQ +#define PORTA_MAX_NUMBER 32 + +#define CNC_GPIO_IRQ 6 + +#define REG_GPIO_INTMASK_A 0x28 +#define REG_GPIO_INTMASK_B 0x3c + +#define REG_GPIO_POLARITY_A 0x2c +#define REG_GPIO_POLARITY_B 0x40 + +#define REG_GPIO_INT_S_A 0x68 +#define REG_GPIO_INT_S_B 0x6c + +struct cnc1800l_gpio_chip { + struct gpio_chip chip; + raw_spinlock_t gpio_lock; + int interrupt_trigger; + int mapped_irq; + struct irq_chip irq_chip; + void __iomem *base; +}; + +static int cnc1800l_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct cnc1800l_gpio_chip* cnc_gc = gpiochip_get_data(gc); + unsigned int *gpio_base = (unsigned int*)cnc_gc->base; + + + if((offset < 32)&&(offset >= 0)) + { + return (gpio_base[REG_GPIO_EXT_PORTA >> 2] >> offset) & 0x1; + } + else if((offset >= 32)&&(offset < gc->ngpio)) + { + return (gpio_base[REG_GPIO_EXT_PORTB >> 2] >> offset) & 0x1; + } + + return 0; +} + +static void cnc1800l_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct cnc1800l_gpio_chip *chip = gpiochip_get_data(gc); + unsigned long flags; + unsigned int *gpio_base = (unsigned int*)chip->base; + + raw_spin_lock_irqsave(&chip->gpio_lock, flags); + + if(offset < 32 && offset >= 0){ + if(!value){ + gpio_base[REG_GPIO_SWPORTA_DR>>2 ] &= ~(1 << offset); + }else{ + gpio_base[REG_GPIO_SWPORTA_DR>>2 ] |= (1 << offset); + } + }else if(offset >= 32 && offset < gc->ngpio){ + offset -= 32; + if(!value){ + gpio_base[REG_GPIO_SWPORTB_DR>>2 ] &= ~(1 << offset); + }else{ + gpio_base[REG_GPIO_SWPORTB_DR>>2 ] |= (1 << offset); + } + } + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +static int cnc1800_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct cnc1800l_gpio_chip *chip = gpiochip_get_data(gc); + unsigned long flags; + unsigned int *gpio_base = (unsigned int*)chip->base; + + raw_spin_lock_irqsave(&chip->gpio_lock, flags); + + if(offset < 32 && offset >= 0){ + gpio_base[REG_GPIO_SWPORTA_DDR >> 2] |= (1 << offset); + }else if(offset >= 32 && offset < gc->ngpio){ + offset -= 32; + gpio_base[REG_GPIO_SWPORTB_DDR >> 2] |= (1 << offset); + } + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +static int cnc1800l_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + struct cnc1800l_gpio_chip *chip = gpiochip_get_data(gc); + unsigned long flags; + unsigned int *gpio_base = (unsigned int*)chip->base; + + raw_spin_lock_irqsave(&chip->gpio_lock, flags); + + if(offset < 32 && offset >= 0){ + gpio_base[REG_GPIO_SWPORTA_DDR >> 2] &= ~(1<>2 ] &= ~(1 << offset); + }else{ + gpio_base[REG_GPIO_SWPORTA_DR>>2 ] |= (1 << offset); + } + }else if(offset >= 32 && offset < gc->ngpio){ + offset -= 32; + gpio_base[REG_GPIO_SWPORTB_DDR >> 2] &= ~(1<>2 ] &= ~(1 << offset); + }else{ + gpio_base[REG_GPIO_SWPORTB_DR>>2 ] |= (1 << offset); + } + } + raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +static int cnc1800l_gpio_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + int reg, ret; + struct cnc1800l_gpio_chip *cnc_gc; + struct resource *res; + + cnc_gc = devm_kzalloc(&pdev->dev, sizeof(struct cnc1800l_gpio_chip), GFP_KERNEL); + if (!cnc_gc) + return -ENOMEM; + + raw_spin_lock_init(&cnc_gc->gpio_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res){ + kfree(cnc_gc); + return -EINVAL; + } + + cnc_gc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(cnc_gc->base)){ + kfree(cnc_gc); + return PTR_ERR(cnc_gc->base); + } + + if (of_property_read_u32(node, "cavium,nrdevs", ®)) + cnc_gc->chip.ngpio = GPIO_NR_DEVS; + else + cnc_gc->chip.ngpio = reg; + + if (cnc_gc->chip.ngpio > GPIO_NR_DEVS) { + dev_warn(&pdev->dev, + "ngpio is greater than %d, defaulting to %d\n", + GPIO_NR_DEVS, GPIO_NR_DEVS); + cnc_gc->chip.ngpio = GPIO_NR_DEVS; + } + + cnc_gc->chip.direction_input = cnc1800_gpio_direction_input; + cnc_gc->chip.direction_output = cnc1800l_gpio_direction_output; + cnc_gc->chip.get = cnc1800l_gpio_get; + cnc_gc->chip.set = cnc1800l_gpio_set; + cnc_gc->chip.owner = THIS_MODULE; + cnc_gc->chip.parent = &pdev->dev; + + devm_gpiochip_add_data(&pdev->dev, &cnc_gc->chip, cnc_gc); + if (ret < 0) { + dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); + return ret; + } + + platform_set_drvdata(pdev, cnc_gc); + + return 0; +} + +static const struct of_device_id cnc1800l_gpio_of_match[] = { + { .compatible = "cavium,cnc1800l-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cnc1800l_gpio_of_match); + +static struct platform_driver cnc1800l_gpio_driver = { + .driver = { + .name = "cnc1800l_gpio", + .of_match_table = cnc1800l_gpio_of_match, + }, + .probe = cnc1800l_gpio_probe, +}; + +static int __init cnc1800l_gpio_init(void) +{ + return platform_driver_register(&cnc1800l_gpio_driver); +} +subsys_initcall(cnc1800l_gpio_init); + +static void __exit cnc1800l_gpio_exit(void) +{ + platform_driver_unregister(&cnc1800l_gpio_driver); +} +module_exit(cnc1800l_gpio_exit); + +MODULE_AUTHOR("Cavium"); +MODULE_DESCRIPTION("Cavium Celestial GPIO driver"); +MODULE_LICENSE("GPL");