162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 762306a36Sopenharmony_ci * Copyright (C) 2008-2011 Florian Fainelli <florian@openwrt.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/spinlock.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <bcm63xx_cpu.h> 1762306a36Sopenharmony_ci#include <bcm63xx_gpio.h> 1862306a36Sopenharmony_ci#include <bcm63xx_io.h> 1962306a36Sopenharmony_ci#include <bcm63xx_regs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic u32 gpio_out_low_reg; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void bcm63xx_gpio_out_low_reg_init(void) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci switch (bcm63xx_get_cpu_id()) { 2662306a36Sopenharmony_ci case BCM6345_CPU_ID: 2762306a36Sopenharmony_ci gpio_out_low_reg = GPIO_DATA_LO_REG_6345; 2862306a36Sopenharmony_ci break; 2962306a36Sopenharmony_ci default: 3062306a36Sopenharmony_ci gpio_out_low_reg = GPIO_DATA_LO_REG; 3162306a36Sopenharmony_ci break; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(bcm63xx_gpio_lock); 3662306a36Sopenharmony_cistatic u32 gpio_out_low, gpio_out_high; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void bcm63xx_gpio_set(struct gpio_chip *chip, 3962306a36Sopenharmony_ci unsigned gpio, int val) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci u32 reg; 4262306a36Sopenharmony_ci u32 mask; 4362306a36Sopenharmony_ci u32 *v; 4462306a36Sopenharmony_ci unsigned long flags; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci BUG_ON(gpio >= chip->ngpio); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (gpio < 32) { 4962306a36Sopenharmony_ci reg = gpio_out_low_reg; 5062306a36Sopenharmony_ci mask = 1 << gpio; 5162306a36Sopenharmony_ci v = &gpio_out_low; 5262306a36Sopenharmony_ci } else { 5362306a36Sopenharmony_ci reg = GPIO_DATA_HI_REG; 5462306a36Sopenharmony_ci mask = 1 << (gpio - 32); 5562306a36Sopenharmony_ci v = &gpio_out_high; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci spin_lock_irqsave(&bcm63xx_gpio_lock, flags); 5962306a36Sopenharmony_ci if (val) 6062306a36Sopenharmony_ci *v |= mask; 6162306a36Sopenharmony_ci else 6262306a36Sopenharmony_ci *v &= ~mask; 6362306a36Sopenharmony_ci bcm_gpio_writel(*v, reg); 6462306a36Sopenharmony_ci spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int bcm63xx_gpio_get(struct gpio_chip *chip, unsigned gpio) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci u32 reg; 7062306a36Sopenharmony_ci u32 mask; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci BUG_ON(gpio >= chip->ngpio); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (gpio < 32) { 7562306a36Sopenharmony_ci reg = gpio_out_low_reg; 7662306a36Sopenharmony_ci mask = 1 << gpio; 7762306a36Sopenharmony_ci } else { 7862306a36Sopenharmony_ci reg = GPIO_DATA_HI_REG; 7962306a36Sopenharmony_ci mask = 1 << (gpio - 32); 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return !!(bcm_gpio_readl(reg) & mask); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int bcm63xx_gpio_set_direction(struct gpio_chip *chip, 8662306a36Sopenharmony_ci unsigned gpio, int dir) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci u32 reg; 8962306a36Sopenharmony_ci u32 mask; 9062306a36Sopenharmony_ci u32 tmp; 9162306a36Sopenharmony_ci unsigned long flags; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci BUG_ON(gpio >= chip->ngpio); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (gpio < 32) { 9662306a36Sopenharmony_ci reg = GPIO_CTL_LO_REG; 9762306a36Sopenharmony_ci mask = 1 << gpio; 9862306a36Sopenharmony_ci } else { 9962306a36Sopenharmony_ci reg = GPIO_CTL_HI_REG; 10062306a36Sopenharmony_ci mask = 1 << (gpio - 32); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci spin_lock_irqsave(&bcm63xx_gpio_lock, flags); 10462306a36Sopenharmony_ci tmp = bcm_gpio_readl(reg); 10562306a36Sopenharmony_ci if (dir == BCM63XX_GPIO_DIR_IN) 10662306a36Sopenharmony_ci tmp &= ~mask; 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci tmp |= mask; 10962306a36Sopenharmony_ci bcm_gpio_writel(tmp, reg); 11062306a36Sopenharmony_ci spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int bcm63xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_IN); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int bcm63xx_gpio_direction_output(struct gpio_chip *chip, 12162306a36Sopenharmony_ci unsigned gpio, int value) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci bcm63xx_gpio_set(chip, gpio, value); 12462306a36Sopenharmony_ci return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_OUT); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct gpio_chip bcm63xx_gpio_chip = { 12962306a36Sopenharmony_ci .label = "bcm63xx-gpio", 13062306a36Sopenharmony_ci .direction_input = bcm63xx_gpio_direction_input, 13162306a36Sopenharmony_ci .direction_output = bcm63xx_gpio_direction_output, 13262306a36Sopenharmony_ci .get = bcm63xx_gpio_get, 13362306a36Sopenharmony_ci .set = bcm63xx_gpio_set, 13462306a36Sopenharmony_ci .base = 0, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciint __init bcm63xx_gpio_init(void) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci bcm63xx_gpio_out_low_reg_init(); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci gpio_out_low = bcm_gpio_readl(gpio_out_low_reg); 14262306a36Sopenharmony_ci if (!BCMCPU_IS_6345()) 14362306a36Sopenharmony_ci gpio_out_high = bcm_gpio_readl(GPIO_DATA_HI_REG); 14462306a36Sopenharmony_ci bcm63xx_gpio_chip.ngpio = bcm63xx_gpio_count(); 14562306a36Sopenharmony_ci pr_info("registering %d GPIOs\n", bcm63xx_gpio_chip.ngpio); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return gpiochip_add_data(&bcm63xx_gpio_chip, NULL); 14862306a36Sopenharmony_ci} 149