Commit | Line | Data |
---|---|---|
74ba9207 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ff1d5c2f MB |
2 | /* |
3 | ||
4 | bt8xx GPIO abuser | |
5 | ||
eb032b98 | 6 | Copyright (C) 2008 Michael Buesch <m@bues.ch> |
ff1d5c2f MB |
7 | |
8 | Please do _only_ contact the people listed _above_ with issues related to this driver. | |
9 | All the other people listed below are not related to this driver. Their names | |
10 | are only here, because this driver is derived from the bt848 driver. | |
11 | ||
12 | ||
13 | Derived from the bt848 driver: | |
14 | ||
15 | Copyright (C) 1996,97,98 Ralph Metzler | |
16 | & Marcus Metzler | |
17 | (c) 1999-2002 Gerd Knorr | |
18 | ||
19 | some v4l2 code lines are taken from Justin's bttv2 driver which is | |
20 | (c) 2000 Justin Schoeman | |
21 | ||
22 | V4L1 removal from: | |
23 | (c) 2005-2006 Nickolay V. Shmyrev | |
24 | ||
25 | Fixes to be fully V4L2 compliant by | |
26 | (c) 2006 Mauro Carvalho Chehab | |
27 | ||
28 | Cropping and overscan support | |
29 | Copyright (C) 2005, 2006 Michael H. Schimek | |
30 | Sponsored by OPQ Systems AB | |
31 | ||
ff1d5c2f MB |
32 | */ |
33 | ||
34 | #include <linux/module.h> | |
35 | #include <linux/pci.h> | |
36 | #include <linux/spinlock.h> | |
e88a2ae2 | 37 | #include <linux/gpio/driver.h> |
5a0e3ad6 | 38 | #include <linux/slab.h> |
ff1d5c2f MB |
39 | |
40 | /* Steal the hardware definitions from the bttv driver. */ | |
52d32258 | 41 | #include "../media/pci/bt8xx/bt848.h" |
ff1d5c2f MB |
42 | |
43 | ||
44 | #define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ | |
45 | ||
46 | ||
47 | struct bt8xxgpio { | |
48 | spinlock_t lock; | |
49 | ||
50 | void __iomem *mmio; | |
51 | struct pci_dev *pdev; | |
52 | struct gpio_chip gpio; | |
53 | ||
54 | #ifdef CONFIG_PM | |
55 | u32 saved_outen; | |
56 | u32 saved_data; | |
57 | #endif | |
58 | }; | |
59 | ||
60 | #define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) | |
61 | #define bgread(adr) readl(bg->mmio+(adr)) | |
62 | ||
63 | ||
64 | static int modparam_gpiobase = -1/* dynamic */; | |
65 | module_param_named(gpiobase, modparam_gpiobase, int, 0444); | |
66 | MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); | |
67 | ||
68 | ||
69 | static int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) | |
70 | { | |
e51b5523 | 71 | struct bt8xxgpio *bg = gpiochip_get_data(gpio); |
ff1d5c2f MB |
72 | unsigned long flags; |
73 | u32 outen, data; | |
74 | ||
75 | spin_lock_irqsave(&bg->lock, flags); | |
76 | ||
77 | data = bgread(BT848_GPIO_DATA); | |
78 | data &= ~(1 << nr); | |
79 | bgwrite(data, BT848_GPIO_DATA); | |
80 | ||
81 | outen = bgread(BT848_GPIO_OUT_EN); | |
82 | outen &= ~(1 << nr); | |
83 | bgwrite(outen, BT848_GPIO_OUT_EN); | |
84 | ||
85 | spin_unlock_irqrestore(&bg->lock, flags); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | static int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) | |
91 | { | |
e51b5523 | 92 | struct bt8xxgpio *bg = gpiochip_get_data(gpio); |
ff1d5c2f MB |
93 | unsigned long flags; |
94 | u32 val; | |
95 | ||
96 | spin_lock_irqsave(&bg->lock, flags); | |
97 | val = bgread(BT848_GPIO_DATA); | |
98 | spin_unlock_irqrestore(&bg->lock, flags); | |
99 | ||
100 | return !!(val & (1 << nr)); | |
101 | } | |
102 | ||
103 | static int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, | |
104 | unsigned nr, int val) | |
105 | { | |
e51b5523 | 106 | struct bt8xxgpio *bg = gpiochip_get_data(gpio); |
ff1d5c2f MB |
107 | unsigned long flags; |
108 | u32 outen, data; | |
109 | ||
110 | spin_lock_irqsave(&bg->lock, flags); | |
111 | ||
112 | outen = bgread(BT848_GPIO_OUT_EN); | |
113 | outen |= (1 << nr); | |
114 | bgwrite(outen, BT848_GPIO_OUT_EN); | |
115 | ||
116 | data = bgread(BT848_GPIO_DATA); | |
117 | if (val) | |
118 | data |= (1 << nr); | |
119 | else | |
120 | data &= ~(1 << nr); | |
121 | bgwrite(data, BT848_GPIO_DATA); | |
122 | ||
123 | spin_unlock_irqrestore(&bg->lock, flags); | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static void bt8xxgpio_gpio_set(struct gpio_chip *gpio, | |
129 | unsigned nr, int val) | |
130 | { | |
e51b5523 | 131 | struct bt8xxgpio *bg = gpiochip_get_data(gpio); |
ff1d5c2f MB |
132 | unsigned long flags; |
133 | u32 data; | |
134 | ||
135 | spin_lock_irqsave(&bg->lock, flags); | |
136 | ||
137 | data = bgread(BT848_GPIO_DATA); | |
138 | if (val) | |
139 | data |= (1 << nr); | |
140 | else | |
141 | data &= ~(1 << nr); | |
142 | bgwrite(data, BT848_GPIO_DATA); | |
143 | ||
144 | spin_unlock_irqrestore(&bg->lock, flags); | |
145 | } | |
146 | ||
147 | static void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) | |
148 | { | |
149 | struct gpio_chip *c = &bg->gpio; | |
150 | ||
3e274bd0 | 151 | c->label = dev_name(&bg->pdev->dev); |
ff1d5c2f MB |
152 | c->owner = THIS_MODULE; |
153 | c->direction_input = bt8xxgpio_gpio_direction_input; | |
154 | c->get = bt8xxgpio_gpio_get; | |
155 | c->direction_output = bt8xxgpio_gpio_direction_output; | |
156 | c->set = bt8xxgpio_gpio_set; | |
157 | c->dbg_show = NULL; | |
158 | c->base = modparam_gpiobase; | |
159 | c->ngpio = BT8XXGPIO_NR_GPIOS; | |
9fb1f39e | 160 | c->can_sleep = false; |
ff1d5c2f MB |
161 | } |
162 | ||
163 | static int bt8xxgpio_probe(struct pci_dev *dev, | |
164 | const struct pci_device_id *pci_id) | |
165 | { | |
166 | struct bt8xxgpio *bg; | |
167 | int err; | |
168 | ||
a435e180 | 169 | bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL); |
ff1d5c2f MB |
170 | if (!bg) |
171 | return -ENOMEM; | |
172 | ||
173 | bg->pdev = dev; | |
174 | spin_lock_init(&bg->lock); | |
175 | ||
176 | err = pci_enable_device(dev); | |
177 | if (err) { | |
3bf1d26c | 178 | dev_err(&dev->dev, "can't enable device.\n"); |
a435e180 | 179 | return err; |
ff1d5c2f | 180 | } |
a435e180 | 181 | if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0), |
ff1d5c2f MB |
182 | pci_resource_len(dev, 0), |
183 | "bt8xxgpio")) { | |
3bf1d26c | 184 | dev_warn(&dev->dev, "can't request iomem (0x%llx).\n", |
ff1d5c2f MB |
185 | (unsigned long long)pci_resource_start(dev, 0)); |
186 | err = -EBUSY; | |
187 | goto err_disable; | |
188 | } | |
189 | pci_set_master(dev); | |
190 | pci_set_drvdata(dev, bg); | |
191 | ||
a435e180 | 192 | bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000); |
ff1d5c2f | 193 | if (!bg->mmio) { |
3bf1d26c | 194 | dev_err(&dev->dev, "ioremap() failed\n"); |
ff1d5c2f | 195 | err = -EIO; |
a435e180 | 196 | goto err_disable; |
ff1d5c2f MB |
197 | } |
198 | ||
199 | /* Disable interrupts */ | |
200 | bgwrite(0, BT848_INT_MASK); | |
201 | ||
202 | /* gpio init */ | |
203 | bgwrite(0, BT848_GPIO_DMA_CTL); | |
204 | bgwrite(0, BT848_GPIO_REG_INP); | |
205 | bgwrite(0, BT848_GPIO_OUT_EN); | |
206 | ||
207 | bt8xxgpio_gpio_setup(bg); | |
e51b5523 | 208 | err = gpiochip_add_data(&bg->gpio, bg); |
ff1d5c2f | 209 | if (err) { |
3bf1d26c | 210 | dev_err(&dev->dev, "failed to register GPIOs\n"); |
a435e180 | 211 | goto err_disable; |
ff1d5c2f MB |
212 | } |
213 | ||
ff1d5c2f MB |
214 | return 0; |
215 | ||
ff1d5c2f MB |
216 | err_disable: |
217 | pci_disable_device(dev); | |
ff1d5c2f MB |
218 | |
219 | return err; | |
220 | } | |
221 | ||
222 | static void bt8xxgpio_remove(struct pci_dev *pdev) | |
223 | { | |
224 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | |
225 | ||
226 | gpiochip_remove(&bg->gpio); | |
227 | ||
228 | bgwrite(0, BT848_INT_MASK); | |
229 | bgwrite(~0x0, BT848_INT_STAT); | |
230 | bgwrite(0x0, BT848_GPIO_OUT_EN); | |
231 | ||
ff1d5c2f | 232 | pci_disable_device(pdev); |
ff1d5c2f MB |
233 | } |
234 | ||
235 | #ifdef CONFIG_PM | |
236 | static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) | |
237 | { | |
238 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | |
239 | unsigned long flags; | |
240 | ||
241 | spin_lock_irqsave(&bg->lock, flags); | |
242 | ||
243 | bg->saved_outen = bgread(BT848_GPIO_OUT_EN); | |
244 | bg->saved_data = bgread(BT848_GPIO_DATA); | |
245 | ||
246 | bgwrite(0, BT848_INT_MASK); | |
247 | bgwrite(~0x0, BT848_INT_STAT); | |
248 | bgwrite(0x0, BT848_GPIO_OUT_EN); | |
249 | ||
250 | spin_unlock_irqrestore(&bg->lock, flags); | |
251 | ||
252 | pci_save_state(pdev); | |
253 | pci_disable_device(pdev); | |
254 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | static int bt8xxgpio_resume(struct pci_dev *pdev) | |
260 | { | |
261 | struct bt8xxgpio *bg = pci_get_drvdata(pdev); | |
262 | unsigned long flags; | |
263 | int err; | |
264 | ||
038f0bab | 265 | pci_set_power_state(pdev, PCI_D0); |
ff1d5c2f MB |
266 | err = pci_enable_device(pdev); |
267 | if (err) | |
268 | return err; | |
269 | pci_restore_state(pdev); | |
270 | ||
271 | spin_lock_irqsave(&bg->lock, flags); | |
272 | ||
273 | bgwrite(0, BT848_INT_MASK); | |
274 | bgwrite(0, BT848_GPIO_DMA_CTL); | |
275 | bgwrite(0, BT848_GPIO_REG_INP); | |
276 | bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); | |
277 | bgwrite(bg->saved_data & bg->saved_outen, | |
278 | BT848_GPIO_DATA); | |
279 | ||
280 | spin_unlock_irqrestore(&bg->lock, flags); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | #else | |
285 | #define bt8xxgpio_suspend NULL | |
286 | #define bt8xxgpio_resume NULL | |
287 | #endif /* CONFIG_PM */ | |
288 | ||
14f4a883 | 289 | static const struct pci_device_id bt8xxgpio_pci_tbl[] = { |
ff1d5c2f MB |
290 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, |
291 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, | |
292 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, | |
293 | { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, | |
294 | { 0, }, | |
295 | }; | |
296 | MODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); | |
297 | ||
298 | static struct pci_driver bt8xxgpio_pci_driver = { | |
299 | .name = "bt8xxgpio", | |
300 | .id_table = bt8xxgpio_pci_tbl, | |
301 | .probe = bt8xxgpio_probe, | |
302 | .remove = bt8xxgpio_remove, | |
303 | .suspend = bt8xxgpio_suspend, | |
304 | .resume = bt8xxgpio_resume, | |
305 | }; | |
306 | ||
93baa65f | 307 | module_pci_driver(bt8xxgpio_pci_driver); |
ff1d5c2f MB |
308 | |
309 | MODULE_LICENSE("GPL"); | |
310 | MODULE_AUTHOR("Michael Buesch"); | |
311 | MODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); |