18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci bt8xx GPIO abuser 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Copyright (C) 2008 Michael Buesch <m@bues.ch> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci Please do _only_ contact the people listed _above_ with issues related to this driver. 98c2ecf20Sopenharmony_ci All the other people listed below are not related to this driver. Their names 108c2ecf20Sopenharmony_ci are only here, because this driver is derived from the bt848 driver. 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci Derived from the bt848 driver: 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci Copyright (C) 1996,97,98 Ralph Metzler 168c2ecf20Sopenharmony_ci & Marcus Metzler 178c2ecf20Sopenharmony_ci (c) 1999-2002 Gerd Knorr 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci some v4l2 code lines are taken from Justin's bttv2 driver which is 208c2ecf20Sopenharmony_ci (c) 2000 Justin Schoeman 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci V4L1 removal from: 238c2ecf20Sopenharmony_ci (c) 2005-2006 Nickolay V. Shmyrev 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci Fixes to be fully V4L2 compliant by 268c2ecf20Sopenharmony_ci (c) 2006 Mauro Carvalho Chehab 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci Cropping and overscan support 298c2ecf20Sopenharmony_ci Copyright (C) 2005, 2006 Michael H. Schimek 308c2ecf20Sopenharmony_ci Sponsored by OPQ Systems AB 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci*/ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/pci.h> 368c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 378c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 388c2ecf20Sopenharmony_ci#include <linux/slab.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Steal the hardware definitions from the bttv driver. */ 418c2ecf20Sopenharmony_ci#include "../media/pci/bt8xx/bt848.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct bt8xxgpio { 488c2ecf20Sopenharmony_ci spinlock_t lock; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci void __iomem *mmio; 518c2ecf20Sopenharmony_ci struct pci_dev *pdev; 528c2ecf20Sopenharmony_ci struct gpio_chip gpio; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 558c2ecf20Sopenharmony_ci u32 saved_outen; 568c2ecf20Sopenharmony_ci u32 saved_data; 578c2ecf20Sopenharmony_ci#endif 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) 618c2ecf20Sopenharmony_ci#define bgread(adr) readl(bg->mmio+(adr)) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int modparam_gpiobase = -1/* dynamic */; 658c2ecf20Sopenharmony_cimodule_param_named(gpiobase, modparam_gpiobase, int, 0444); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 728c2ecf20Sopenharmony_ci unsigned long flags; 738c2ecf20Sopenharmony_ci u32 outen, data; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci data = bgread(BT848_GPIO_DATA); 788c2ecf20Sopenharmony_ci data &= ~(1 << nr); 798c2ecf20Sopenharmony_ci bgwrite(data, BT848_GPIO_DATA); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci outen = bgread(BT848_GPIO_OUT_EN); 828c2ecf20Sopenharmony_ci outen &= ~(1 << nr); 838c2ecf20Sopenharmony_ci bgwrite(outen, BT848_GPIO_OUT_EN); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci u32 val; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 978c2ecf20Sopenharmony_ci val = bgread(BT848_GPIO_DATA); 988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return !!(val & (1 << nr)); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, 1048c2ecf20Sopenharmony_ci unsigned nr, int val) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 1078c2ecf20Sopenharmony_ci unsigned long flags; 1088c2ecf20Sopenharmony_ci u32 outen, data; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci outen = bgread(BT848_GPIO_OUT_EN); 1138c2ecf20Sopenharmony_ci outen |= (1 << nr); 1148c2ecf20Sopenharmony_ci bgwrite(outen, BT848_GPIO_OUT_EN); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci data = bgread(BT848_GPIO_DATA); 1178c2ecf20Sopenharmony_ci if (val) 1188c2ecf20Sopenharmony_ci data |= (1 << nr); 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci data &= ~(1 << nr); 1218c2ecf20Sopenharmony_ci bgwrite(data, BT848_GPIO_DATA); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void bt8xxgpio_gpio_set(struct gpio_chip *gpio, 1298c2ecf20Sopenharmony_ci unsigned nr, int val) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 1328c2ecf20Sopenharmony_ci unsigned long flags; 1338c2ecf20Sopenharmony_ci u32 data; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci data = bgread(BT848_GPIO_DATA); 1388c2ecf20Sopenharmony_ci if (val) 1398c2ecf20Sopenharmony_ci data |= (1 << nr); 1408c2ecf20Sopenharmony_ci else 1418c2ecf20Sopenharmony_ci data &= ~(1 << nr); 1428c2ecf20Sopenharmony_ci bgwrite(data, BT848_GPIO_DATA); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct gpio_chip *c = &bg->gpio; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci c->label = dev_name(&bg->pdev->dev); 1528c2ecf20Sopenharmony_ci c->owner = THIS_MODULE; 1538c2ecf20Sopenharmony_ci c->direction_input = bt8xxgpio_gpio_direction_input; 1548c2ecf20Sopenharmony_ci c->get = bt8xxgpio_gpio_get; 1558c2ecf20Sopenharmony_ci c->direction_output = bt8xxgpio_gpio_direction_output; 1568c2ecf20Sopenharmony_ci c->set = bt8xxgpio_gpio_set; 1578c2ecf20Sopenharmony_ci c->dbg_show = NULL; 1588c2ecf20Sopenharmony_ci c->base = modparam_gpiobase; 1598c2ecf20Sopenharmony_ci c->ngpio = BT8XXGPIO_NR_GPIOS; 1608c2ecf20Sopenharmony_ci c->can_sleep = false; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int bt8xxgpio_probe(struct pci_dev *dev, 1648c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct bt8xxgpio *bg; 1678c2ecf20Sopenharmony_ci int err; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL); 1708c2ecf20Sopenharmony_ci if (!bg) 1718c2ecf20Sopenharmony_ci return -ENOMEM; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci bg->pdev = dev; 1748c2ecf20Sopenharmony_ci spin_lock_init(&bg->lock); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci err = pci_enable_device(dev); 1778c2ecf20Sopenharmony_ci if (err) { 1788c2ecf20Sopenharmony_ci printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0), 1828c2ecf20Sopenharmony_ci pci_resource_len(dev, 0), 1838c2ecf20Sopenharmony_ci "bt8xxgpio")) { 1848c2ecf20Sopenharmony_ci printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", 1858c2ecf20Sopenharmony_ci (unsigned long long)pci_resource_start(dev, 0)); 1868c2ecf20Sopenharmony_ci err = -EBUSY; 1878c2ecf20Sopenharmony_ci goto err_disable; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci pci_set_master(dev); 1908c2ecf20Sopenharmony_ci pci_set_drvdata(dev, bg); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000); 1938c2ecf20Sopenharmony_ci if (!bg->mmio) { 1948c2ecf20Sopenharmony_ci printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); 1958c2ecf20Sopenharmony_ci err = -EIO; 1968c2ecf20Sopenharmony_ci goto err_disable; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* Disable interrupts */ 2008c2ecf20Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* gpio init */ 2038c2ecf20Sopenharmony_ci bgwrite(0, BT848_GPIO_DMA_CTL); 2048c2ecf20Sopenharmony_ci bgwrite(0, BT848_GPIO_REG_INP); 2058c2ecf20Sopenharmony_ci bgwrite(0, BT848_GPIO_OUT_EN); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci bt8xxgpio_gpio_setup(bg); 2088c2ecf20Sopenharmony_ci err = gpiochip_add_data(&bg->gpio, bg); 2098c2ecf20Sopenharmony_ci if (err) { 2108c2ecf20Sopenharmony_ci printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); 2118c2ecf20Sopenharmony_ci goto err_disable; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cierr_disable: 2178c2ecf20Sopenharmony_ci pci_disable_device(dev); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return err; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void bt8xxgpio_remove(struct pci_dev *pdev) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = pci_get_drvdata(pdev); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci gpiochip_remove(&bg->gpio); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 2298c2ecf20Sopenharmony_ci bgwrite(~0x0, BT848_INT_STAT); 2308c2ecf20Sopenharmony_ci bgwrite(0x0, BT848_GPIO_OUT_EN); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci pci_disable_device(pdev); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2368c2ecf20Sopenharmony_cistatic int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = pci_get_drvdata(pdev); 2398c2ecf20Sopenharmony_ci unsigned long flags; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci bg->saved_outen = bgread(BT848_GPIO_OUT_EN); 2448c2ecf20Sopenharmony_ci bg->saved_data = bgread(BT848_GPIO_DATA); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 2478c2ecf20Sopenharmony_ci bgwrite(~0x0, BT848_INT_STAT); 2488c2ecf20Sopenharmony_ci bgwrite(0x0, BT848_GPIO_OUT_EN); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci pci_save_state(pdev); 2538c2ecf20Sopenharmony_ci pci_disable_device(pdev); 2548c2ecf20Sopenharmony_ci pci_set_power_state(pdev, pci_choose_state(pdev, state)); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int bt8xxgpio_resume(struct pci_dev *pdev) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct bt8xxgpio *bg = pci_get_drvdata(pdev); 2628c2ecf20Sopenharmony_ci unsigned long flags; 2638c2ecf20Sopenharmony_ci int err; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 2668c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 2678c2ecf20Sopenharmony_ci if (err) 2688c2ecf20Sopenharmony_ci return err; 2698c2ecf20Sopenharmony_ci pci_restore_state(pdev); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 2748c2ecf20Sopenharmony_ci bgwrite(0, BT848_GPIO_DMA_CTL); 2758c2ecf20Sopenharmony_ci bgwrite(0, BT848_GPIO_REG_INP); 2768c2ecf20Sopenharmony_ci bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); 2778c2ecf20Sopenharmony_ci bgwrite(bg->saved_data & bg->saved_outen, 2788c2ecf20Sopenharmony_ci BT848_GPIO_DATA); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci#else 2858c2ecf20Sopenharmony_ci#define bt8xxgpio_suspend NULL 2868c2ecf20Sopenharmony_ci#define bt8xxgpio_resume NULL 2878c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const struct pci_device_id bt8xxgpio_pci_tbl[] = { 2908c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, 2918c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, 2928c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, 2938c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, 2948c2ecf20Sopenharmony_ci { 0, }, 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic struct pci_driver bt8xxgpio_pci_driver = { 2998c2ecf20Sopenharmony_ci .name = "bt8xxgpio", 3008c2ecf20Sopenharmony_ci .id_table = bt8xxgpio_pci_tbl, 3018c2ecf20Sopenharmony_ci .probe = bt8xxgpio_probe, 3028c2ecf20Sopenharmony_ci .remove = bt8xxgpio_remove, 3038c2ecf20Sopenharmony_ci .suspend = bt8xxgpio_suspend, 3048c2ecf20Sopenharmony_ci .resume = bt8xxgpio_resume, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cimodule_pci_driver(bt8xxgpio_pci_driver); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Buesch"); 3118c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); 312