18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * RDC321x GPIO driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> 68c2ecf20Sopenharmony_ci * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/rdc321x.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct rdc321x_gpio { 198c2ecf20Sopenharmony_ci spinlock_t lock; 208c2ecf20Sopenharmony_ci struct pci_dev *sb_pdev; 218c2ecf20Sopenharmony_ci u32 data_reg[2]; 228c2ecf20Sopenharmony_ci int reg1_ctrl_base; 238c2ecf20Sopenharmony_ci int reg1_data_base; 248c2ecf20Sopenharmony_ci int reg2_ctrl_base; 258c2ecf20Sopenharmony_ci int reg2_data_base; 268c2ecf20Sopenharmony_ci struct gpio_chip chip; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* read GPIO pin */ 308c2ecf20Sopenharmony_cistatic int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct rdc321x_gpio *gpch; 338c2ecf20Sopenharmony_ci u32 value = 0; 348c2ecf20Sopenharmony_ci int reg; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci gpch = gpiochip_get_data(chip); 378c2ecf20Sopenharmony_ci reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci spin_lock(&gpch->lock); 408c2ecf20Sopenharmony_ci pci_write_config_dword(gpch->sb_pdev, reg, 418c2ecf20Sopenharmony_ci gpch->data_reg[gpio < 32 ? 0 : 1]); 428c2ecf20Sopenharmony_ci pci_read_config_dword(gpch->sb_pdev, reg, &value); 438c2ecf20Sopenharmony_ci spin_unlock(&gpch->lock); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return (1 << (gpio & 0x1f)) & value ? 1 : 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void rdc_gpio_set_value_impl(struct gpio_chip *chip, 498c2ecf20Sopenharmony_ci unsigned gpio, int value) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct rdc321x_gpio *gpch; 528c2ecf20Sopenharmony_ci int reg = (gpio < 32) ? 0 : 1; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci gpch = gpiochip_get_data(chip); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (value) 578c2ecf20Sopenharmony_ci gpch->data_reg[reg] |= 1 << (gpio & 0x1f); 588c2ecf20Sopenharmony_ci else 598c2ecf20Sopenharmony_ci gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci pci_write_config_dword(gpch->sb_pdev, 628c2ecf20Sopenharmony_ci reg ? gpch->reg2_data_base : gpch->reg1_data_base, 638c2ecf20Sopenharmony_ci gpch->data_reg[reg]); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* set GPIO pin to value */ 678c2ecf20Sopenharmony_cistatic void rdc_gpio_set_value(struct gpio_chip *chip, 688c2ecf20Sopenharmony_ci unsigned gpio, int value) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct rdc321x_gpio *gpch; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci gpch = gpiochip_get_data(chip); 738c2ecf20Sopenharmony_ci spin_lock(&gpch->lock); 748c2ecf20Sopenharmony_ci rdc_gpio_set_value_impl(chip, gpio, value); 758c2ecf20Sopenharmony_ci spin_unlock(&gpch->lock); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int rdc_gpio_config(struct gpio_chip *chip, 798c2ecf20Sopenharmony_ci unsigned gpio, int value) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct rdc321x_gpio *gpch; 828c2ecf20Sopenharmony_ci int err; 838c2ecf20Sopenharmony_ci u32 reg; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci gpch = gpiochip_get_data(chip); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci spin_lock(&gpch->lock); 888c2ecf20Sopenharmony_ci err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? 898c2ecf20Sopenharmony_ci gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); 908c2ecf20Sopenharmony_ci if (err) 918c2ecf20Sopenharmony_ci goto unlock; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci reg |= 1 << (gpio & 0x1f); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? 968c2ecf20Sopenharmony_ci gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); 978c2ecf20Sopenharmony_ci if (err) 988c2ecf20Sopenharmony_ci goto unlock; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rdc_gpio_set_value_impl(chip, gpio, value); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ciunlock: 1038c2ecf20Sopenharmony_ci spin_unlock(&gpch->lock); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return err; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* configure GPIO pin as input */ 1098c2ecf20Sopenharmony_cistatic int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci return rdc_gpio_config(chip, gpio, 1); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * Cache the initial value of both GPIO data registers 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic int rdc321x_gpio_probe(struct platform_device *pdev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int err; 1208c2ecf20Sopenharmony_ci struct resource *r; 1218c2ecf20Sopenharmony_ci struct rdc321x_gpio *rdc321x_gpio_dev; 1228c2ecf20Sopenharmony_ci struct rdc321x_gpio_pdata *pdata; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 1258c2ecf20Sopenharmony_ci if (!pdata) { 1268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform data supplied\n"); 1278c2ecf20Sopenharmony_ci return -ENODEV; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci rdc321x_gpio_dev = devm_kzalloc(&pdev->dev, sizeof(struct rdc321x_gpio), 1318c2ecf20Sopenharmony_ci GFP_KERNEL); 1328c2ecf20Sopenharmony_ci if (!rdc321x_gpio_dev) 1338c2ecf20Sopenharmony_ci return -ENOMEM; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1"); 1368c2ecf20Sopenharmony_ci if (!r) { 1378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); 1388c2ecf20Sopenharmony_ci return -ENODEV; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock_init(&rdc321x_gpio_dev->lock); 1428c2ecf20Sopenharmony_ci rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; 1438c2ecf20Sopenharmony_ci rdc321x_gpio_dev->reg1_ctrl_base = r->start; 1448c2ecf20Sopenharmony_ci rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2"); 1478c2ecf20Sopenharmony_ci if (!r) { 1488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); 1498c2ecf20Sopenharmony_ci return -ENODEV; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci rdc321x_gpio_dev->reg2_ctrl_base = r->start; 1538c2ecf20Sopenharmony_ci rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; 1568c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.owner = THIS_MODULE; 1578c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; 1588c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; 1598c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; 1608c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; 1618c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.base = 0; 1628c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rdc321x_gpio_dev); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* This might not be, what others (BIOS, bootloader, etc.) 1678c2ecf20Sopenharmony_ci wrote to these registers before, but it's a good guess. Still 1688c2ecf20Sopenharmony_ci better than just using 0xffffffff. */ 1698c2ecf20Sopenharmony_ci err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, 1708c2ecf20Sopenharmony_ci rdc321x_gpio_dev->reg1_data_base, 1718c2ecf20Sopenharmony_ci &rdc321x_gpio_dev->data_reg[0]); 1728c2ecf20Sopenharmony_ci if (err) 1738c2ecf20Sopenharmony_ci return err; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, 1768c2ecf20Sopenharmony_ci rdc321x_gpio_dev->reg2_data_base, 1778c2ecf20Sopenharmony_ci &rdc321x_gpio_dev->data_reg[1]); 1788c2ecf20Sopenharmony_ci if (err) 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "registering %d GPIOs\n", 1828c2ecf20Sopenharmony_ci rdc321x_gpio_dev->chip.ngpio); 1838c2ecf20Sopenharmony_ci return devm_gpiochip_add_data(&pdev->dev, &rdc321x_gpio_dev->chip, 1848c2ecf20Sopenharmony_ci rdc321x_gpio_dev); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic struct platform_driver rdc321x_gpio_driver = { 1888c2ecf20Sopenharmony_ci .driver.name = "rdc321x-gpio", 1898c2ecf20Sopenharmony_ci .probe = rdc321x_gpio_probe, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cimodule_platform_driver(rdc321x_gpio_driver); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 1958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RDC321x GPIO driver"); 1968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1978c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:rdc321x-gpio"); 198