1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * RDC321x GPIO driver |
4 | * |
5 | * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> |
6 | * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> |
7 | */ |
8 | #include <linux/module.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/spinlock.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pci.h> |
14 | #include <linux/gpio/driver.h> |
15 | #include <linux/mfd/rdc321x.h> |
16 | #include <linux/slab.h> |
17 | |
18 | struct rdc321x_gpio { |
19 | spinlock_t lock; |
20 | struct pci_dev *sb_pdev; |
21 | u32 data_reg[2]; |
22 | int reg1_ctrl_base; |
23 | int reg1_data_base; |
24 | int reg2_ctrl_base; |
25 | int reg2_data_base; |
26 | struct gpio_chip chip; |
27 | }; |
28 | |
29 | /* read GPIO pin */ |
30 | static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) |
31 | { |
32 | struct rdc321x_gpio *gpch; |
33 | u32 value = 0; |
34 | int reg; |
35 | |
36 | gpch = gpiochip_get_data(gc: chip); |
37 | reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; |
38 | |
39 | spin_lock(lock: &gpch->lock); |
40 | pci_write_config_dword(dev: gpch->sb_pdev, where: reg, |
41 | val: gpch->data_reg[gpio < 32 ? 0 : 1]); |
42 | pci_read_config_dword(dev: gpch->sb_pdev, where: reg, val: &value); |
43 | spin_unlock(lock: &gpch->lock); |
44 | |
45 | return (1 << (gpio & 0x1f)) & value ? 1 : 0; |
46 | } |
47 | |
48 | static void rdc_gpio_set_value_impl(struct gpio_chip *chip, |
49 | unsigned gpio, int value) |
50 | { |
51 | struct rdc321x_gpio *gpch; |
52 | int reg = (gpio < 32) ? 0 : 1; |
53 | |
54 | gpch = gpiochip_get_data(gc: chip); |
55 | |
56 | if (value) |
57 | gpch->data_reg[reg] |= 1 << (gpio & 0x1f); |
58 | else |
59 | gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); |
60 | |
61 | pci_write_config_dword(dev: gpch->sb_pdev, |
62 | where: reg ? gpch->reg2_data_base : gpch->reg1_data_base, |
63 | val: gpch->data_reg[reg]); |
64 | } |
65 | |
66 | /* set GPIO pin to value */ |
67 | static void rdc_gpio_set_value(struct gpio_chip *chip, |
68 | unsigned gpio, int value) |
69 | { |
70 | struct rdc321x_gpio *gpch; |
71 | |
72 | gpch = gpiochip_get_data(gc: chip); |
73 | spin_lock(lock: &gpch->lock); |
74 | rdc_gpio_set_value_impl(chip, gpio, value); |
75 | spin_unlock(lock: &gpch->lock); |
76 | } |
77 | |
78 | static int rdc_gpio_config(struct gpio_chip *chip, |
79 | unsigned gpio, int value) |
80 | { |
81 | struct rdc321x_gpio *gpch; |
82 | int err; |
83 | u32 reg; |
84 | |
85 | gpch = gpiochip_get_data(gc: chip); |
86 | |
87 | spin_lock(lock: &gpch->lock); |
88 | err = pci_read_config_dword(dev: gpch->sb_pdev, where: gpio < 32 ? |
89 | gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, val: ®); |
90 | if (err) |
91 | goto unlock; |
92 | |
93 | reg |= 1 << (gpio & 0x1f); |
94 | |
95 | err = pci_write_config_dword(dev: gpch->sb_pdev, where: gpio < 32 ? |
96 | gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, val: reg); |
97 | if (err) |
98 | goto unlock; |
99 | |
100 | rdc_gpio_set_value_impl(chip, gpio, value); |
101 | |
102 | unlock: |
103 | spin_unlock(lock: &gpch->lock); |
104 | |
105 | return err; |
106 | } |
107 | |
108 | /* configure GPIO pin as input */ |
109 | static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) |
110 | { |
111 | return rdc_gpio_config(chip, gpio, value: 1); |
112 | } |
113 | |
114 | /* |
115 | * Cache the initial value of both GPIO data registers |
116 | */ |
117 | static int rdc321x_gpio_probe(struct platform_device *pdev) |
118 | { |
119 | int err; |
120 | struct resource *r; |
121 | struct rdc321x_gpio *rdc321x_gpio_dev; |
122 | struct rdc321x_gpio_pdata *pdata; |
123 | |
124 | pdata = dev_get_platdata(dev: &pdev->dev); |
125 | if (!pdata) { |
126 | dev_err(&pdev->dev, "no platform data supplied\n" ); |
127 | return -ENODEV; |
128 | } |
129 | |
130 | rdc321x_gpio_dev = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct rdc321x_gpio), |
131 | GFP_KERNEL); |
132 | if (!rdc321x_gpio_dev) |
133 | return -ENOMEM; |
134 | |
135 | r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1" ); |
136 | if (!r) { |
137 | dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n" ); |
138 | return -ENODEV; |
139 | } |
140 | |
141 | spin_lock_init(&rdc321x_gpio_dev->lock); |
142 | rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; |
143 | rdc321x_gpio_dev->reg1_ctrl_base = r->start; |
144 | rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; |
145 | |
146 | r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2" ); |
147 | if (!r) { |
148 | dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n" ); |
149 | return -ENODEV; |
150 | } |
151 | |
152 | rdc321x_gpio_dev->reg2_ctrl_base = r->start; |
153 | rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; |
154 | |
155 | rdc321x_gpio_dev->chip.label = "rdc321x-gpio" ; |
156 | rdc321x_gpio_dev->chip.owner = THIS_MODULE; |
157 | rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; |
158 | rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; |
159 | rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; |
160 | rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; |
161 | rdc321x_gpio_dev->chip.base = 0; |
162 | rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; |
163 | |
164 | platform_set_drvdata(pdev, data: rdc321x_gpio_dev); |
165 | |
166 | /* This might not be, what others (BIOS, bootloader, etc.) |
167 | wrote to these registers before, but it's a good guess. Still |
168 | better than just using 0xffffffff. */ |
169 | err = pci_read_config_dword(dev: rdc321x_gpio_dev->sb_pdev, |
170 | where: rdc321x_gpio_dev->reg1_data_base, |
171 | val: &rdc321x_gpio_dev->data_reg[0]); |
172 | if (err) |
173 | return err; |
174 | |
175 | err = pci_read_config_dword(dev: rdc321x_gpio_dev->sb_pdev, |
176 | where: rdc321x_gpio_dev->reg2_data_base, |
177 | val: &rdc321x_gpio_dev->data_reg[1]); |
178 | if (err) |
179 | return err; |
180 | |
181 | dev_info(&pdev->dev, "registering %d GPIOs\n" , |
182 | rdc321x_gpio_dev->chip.ngpio); |
183 | return devm_gpiochip_add_data(&pdev->dev, &rdc321x_gpio_dev->chip, |
184 | rdc321x_gpio_dev); |
185 | } |
186 | |
187 | static struct platform_driver rdc321x_gpio_driver = { |
188 | .driver.name = "rdc321x-gpio" , |
189 | .probe = rdc321x_gpio_probe, |
190 | }; |
191 | |
192 | module_platform_driver(rdc321x_gpio_driver); |
193 | |
194 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>" ); |
195 | MODULE_DESCRIPTION("RDC321x GPIO driver" ); |
196 | MODULE_LICENSE("GPL" ); |
197 | MODULE_ALIAS("platform:rdc321x-gpio" ); |
198 | |