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