162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci bt8xx GPIO abuser 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Copyright (C) 2008 Michael Buesch <m@bues.ch> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci Please do _only_ contact the people listed _above_ with issues related to this driver. 962306a36Sopenharmony_ci All the other people listed below are not related to this driver. Their names 1062306a36Sopenharmony_ci are only here, because this driver is derived from the bt848 driver. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci Derived from the bt848 driver: 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci Copyright (C) 1996,97,98 Ralph Metzler 1662306a36Sopenharmony_ci & Marcus Metzler 1762306a36Sopenharmony_ci (c) 1999-2002 Gerd Knorr 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci some v4l2 code lines are taken from Justin's bttv2 driver which is 2062306a36Sopenharmony_ci (c) 2000 Justin Schoeman 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci V4L1 removal from: 2362306a36Sopenharmony_ci (c) 2005-2006 Nickolay V. Shmyrev 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci Fixes to be fully V4L2 compliant by 2662306a36Sopenharmony_ci (c) 2006 Mauro Carvalho Chehab 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci Cropping and overscan support 2962306a36Sopenharmony_ci Copyright (C) 2005, 2006 Michael H. Schimek 3062306a36Sopenharmony_ci Sponsored by OPQ Systems AB 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci*/ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/module.h> 3562306a36Sopenharmony_ci#include <linux/pci.h> 3662306a36Sopenharmony_ci#include <linux/spinlock.h> 3762306a36Sopenharmony_ci#include <linux/gpio/driver.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Steal the hardware definitions from the bttv driver. */ 4162306a36Sopenharmony_ci#include "../media/pci/bt8xx/bt848.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct bt8xxgpio { 4862306a36Sopenharmony_ci spinlock_t lock; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci void __iomem *mmio; 5162306a36Sopenharmony_ci struct pci_dev *pdev; 5262306a36Sopenharmony_ci struct gpio_chip gpio; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#ifdef CONFIG_PM 5562306a36Sopenharmony_ci u32 saved_outen; 5662306a36Sopenharmony_ci u32 saved_data; 5762306a36Sopenharmony_ci#endif 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr)) 6162306a36Sopenharmony_ci#define bgread(adr) readl(bg->mmio+(adr)) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int modparam_gpiobase = -1/* dynamic */; 6562306a36Sopenharmony_cimodule_param_named(gpiobase, modparam_gpiobase, int, 0444); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, which is the default."); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int bt8xxgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 7262306a36Sopenharmony_ci unsigned long flags; 7362306a36Sopenharmony_ci u32 outen, data; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci data = bgread(BT848_GPIO_DATA); 7862306a36Sopenharmony_ci data &= ~(1 << nr); 7962306a36Sopenharmony_ci bgwrite(data, BT848_GPIO_DATA); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci outen = bgread(BT848_GPIO_OUT_EN); 8262306a36Sopenharmony_ci outen &= ~(1 << nr); 8362306a36Sopenharmony_ci bgwrite(outen, BT848_GPIO_OUT_EN); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int bt8xxgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 9362306a36Sopenharmony_ci unsigned long flags; 9462306a36Sopenharmony_ci u32 val; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 9762306a36Sopenharmony_ci val = bgread(BT848_GPIO_DATA); 9862306a36Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return !!(val & (1 << nr)); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int bt8xxgpio_gpio_direction_output(struct gpio_chip *gpio, 10462306a36Sopenharmony_ci unsigned nr, int val) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 10762306a36Sopenharmony_ci unsigned long flags; 10862306a36Sopenharmony_ci u32 outen, data; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci outen = bgread(BT848_GPIO_OUT_EN); 11362306a36Sopenharmony_ci outen |= (1 << nr); 11462306a36Sopenharmony_ci bgwrite(outen, BT848_GPIO_OUT_EN); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci data = bgread(BT848_GPIO_DATA); 11762306a36Sopenharmony_ci if (val) 11862306a36Sopenharmony_ci data |= (1 << nr); 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci data &= ~(1 << nr); 12162306a36Sopenharmony_ci bgwrite(data, BT848_GPIO_DATA); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void bt8xxgpio_gpio_set(struct gpio_chip *gpio, 12962306a36Sopenharmony_ci unsigned nr, int val) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct bt8xxgpio *bg = gpiochip_get_data(gpio); 13262306a36Sopenharmony_ci unsigned long flags; 13362306a36Sopenharmony_ci u32 data; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci data = bgread(BT848_GPIO_DATA); 13862306a36Sopenharmony_ci if (val) 13962306a36Sopenharmony_ci data |= (1 << nr); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci data &= ~(1 << nr); 14262306a36Sopenharmony_ci bgwrite(data, BT848_GPIO_DATA); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void bt8xxgpio_gpio_setup(struct bt8xxgpio *bg) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct gpio_chip *c = &bg->gpio; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci c->label = dev_name(&bg->pdev->dev); 15262306a36Sopenharmony_ci c->owner = THIS_MODULE; 15362306a36Sopenharmony_ci c->direction_input = bt8xxgpio_gpio_direction_input; 15462306a36Sopenharmony_ci c->get = bt8xxgpio_gpio_get; 15562306a36Sopenharmony_ci c->direction_output = bt8xxgpio_gpio_direction_output; 15662306a36Sopenharmony_ci c->set = bt8xxgpio_gpio_set; 15762306a36Sopenharmony_ci c->dbg_show = NULL; 15862306a36Sopenharmony_ci c->base = modparam_gpiobase; 15962306a36Sopenharmony_ci c->ngpio = BT8XXGPIO_NR_GPIOS; 16062306a36Sopenharmony_ci c->can_sleep = false; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int bt8xxgpio_probe(struct pci_dev *dev, 16462306a36Sopenharmony_ci const struct pci_device_id *pci_id) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct bt8xxgpio *bg; 16762306a36Sopenharmony_ci int err; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci bg = devm_kzalloc(&dev->dev, sizeof(struct bt8xxgpio), GFP_KERNEL); 17062306a36Sopenharmony_ci if (!bg) 17162306a36Sopenharmony_ci return -ENOMEM; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci bg->pdev = dev; 17462306a36Sopenharmony_ci spin_lock_init(&bg->lock); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci err = pci_enable_device(dev); 17762306a36Sopenharmony_ci if (err) { 17862306a36Sopenharmony_ci dev_err(&dev->dev, "can't enable device.\n"); 17962306a36Sopenharmony_ci return err; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0), 18262306a36Sopenharmony_ci pci_resource_len(dev, 0), 18362306a36Sopenharmony_ci "bt8xxgpio")) { 18462306a36Sopenharmony_ci dev_warn(&dev->dev, "can't request iomem (0x%llx).\n", 18562306a36Sopenharmony_ci (unsigned long long)pci_resource_start(dev, 0)); 18662306a36Sopenharmony_ci err = -EBUSY; 18762306a36Sopenharmony_ci goto err_disable; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci pci_set_master(dev); 19062306a36Sopenharmony_ci pci_set_drvdata(dev, bg); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000); 19362306a36Sopenharmony_ci if (!bg->mmio) { 19462306a36Sopenharmony_ci dev_err(&dev->dev, "ioremap() failed\n"); 19562306a36Sopenharmony_ci err = -EIO; 19662306a36Sopenharmony_ci goto err_disable; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Disable interrupts */ 20062306a36Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* gpio init */ 20362306a36Sopenharmony_ci bgwrite(0, BT848_GPIO_DMA_CTL); 20462306a36Sopenharmony_ci bgwrite(0, BT848_GPIO_REG_INP); 20562306a36Sopenharmony_ci bgwrite(0, BT848_GPIO_OUT_EN); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci bt8xxgpio_gpio_setup(bg); 20862306a36Sopenharmony_ci err = gpiochip_add_data(&bg->gpio, bg); 20962306a36Sopenharmony_ci if (err) { 21062306a36Sopenharmony_ci dev_err(&dev->dev, "failed to register GPIOs\n"); 21162306a36Sopenharmony_ci goto err_disable; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cierr_disable: 21762306a36Sopenharmony_ci pci_disable_device(dev); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return err; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void bt8xxgpio_remove(struct pci_dev *pdev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct bt8xxgpio *bg = pci_get_drvdata(pdev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci gpiochip_remove(&bg->gpio); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 22962306a36Sopenharmony_ci bgwrite(~0x0, BT848_INT_STAT); 23062306a36Sopenharmony_ci bgwrite(0x0, BT848_GPIO_OUT_EN); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci pci_disable_device(pdev); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#ifdef CONFIG_PM 23662306a36Sopenharmony_cistatic int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct bt8xxgpio *bg = pci_get_drvdata(pdev); 23962306a36Sopenharmony_ci unsigned long flags; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci bg->saved_outen = bgread(BT848_GPIO_OUT_EN); 24462306a36Sopenharmony_ci bg->saved_data = bgread(BT848_GPIO_DATA); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 24762306a36Sopenharmony_ci bgwrite(~0x0, BT848_INT_STAT); 24862306a36Sopenharmony_ci bgwrite(0x0, BT848_GPIO_OUT_EN); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci pci_save_state(pdev); 25362306a36Sopenharmony_ci pci_disable_device(pdev); 25462306a36Sopenharmony_ci pci_set_power_state(pdev, pci_choose_state(pdev, state)); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int bt8xxgpio_resume(struct pci_dev *pdev) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct bt8xxgpio *bg = pci_get_drvdata(pdev); 26262306a36Sopenharmony_ci unsigned long flags; 26362306a36Sopenharmony_ci int err; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 26662306a36Sopenharmony_ci err = pci_enable_device(pdev); 26762306a36Sopenharmony_ci if (err) 26862306a36Sopenharmony_ci return err; 26962306a36Sopenharmony_ci pci_restore_state(pdev); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock_irqsave(&bg->lock, flags); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci bgwrite(0, BT848_INT_MASK); 27462306a36Sopenharmony_ci bgwrite(0, BT848_GPIO_DMA_CTL); 27562306a36Sopenharmony_ci bgwrite(0, BT848_GPIO_REG_INP); 27662306a36Sopenharmony_ci bgwrite(bg->saved_outen, BT848_GPIO_OUT_EN); 27762306a36Sopenharmony_ci bgwrite(bg->saved_data & bg->saved_outen, 27862306a36Sopenharmony_ci BT848_GPIO_DATA); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci spin_unlock_irqrestore(&bg->lock, flags); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci#else 28562306a36Sopenharmony_ci#define bt8xxgpio_suspend NULL 28662306a36Sopenharmony_ci#define bt8xxgpio_resume NULL 28762306a36Sopenharmony_ci#endif /* CONFIG_PM */ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const struct pci_device_id bt8xxgpio_pci_tbl[] = { 29062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) }, 29162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849) }, 29262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878) }, 29362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879) }, 29462306a36Sopenharmony_ci { 0, }, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bt8xxgpio_pci_tbl); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct pci_driver bt8xxgpio_pci_driver = { 29962306a36Sopenharmony_ci .name = "bt8xxgpio", 30062306a36Sopenharmony_ci .id_table = bt8xxgpio_pci_tbl, 30162306a36Sopenharmony_ci .probe = bt8xxgpio_probe, 30262306a36Sopenharmony_ci .remove = bt8xxgpio_remove, 30362306a36Sopenharmony_ci .suspend = bt8xxgpio_suspend, 30462306a36Sopenharmony_ci .resume = bt8xxgpio_resume, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cimodule_pci_driver(bt8xxgpio_pci_driver); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 31062306a36Sopenharmony_ciMODULE_AUTHOR("Michael Buesch"); 31162306a36Sopenharmony_ciMODULE_DESCRIPTION("Abuse a BT8xx framegrabber card as generic GPIO card"); 312