Commit | Line | Data |
---|---|---|
6eae43c5 GJ |
1 | /* |
2 | * Atheros AR71XX/AR724X/AR913X GPIO API support | |
3 | * | |
5b5b544e GJ |
4 | * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> |
5 | * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> | |
6eae43c5 GJ |
6 | * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> |
7 | * | |
5b5b544e GJ |
8 | * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP |
9 | * | |
6eae43c5 GJ |
10 | * This program is free software; you can redistribute it and/or modify it |
11 | * under the terms of the GNU General Public License version 2 as published | |
12 | * by the Free Software Foundation. | |
13 | */ | |
14 | ||
49a5bd88 | 15 | #include <linux/gpio/driver.h> |
2ddf3a79 AB |
16 | #include <linux/platform_data/gpio-ath79.h> |
17 | #include <linux/of_device.h> | |
6eae43c5 GJ |
18 | |
19 | #include <asm/mach-ath79/ar71xx_regs.h> | |
6eae43c5 | 20 | |
49a5bd88 AB |
21 | struct ath79_gpio_ctrl { |
22 | struct gpio_chip chip; | |
23 | void __iomem *base; | |
24 | spinlock_t lock; | |
25 | }; | |
26 | ||
49a5bd88 AB |
27 | static void ath79_gpio_set_value(struct gpio_chip *chip, |
28 | unsigned gpio, int value) | |
6eae43c5 | 29 | { |
b4e97a61 | 30 | struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); |
6eae43c5 GJ |
31 | |
32 | if (value) | |
49a5bd88 | 33 | __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_SET); |
6eae43c5 | 34 | else |
49a5bd88 | 35 | __raw_writel(BIT(gpio), ctrl->base + AR71XX_GPIO_REG_CLEAR); |
6eae43c5 GJ |
36 | } |
37 | ||
49a5bd88 | 38 | static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned gpio) |
6eae43c5 | 39 | { |
b4e97a61 | 40 | struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); |
6eae43c5 | 41 | |
49a5bd88 | 42 | return (__raw_readl(ctrl->base + AR71XX_GPIO_REG_IN) >> gpio) & 1; |
6eae43c5 GJ |
43 | } |
44 | ||
45 | static int ath79_gpio_direction_input(struct gpio_chip *chip, | |
46 | unsigned offset) | |
47 | { | |
b4e97a61 | 48 | struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); |
6eae43c5 GJ |
49 | unsigned long flags; |
50 | ||
49a5bd88 | 51 | spin_lock_irqsave(&ctrl->lock, flags); |
6eae43c5 | 52 | |
49a5bd88 AB |
53 | __raw_writel( |
54 | __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset), | |
55 | ctrl->base + AR71XX_GPIO_REG_OE); | |
6eae43c5 | 56 | |
49a5bd88 | 57 | spin_unlock_irqrestore(&ctrl->lock, flags); |
6eae43c5 GJ |
58 | |
59 | return 0; | |
60 | } | |
61 | ||
62 | static int ath79_gpio_direction_output(struct gpio_chip *chip, | |
63 | unsigned offset, int value) | |
64 | { | |
b4e97a61 | 65 | struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); |
6eae43c5 GJ |
66 | unsigned long flags; |
67 | ||
49a5bd88 | 68 | spin_lock_irqsave(&ctrl->lock, flags); |
6eae43c5 GJ |
69 | |
70 | if (value) | |
49a5bd88 | 71 | __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET); |
6eae43c5 | 72 | else |
49a5bd88 | 73 | __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR); |
6eae43c5 | 74 | |
49a5bd88 AB |
75 | __raw_writel( |
76 | __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset), | |
77 | ctrl->base + AR71XX_GPIO_REG_OE); | |
6eae43c5 | 78 | |
49a5bd88 | 79 | spin_unlock_irqrestore(&ctrl->lock, flags); |
6eae43c5 GJ |
80 | |
81 | return 0; | |
82 | } | |
83 | ||
5b5b544e GJ |
84 | static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) |
85 | { | |
b4e97a61 | 86 | struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); |
5b5b544e GJ |
87 | unsigned long flags; |
88 | ||
49a5bd88 | 89 | spin_lock_irqsave(&ctrl->lock, flags); |
5b5b544e | 90 | |
49a5bd88 AB |
91 | __raw_writel( |
92 | __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) | BIT(offset), | |
93 | ctrl->base + AR71XX_GPIO_REG_OE); | |
5b5b544e | 94 | |
49a5bd88 | 95 | spin_unlock_irqrestore(&ctrl->lock, flags); |
5b5b544e GJ |
96 | |
97 | return 0; | |
98 | } | |
99 | ||
100 | static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, | |
101 | int value) | |
102 | { | |
b4e97a61 | 103 | struct ath79_gpio_ctrl *ctrl = gpiochip_get_data(chip); |
5b5b544e GJ |
104 | unsigned long flags; |
105 | ||
49a5bd88 | 106 | spin_lock_irqsave(&ctrl->lock, flags); |
5b5b544e GJ |
107 | |
108 | if (value) | |
49a5bd88 | 109 | __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_SET); |
5b5b544e | 110 | else |
49a5bd88 | 111 | __raw_writel(BIT(offset), ctrl->base + AR71XX_GPIO_REG_CLEAR); |
5b5b544e | 112 | |
49a5bd88 | 113 | __raw_writel( |
3a57e741 | 114 | __raw_readl(ctrl->base + AR71XX_GPIO_REG_OE) & ~BIT(offset), |
49a5bd88 | 115 | ctrl->base + AR71XX_GPIO_REG_OE); |
5b5b544e | 116 | |
49a5bd88 | 117 | spin_unlock_irqrestore(&ctrl->lock, flags); |
5b5b544e GJ |
118 | |
119 | return 0; | |
120 | } | |
121 | ||
49a5bd88 | 122 | static const struct gpio_chip ath79_gpio_chip = { |
6eae43c5 GJ |
123 | .label = "ath79", |
124 | .get = ath79_gpio_get_value, | |
125 | .set = ath79_gpio_set_value, | |
126 | .direction_input = ath79_gpio_direction_input, | |
127 | .direction_output = ath79_gpio_direction_output, | |
128 | .base = 0, | |
129 | }; | |
130 | ||
2ddf3a79 AB |
131 | static const struct of_device_id ath79_gpio_of_match[] = { |
132 | { .compatible = "qca,ar7100-gpio" }, | |
133 | { .compatible = "qca,ar9340-gpio" }, | |
134 | {}, | |
135 | }; | |
136 | ||
137 | static int ath79_gpio_probe(struct platform_device *pdev) | |
6eae43c5 | 138 | { |
ab128afc | 139 | struct ath79_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); |
2ddf3a79 | 140 | struct device_node *np = pdev->dev.of_node; |
49a5bd88 | 141 | struct ath79_gpio_ctrl *ctrl; |
2ddf3a79 | 142 | struct resource *res; |
49a5bd88 | 143 | u32 ath79_gpio_count; |
2ddf3a79 | 144 | bool oe_inverted; |
6eae43c5 GJ |
145 | int err; |
146 | ||
49a5bd88 AB |
147 | ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); |
148 | if (!ctrl) | |
149 | return -ENOMEM; | |
150 | ||
2ddf3a79 AB |
151 | if (np) { |
152 | err = of_property_read_u32(np, "ngpios", &ath79_gpio_count); | |
153 | if (err) { | |
154 | dev_err(&pdev->dev, "ngpios property is not valid\n"); | |
155 | return err; | |
156 | } | |
157 | if (ath79_gpio_count >= 32) { | |
158 | dev_err(&pdev->dev, "ngpios must be less than 32\n"); | |
159 | return -EINVAL; | |
160 | } | |
161 | oe_inverted = of_device_is_compatible(np, "qca,ar9340-gpio"); | |
162 | } else if (pdata) { | |
163 | ath79_gpio_count = pdata->ngpios; | |
164 | oe_inverted = pdata->oe_inverted; | |
165 | } else { | |
166 | dev_err(&pdev->dev, "No DT node or platform data found\n"); | |
167 | return -EINVAL; | |
168 | } | |
169 | ||
170 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
49a5bd88 | 171 | ctrl->base = devm_ioremap_nocache( |
2ddf3a79 | 172 | &pdev->dev, res->start, resource_size(res)); |
49a5bd88 | 173 | if (!ctrl->base) |
2ddf3a79 | 174 | return -ENOMEM; |
6eae43c5 | 175 | |
49a5bd88 AB |
176 | spin_lock_init(&ctrl->lock); |
177 | memcpy(&ctrl->chip, &ath79_gpio_chip, sizeof(ctrl->chip)); | |
58383c78 | 178 | ctrl->chip.parent = &pdev->dev; |
49a5bd88 | 179 | ctrl->chip.ngpio = ath79_gpio_count; |
2ddf3a79 | 180 | if (oe_inverted) { |
49a5bd88 AB |
181 | ctrl->chip.direction_input = ar934x_gpio_direction_input; |
182 | ctrl->chip.direction_output = ar934x_gpio_direction_output; | |
5b5b544e | 183 | } |
6eae43c5 | 184 | |
b4e97a61 | 185 | err = gpiochip_add_data(&ctrl->chip, ctrl); |
2ddf3a79 AB |
186 | if (err) { |
187 | dev_err(&pdev->dev, | |
188 | "cannot add AR71xx GPIO chip, error=%d", err); | |
189 | return err; | |
190 | } | |
191 | ||
192 | return 0; | |
6eae43c5 GJ |
193 | } |
194 | ||
2ddf3a79 AB |
195 | static struct platform_driver ath79_gpio_driver = { |
196 | .driver = { | |
197 | .name = "ath79-gpio", | |
198 | .of_match_table = ath79_gpio_of_match, | |
199 | }, | |
200 | .probe = ath79_gpio_probe, | |
201 | }; | |
202 | ||
203 | module_platform_driver(ath79_gpio_driver); |