18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 78c2ecf20Sopenharmony_ci * Copyright (C) 2008-2011 Florian Fainelli <florian@openwrt.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <bcm63xx_cpu.h> 178c2ecf20Sopenharmony_ci#include <bcm63xx_gpio.h> 188c2ecf20Sopenharmony_ci#include <bcm63xx_io.h> 198c2ecf20Sopenharmony_ci#include <bcm63xx_regs.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic u32 gpio_out_low_reg; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void bcm63xx_gpio_out_low_reg_init(void) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci switch (bcm63xx_get_cpu_id()) { 268c2ecf20Sopenharmony_ci case BCM6345_CPU_ID: 278c2ecf20Sopenharmony_ci gpio_out_low_reg = GPIO_DATA_LO_REG_6345; 288c2ecf20Sopenharmony_ci break; 298c2ecf20Sopenharmony_ci default: 308c2ecf20Sopenharmony_ci gpio_out_low_reg = GPIO_DATA_LO_REG; 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(bcm63xx_gpio_lock); 368c2ecf20Sopenharmony_cistatic u32 gpio_out_low, gpio_out_high; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void bcm63xx_gpio_set(struct gpio_chip *chip, 398c2ecf20Sopenharmony_ci unsigned gpio, int val) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci u32 reg; 428c2ecf20Sopenharmony_ci u32 mask; 438c2ecf20Sopenharmony_ci u32 *v; 448c2ecf20Sopenharmony_ci unsigned long flags; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (gpio >= chip->ngpio) 478c2ecf20Sopenharmony_ci BUG(); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (gpio < 32) { 508c2ecf20Sopenharmony_ci reg = gpio_out_low_reg; 518c2ecf20Sopenharmony_ci mask = 1 << gpio; 528c2ecf20Sopenharmony_ci v = &gpio_out_low; 538c2ecf20Sopenharmony_ci } else { 548c2ecf20Sopenharmony_ci reg = GPIO_DATA_HI_REG; 558c2ecf20Sopenharmony_ci mask = 1 << (gpio - 32); 568c2ecf20Sopenharmony_ci v = &gpio_out_high; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci spin_lock_irqsave(&bcm63xx_gpio_lock, flags); 608c2ecf20Sopenharmony_ci if (val) 618c2ecf20Sopenharmony_ci *v |= mask; 628c2ecf20Sopenharmony_ci else 638c2ecf20Sopenharmony_ci *v &= ~mask; 648c2ecf20Sopenharmony_ci bcm_gpio_writel(*v, reg); 658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int bcm63xx_gpio_get(struct gpio_chip *chip, unsigned gpio) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u32 reg; 718c2ecf20Sopenharmony_ci u32 mask; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (gpio >= chip->ngpio) 748c2ecf20Sopenharmony_ci BUG(); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (gpio < 32) { 778c2ecf20Sopenharmony_ci reg = gpio_out_low_reg; 788c2ecf20Sopenharmony_ci mask = 1 << gpio; 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci reg = GPIO_DATA_HI_REG; 818c2ecf20Sopenharmony_ci mask = 1 << (gpio - 32); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return !!(bcm_gpio_readl(reg) & mask); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int bcm63xx_gpio_set_direction(struct gpio_chip *chip, 888c2ecf20Sopenharmony_ci unsigned gpio, int dir) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci u32 reg; 918c2ecf20Sopenharmony_ci u32 mask; 928c2ecf20Sopenharmony_ci u32 tmp; 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (gpio >= chip->ngpio) 968c2ecf20Sopenharmony_ci BUG(); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (gpio < 32) { 998c2ecf20Sopenharmony_ci reg = GPIO_CTL_LO_REG; 1008c2ecf20Sopenharmony_ci mask = 1 << gpio; 1018c2ecf20Sopenharmony_ci } else { 1028c2ecf20Sopenharmony_ci reg = GPIO_CTL_HI_REG; 1038c2ecf20Sopenharmony_ci mask = 1 << (gpio - 32); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(&bcm63xx_gpio_lock, flags); 1078c2ecf20Sopenharmony_ci tmp = bcm_gpio_readl(reg); 1088c2ecf20Sopenharmony_ci if (dir == BCM63XX_GPIO_DIR_IN) 1098c2ecf20Sopenharmony_ci tmp &= ~mask; 1108c2ecf20Sopenharmony_ci else 1118c2ecf20Sopenharmony_ci tmp |= mask; 1128c2ecf20Sopenharmony_ci bcm_gpio_writel(tmp, reg); 1138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int bcm63xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_IN); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int bcm63xx_gpio_direction_output(struct gpio_chip *chip, 1248c2ecf20Sopenharmony_ci unsigned gpio, int value) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci bcm63xx_gpio_set(chip, gpio, value); 1278c2ecf20Sopenharmony_ci return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_OUT); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct gpio_chip bcm63xx_gpio_chip = { 1328c2ecf20Sopenharmony_ci .label = "bcm63xx-gpio", 1338c2ecf20Sopenharmony_ci .direction_input = bcm63xx_gpio_direction_input, 1348c2ecf20Sopenharmony_ci .direction_output = bcm63xx_gpio_direction_output, 1358c2ecf20Sopenharmony_ci .get = bcm63xx_gpio_get, 1368c2ecf20Sopenharmony_ci .set = bcm63xx_gpio_set, 1378c2ecf20Sopenharmony_ci .base = 0, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciint __init bcm63xx_gpio_init(void) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci bcm63xx_gpio_out_low_reg_init(); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci gpio_out_low = bcm_gpio_readl(gpio_out_low_reg); 1458c2ecf20Sopenharmony_ci if (!BCMCPU_IS_6345()) 1468c2ecf20Sopenharmony_ci gpio_out_high = bcm_gpio_readl(GPIO_DATA_HI_REG); 1478c2ecf20Sopenharmony_ci bcm63xx_gpio_chip.ngpio = bcm63xx_gpio_count(); 1488c2ecf20Sopenharmony_ci pr_info("registering %d GPIOs\n", bcm63xx_gpio_chip.ngpio); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return gpiochip_add_data(&bcm63xx_gpio_chip, NULL); 1518c2ecf20Sopenharmony_ci} 152