1e7300d04SMaxime Bizon /*
2e7300d04SMaxime Bizon * This file is subject to the terms and conditions of the GNU General Public
3e7300d04SMaxime Bizon * License. See the file "COPYING" in the main directory of this archive
4e7300d04SMaxime Bizon * for more details.
5e7300d04SMaxime Bizon *
6e7300d04SMaxime Bizon * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
792d9ae20SFlorian Fainelli * Copyright (C) 2008-2011 Florian Fainelli <florian@openwrt.org>
8e7300d04SMaxime Bizon */
9e7300d04SMaxime Bizon
10e7300d04SMaxime Bizon #include <linux/kernel.h>
1126dd3e4fSPaul Gortmaker #include <linux/init.h>
12e7300d04SMaxime Bizon #include <linux/spinlock.h>
13e7300d04SMaxime Bizon #include <linux/platform_device.h>
146483d829SLinus Walleij #include <linux/gpio/driver.h>
15e7300d04SMaxime Bizon
16e7300d04SMaxime Bizon #include <bcm63xx_cpu.h>
17e7300d04SMaxime Bizon #include <bcm63xx_gpio.h>
18e7300d04SMaxime Bizon #include <bcm63xx_io.h>
19e7300d04SMaxime Bizon #include <bcm63xx_regs.h>
20e7300d04SMaxime Bizon
2192d9ae20SFlorian Fainelli static u32 gpio_out_low_reg;
2292d9ae20SFlorian Fainelli
bcm63xx_gpio_out_low_reg_init(void)2392d9ae20SFlorian Fainelli static void bcm63xx_gpio_out_low_reg_init(void)
2492d9ae20SFlorian Fainelli {
2592d9ae20SFlorian Fainelli switch (bcm63xx_get_cpu_id()) {
2692d9ae20SFlorian Fainelli case BCM6345_CPU_ID:
2792d9ae20SFlorian Fainelli gpio_out_low_reg = GPIO_DATA_LO_REG_6345;
2892d9ae20SFlorian Fainelli break;
2992d9ae20SFlorian Fainelli default:
3092d9ae20SFlorian Fainelli gpio_out_low_reg = GPIO_DATA_LO_REG;
3192d9ae20SFlorian Fainelli break;
3292d9ae20SFlorian Fainelli }
3392d9ae20SFlorian Fainelli }
3492d9ae20SFlorian Fainelli
35e7300d04SMaxime Bizon static DEFINE_SPINLOCK(bcm63xx_gpio_lock);
36e7300d04SMaxime Bizon static u32 gpio_out_low, gpio_out_high;
37e7300d04SMaxime Bizon
bcm63xx_gpio_set(struct gpio_chip * chip,unsigned gpio,int val)38e7300d04SMaxime Bizon static void bcm63xx_gpio_set(struct gpio_chip *chip,
39e7300d04SMaxime Bizon unsigned gpio, int val)
40e7300d04SMaxime Bizon {
41e7300d04SMaxime Bizon u32 reg;
42e7300d04SMaxime Bizon u32 mask;
43e7300d04SMaxime Bizon u32 *v;
44e7300d04SMaxime Bizon unsigned long flags;
45e7300d04SMaxime Bizon
46*7e9be673Szhouchuangao BUG_ON(gpio >= chip->ngpio);
47e7300d04SMaxime Bizon
48e7300d04SMaxime Bizon if (gpio < 32) {
4992d9ae20SFlorian Fainelli reg = gpio_out_low_reg;
50e7300d04SMaxime Bizon mask = 1 << gpio;
51e7300d04SMaxime Bizon v = &gpio_out_low;
52e7300d04SMaxime Bizon } else {
53e7300d04SMaxime Bizon reg = GPIO_DATA_HI_REG;
54e7300d04SMaxime Bizon mask = 1 << (gpio - 32);
55e7300d04SMaxime Bizon v = &gpio_out_high;
56e7300d04SMaxime Bizon }
57e7300d04SMaxime Bizon
58e7300d04SMaxime Bizon spin_lock_irqsave(&bcm63xx_gpio_lock, flags);
59e7300d04SMaxime Bizon if (val)
60e7300d04SMaxime Bizon *v |= mask;
61e7300d04SMaxime Bizon else
62e7300d04SMaxime Bizon *v &= ~mask;
63e7300d04SMaxime Bizon bcm_gpio_writel(*v, reg);
64e7300d04SMaxime Bizon spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags);
65e7300d04SMaxime Bizon }
66e7300d04SMaxime Bizon
bcm63xx_gpio_get(struct gpio_chip * chip,unsigned gpio)67e7300d04SMaxime Bizon static int bcm63xx_gpio_get(struct gpio_chip *chip, unsigned gpio)
68e7300d04SMaxime Bizon {
69e7300d04SMaxime Bizon u32 reg;
70e7300d04SMaxime Bizon u32 mask;
71e7300d04SMaxime Bizon
72*7e9be673Szhouchuangao BUG_ON(gpio >= chip->ngpio);
73e7300d04SMaxime Bizon
74e7300d04SMaxime Bizon if (gpio < 32) {
7592d9ae20SFlorian Fainelli reg = gpio_out_low_reg;
76e7300d04SMaxime Bizon mask = 1 << gpio;
77e7300d04SMaxime Bizon } else {
78e7300d04SMaxime Bizon reg = GPIO_DATA_HI_REG;
79e7300d04SMaxime Bizon mask = 1 << (gpio - 32);
80e7300d04SMaxime Bizon }
81e7300d04SMaxime Bizon
82e7300d04SMaxime Bizon return !!(bcm_gpio_readl(reg) & mask);
83e7300d04SMaxime Bizon }
84e7300d04SMaxime Bizon
bcm63xx_gpio_set_direction(struct gpio_chip * chip,unsigned gpio,int dir)85e7300d04SMaxime Bizon static int bcm63xx_gpio_set_direction(struct gpio_chip *chip,
86e7300d04SMaxime Bizon unsigned gpio, int dir)
87e7300d04SMaxime Bizon {
88e7300d04SMaxime Bizon u32 reg;
89e7300d04SMaxime Bizon u32 mask;
90e7300d04SMaxime Bizon u32 tmp;
91e7300d04SMaxime Bizon unsigned long flags;
92e7300d04SMaxime Bizon
93*7e9be673Szhouchuangao BUG_ON(gpio >= chip->ngpio);
94e7300d04SMaxime Bizon
95e7300d04SMaxime Bizon if (gpio < 32) {
96e7300d04SMaxime Bizon reg = GPIO_CTL_LO_REG;
97e7300d04SMaxime Bizon mask = 1 << gpio;
98e7300d04SMaxime Bizon } else {
99e7300d04SMaxime Bizon reg = GPIO_CTL_HI_REG;
100e7300d04SMaxime Bizon mask = 1 << (gpio - 32);
101e7300d04SMaxime Bizon }
102e7300d04SMaxime Bizon
103e7300d04SMaxime Bizon spin_lock_irqsave(&bcm63xx_gpio_lock, flags);
104e7300d04SMaxime Bizon tmp = bcm_gpio_readl(reg);
1053e1bf29fSFlorian Fainelli if (dir == BCM63XX_GPIO_DIR_IN)
106e7300d04SMaxime Bizon tmp &= ~mask;
107e7300d04SMaxime Bizon else
108e7300d04SMaxime Bizon tmp |= mask;
109e7300d04SMaxime Bizon bcm_gpio_writel(tmp, reg);
110e7300d04SMaxime Bizon spin_unlock_irqrestore(&bcm63xx_gpio_lock, flags);
111e7300d04SMaxime Bizon
112e7300d04SMaxime Bizon return 0;
113e7300d04SMaxime Bizon }
114e7300d04SMaxime Bizon
bcm63xx_gpio_direction_input(struct gpio_chip * chip,unsigned gpio)115e7300d04SMaxime Bizon static int bcm63xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
116e7300d04SMaxime Bizon {
1173e1bf29fSFlorian Fainelli return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_IN);
118e7300d04SMaxime Bizon }
119e7300d04SMaxime Bizon
bcm63xx_gpio_direction_output(struct gpio_chip * chip,unsigned gpio,int value)120e7300d04SMaxime Bizon static int bcm63xx_gpio_direction_output(struct gpio_chip *chip,
121e7300d04SMaxime Bizon unsigned gpio, int value)
122e7300d04SMaxime Bizon {
123e7300d04SMaxime Bizon bcm63xx_gpio_set(chip, gpio, value);
1243e1bf29fSFlorian Fainelli return bcm63xx_gpio_set_direction(chip, gpio, BCM63XX_GPIO_DIR_OUT);
125e7300d04SMaxime Bizon }
126e7300d04SMaxime Bizon
127e7300d04SMaxime Bizon
128e7300d04SMaxime Bizon static struct gpio_chip bcm63xx_gpio_chip = {
129e7300d04SMaxime Bizon .label = "bcm63xx-gpio",
130e7300d04SMaxime Bizon .direction_input = bcm63xx_gpio_direction_input,
131e7300d04SMaxime Bizon .direction_output = bcm63xx_gpio_direction_output,
132e7300d04SMaxime Bizon .get = bcm63xx_gpio_get,
133e7300d04SMaxime Bizon .set = bcm63xx_gpio_set,
134e7300d04SMaxime Bizon .base = 0,
135e7300d04SMaxime Bizon };
136e7300d04SMaxime Bizon
bcm63xx_gpio_init(void)137e7300d04SMaxime Bizon int __init bcm63xx_gpio_init(void)
138e7300d04SMaxime Bizon {
13992d9ae20SFlorian Fainelli bcm63xx_gpio_out_low_reg_init();
14092d9ae20SFlorian Fainelli
14192d9ae20SFlorian Fainelli gpio_out_low = bcm_gpio_readl(gpio_out_low_reg);
14292d9ae20SFlorian Fainelli if (!BCMCPU_IS_6345())
1439538ca63SMaxime Bizon gpio_out_high = bcm_gpio_readl(GPIO_DATA_HI_REG);
144e7300d04SMaxime Bizon bcm63xx_gpio_chip.ngpio = bcm63xx_gpio_count();
145e7300d04SMaxime Bizon pr_info("registering %d GPIOs\n", bcm63xx_gpio_chip.ngpio);
146e7300d04SMaxime Bizon
1476483d829SLinus Walleij return gpiochip_add_data(&bcm63xx_gpio_chip, NULL);
148e7300d04SMaxime Bizon }
149