1/* 2 * RDC321x GPIO driver 3 * 4 * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> 5 * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 * 21 */ 22#include <linux/module.h> 23#include <linux/kernel.h> 24#include <linux/init.h> 25#include <linux/spinlock.h> 26#include <linux/platform_device.h> 27#include <linux/pci.h> 28#include <linux/gpio.h> 29#include <linux/mfd/rdc321x.h> 30#include <linux/slab.h> 31 32struct rdc321x_gpio { 33 spinlock_t lock; 34 struct pci_dev *sb_pdev; 35 u32 data_reg[2]; 36 int reg1_ctrl_base; 37 int reg1_data_base; 38 int reg2_ctrl_base; 39 int reg2_data_base; 40 struct gpio_chip chip; 41}; 42 43/* read GPIO pin */ 44static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) 45{ 46 struct rdc321x_gpio *gpch; 47 u32 value = 0; 48 int reg; 49 50 gpch = container_of(chip, struct rdc321x_gpio, chip); 51 reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; 52 53 spin_lock(&gpch->lock); 54 pci_write_config_dword(gpch->sb_pdev, reg, 55 gpch->data_reg[gpio < 32 ? 0 : 1]); 56 pci_read_config_dword(gpch->sb_pdev, reg, &value); 57 spin_unlock(&gpch->lock); 58 59 return (1 << (gpio & 0x1f)) & value ? 1 : 0; 60} 61 62static void rdc_gpio_set_value_impl(struct gpio_chip *chip, 63 unsigned gpio, int value) 64{ 65 struct rdc321x_gpio *gpch; 66 int reg = (gpio < 32) ? 0 : 1; 67 68 gpch = container_of(chip, struct rdc321x_gpio, chip); 69 70 if (value) 71 gpch->data_reg[reg] |= 1 << (gpio & 0x1f); 72 else 73 gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); 74 75 pci_write_config_dword(gpch->sb_pdev, 76 reg ? gpch->reg2_data_base : gpch->reg1_data_base, 77 gpch->data_reg[reg]); 78} 79 80/* set GPIO pin to value */ 81static void rdc_gpio_set_value(struct gpio_chip *chip, 82 unsigned gpio, int value) 83{ 84 struct rdc321x_gpio *gpch; 85 86 gpch = container_of(chip, struct rdc321x_gpio, chip); 87 spin_lock(&gpch->lock); 88 rdc_gpio_set_value_impl(chip, gpio, value); 89 spin_unlock(&gpch->lock); 90} 91 92static int rdc_gpio_config(struct gpio_chip *chip, 93 unsigned gpio, int value) 94{ 95 struct rdc321x_gpio *gpch; 96 int err; 97 u32 reg; 98 99 gpch = container_of(chip, struct rdc321x_gpio, chip); 100 101 spin_lock(&gpch->lock); 102 err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? 103 gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); 104 if (err) 105 goto unlock; 106 107 reg |= 1 << (gpio & 0x1f); 108 109 err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? 110 gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); 111 if (err) 112 goto unlock; 113 114 rdc_gpio_set_value_impl(chip, gpio, value); 115 116unlock: 117 spin_unlock(&gpch->lock); 118 119 return err; 120} 121 122/* configure GPIO pin as input */ 123static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) 124{ 125 return rdc_gpio_config(chip, gpio, 1); 126} 127 128/* 129 * Cache the initial value of both GPIO data registers 130 */ 131static int rdc321x_gpio_probe(struct platform_device *pdev) 132{ 133 int err; 134 struct resource *r; 135 struct rdc321x_gpio *rdc321x_gpio_dev; 136 struct rdc321x_gpio_pdata *pdata; 137 138 pdata = dev_get_platdata(&pdev->dev); 139 if (!pdata) { 140 dev_err(&pdev->dev, "no platform data supplied\n"); 141 return -ENODEV; 142 } 143 144 rdc321x_gpio_dev = devm_kzalloc(&pdev->dev, sizeof(struct rdc321x_gpio), 145 GFP_KERNEL); 146 if (!rdc321x_gpio_dev) 147 return -ENOMEM; 148 149 r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1"); 150 if (!r) { 151 dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); 152 return -ENODEV; 153 } 154 155 spin_lock_init(&rdc321x_gpio_dev->lock); 156 rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; 157 rdc321x_gpio_dev->reg1_ctrl_base = r->start; 158 rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; 159 160 r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2"); 161 if (!r) { 162 dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); 163 return -ENODEV; 164 } 165 166 rdc321x_gpio_dev->reg2_ctrl_base = r->start; 167 rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; 168 169 rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; 170 rdc321x_gpio_dev->chip.owner = THIS_MODULE; 171 rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; 172 rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; 173 rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; 174 rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; 175 rdc321x_gpio_dev->chip.base = 0; 176 rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; 177 178 platform_set_drvdata(pdev, rdc321x_gpio_dev); 179 180 /* This might not be, what others (BIOS, bootloader, etc.) 181 wrote to these registers before, but it's a good guess. Still 182 better than just using 0xffffffff. */ 183 err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, 184 rdc321x_gpio_dev->reg1_data_base, 185 &rdc321x_gpio_dev->data_reg[0]); 186 if (err) 187 return err; 188 189 err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, 190 rdc321x_gpio_dev->reg2_data_base, 191 &rdc321x_gpio_dev->data_reg[1]); 192 if (err) 193 return err; 194 195 dev_info(&pdev->dev, "registering %d GPIOs\n", 196 rdc321x_gpio_dev->chip.ngpio); 197 return gpiochip_add(&rdc321x_gpio_dev->chip); 198} 199 200static int rdc321x_gpio_remove(struct platform_device *pdev) 201{ 202 struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev); 203 204 gpiochip_remove(&rdc321x_gpio_dev->chip); 205 206 return 0; 207} 208 209static struct platform_driver rdc321x_gpio_driver = { 210 .driver.name = "rdc321x-gpio", 211 .driver.owner = THIS_MODULE, 212 .probe = rdc321x_gpio_probe, 213 .remove = rdc321x_gpio_remove, 214}; 215 216module_platform_driver(rdc321x_gpio_driver); 217 218MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 219MODULE_DESCRIPTION("RDC321x GPIO driver"); 220MODULE_LICENSE("GPL"); 221MODULE_ALIAS("platform:rdc321x-gpio"); 222