Blame view

drivers/gpio/gpio-rdc321x.c 5.65 KB
9956d02d6   Florian Fainelli   gpio: Add support...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  /*
   * RDC321x GPIO driver
   *
   * Copyright (C) 2008, Volker Weiss <dev@tintuc.de>
   * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   *
   */
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/spinlock.h>
  #include <linux/platform_device.h>
  #include <linux/pci.h>
  #include <linux/gpio.h>
  #include <linux/mfd/rdc321x.h>
374b72eca   Tejun Heo   gpio: Update gfp/...
30
  #include <linux/slab.h>
9956d02d6   Florian Fainelli   gpio: Add support...
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  
  struct rdc321x_gpio {
  	spinlock_t		lock;
  	struct pci_dev		*sb_pdev;
  	u32			data_reg[2];
  	int			reg1_ctrl_base;
  	int			reg1_data_base;
  	int			reg2_ctrl_base;
  	int			reg2_data_base;
  	struct gpio_chip	chip;
  };
  
  /* read GPIO pin */
  static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
  {
  	struct rdc321x_gpio *gpch;
  	u32 value = 0;
  	int reg;
78132252c   Linus Walleij   gpio: rdc321x: us...
49
  	gpch = gpiochip_get_data(chip);
9956d02d6   Florian Fainelli   gpio: Add support...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  	reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base;
  
  	spin_lock(&gpch->lock);
  	pci_write_config_dword(gpch->sb_pdev, reg,
  					gpch->data_reg[gpio < 32 ? 0 : 1]);
  	pci_read_config_dword(gpch->sb_pdev, reg, &value);
  	spin_unlock(&gpch->lock);
  
  	return (1 << (gpio & 0x1f)) & value ? 1 : 0;
  }
  
  static void rdc_gpio_set_value_impl(struct gpio_chip *chip,
  				unsigned gpio, int value)
  {
  	struct rdc321x_gpio *gpch;
  	int reg = (gpio < 32) ? 0 : 1;
78132252c   Linus Walleij   gpio: rdc321x: us...
66
  	gpch = gpiochip_get_data(chip);
9956d02d6   Florian Fainelli   gpio: Add support...
67
68
69
70
71
72
73
  
  	if (value)
  		gpch->data_reg[reg] |= 1 << (gpio & 0x1f);
  	else
  		gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f));
  
  	pci_write_config_dword(gpch->sb_pdev,
75907a115   Florian Fainelli   gpio: Fix inverte...
74
  			reg ? gpch->reg2_data_base : gpch->reg1_data_base,
9956d02d6   Florian Fainelli   gpio: Add support...
75
76
77
78
79
80
81
82
  			gpch->data_reg[reg]);
  }
  
  /* set GPIO pin to value */
  static void rdc_gpio_set_value(struct gpio_chip *chip,
  				unsigned gpio, int value)
  {
  	struct rdc321x_gpio *gpch;
78132252c   Linus Walleij   gpio: rdc321x: us...
83
  	gpch = gpiochip_get_data(chip);
9956d02d6   Florian Fainelli   gpio: Add support...
84
85
86
87
88
89
90
91
92
93
94
  	spin_lock(&gpch->lock);
  	rdc_gpio_set_value_impl(chip, gpio, value);
  	spin_unlock(&gpch->lock);
  }
  
  static int rdc_gpio_config(struct gpio_chip *chip,
  				unsigned gpio, int value)
  {
  	struct rdc321x_gpio *gpch;
  	int err;
  	u32 reg;
78132252c   Linus Walleij   gpio: rdc321x: us...
95
  	gpch = gpiochip_get_data(chip);
9956d02d6   Florian Fainelli   gpio: Add support...
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  
  	spin_lock(&gpch->lock);
  	err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ?
  			gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, &reg);
  	if (err)
  		goto unlock;
  
  	reg |= 1 << (gpio & 0x1f);
  
  	err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ?
  			gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg);
  	if (err)
  		goto unlock;
  
  	rdc_gpio_set_value_impl(chip, gpio, value);
  
  unlock:
  	spin_unlock(&gpch->lock);
  
  	return err;
  }
  
  /* configure GPIO pin as input */
  static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
  {
  	return rdc_gpio_config(chip, gpio, 1);
  }
  
  /*
   * Cache the initial value of both GPIO data registers
   */
3836309d9   Bill Pemberton   gpio: remove use ...
127
  static int rdc321x_gpio_probe(struct platform_device *pdev)
9956d02d6   Florian Fainelli   gpio: Add support...
128
129
130
131
132
  {
  	int err;
  	struct resource *r;
  	struct rdc321x_gpio *rdc321x_gpio_dev;
  	struct rdc321x_gpio_pdata *pdata;
e56aee189   Jingoo Han   gpio: use dev_get...
133
  	pdata = dev_get_platdata(&pdev->dev);
9956d02d6   Florian Fainelli   gpio: Add support...
134
135
136
137
138
  	if (!pdata) {
  		dev_err(&pdev->dev, "no platform data supplied
  ");
  		return -ENODEV;
  	}
95ad6b9da   Axel Lin   gpio: rdc321x: Co...
139
140
  	rdc321x_gpio_dev = devm_kzalloc(&pdev->dev, sizeof(struct rdc321x_gpio),
  					GFP_KERNEL);
84e27f97e   Jingoo Han   gpio: rdc321x: re...
141
  	if (!rdc321x_gpio_dev)
9956d02d6   Florian Fainelli   gpio: Add support...
142
  		return -ENOMEM;
9956d02d6   Florian Fainelli   gpio: Add support...
143

8deca39e5   Florian Fainelli   mfd: Change rdc32...
144
  	r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg1");
9956d02d6   Florian Fainelli   gpio: Add support...
145
146
147
  	if (!r) {
  		dev_err(&pdev->dev, "failed to get gpio-reg1 resource
  ");
95ad6b9da   Axel Lin   gpio: rdc321x: Co...
148
  		return -ENODEV;
9956d02d6   Florian Fainelli   gpio: Add support...
149
150
151
152
153
154
  	}
  
  	spin_lock_init(&rdc321x_gpio_dev->lock);
  	rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev;
  	rdc321x_gpio_dev->reg1_ctrl_base = r->start;
  	rdc321x_gpio_dev->reg1_data_base = r->start + 0x4;
8deca39e5   Florian Fainelli   mfd: Change rdc32...
155
  	r = platform_get_resource_byname(pdev, IORESOURCE_IO, "gpio-reg2");
9956d02d6   Florian Fainelli   gpio: Add support...
156
157
158
  	if (!r) {
  		dev_err(&pdev->dev, "failed to get gpio-reg2 resource
  ");
95ad6b9da   Axel Lin   gpio: rdc321x: Co...
159
  		return -ENODEV;
9956d02d6   Florian Fainelli   gpio: Add support...
160
161
162
163
164
165
  	}
  
  	rdc321x_gpio_dev->reg2_ctrl_base = r->start;
  	rdc321x_gpio_dev->reg2_data_base = r->start + 0x4;
  
  	rdc321x_gpio_dev->chip.label = "rdc321x-gpio";
1146f8822   Axel Lin   gpio: rdc321x: Pr...
166
  	rdc321x_gpio_dev->chip.owner = THIS_MODULE;
9956d02d6   Florian Fainelli   gpio: Add support...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  	rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input;
  	rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config;
  	rdc321x_gpio_dev->chip.get = rdc_gpio_get_value;
  	rdc321x_gpio_dev->chip.set = rdc_gpio_set_value;
  	rdc321x_gpio_dev->chip.base = 0;
  	rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios;
  
  	platform_set_drvdata(pdev, rdc321x_gpio_dev);
  
  	/* This might not be, what others (BIOS, bootloader, etc.)
  	   wrote to these registers before, but it's a good guess. Still
  	   better than just using 0xffffffff. */
  	err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
  					rdc321x_gpio_dev->reg1_data_base,
  					&rdc321x_gpio_dev->data_reg[0]);
  	if (err)
95ad6b9da   Axel Lin   gpio: rdc321x: Co...
183
  		return err;
9956d02d6   Florian Fainelli   gpio: Add support...
184
185
186
187
188
  
  	err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev,
  					rdc321x_gpio_dev->reg2_data_base,
  					&rdc321x_gpio_dev->data_reg[1]);
  	if (err)
95ad6b9da   Axel Lin   gpio: rdc321x: Co...
189
  		return err;
9956d02d6   Florian Fainelli   gpio: Add support...
190
191
192
193
  
  	dev_info(&pdev->dev, "registering %d GPIOs
  ",
  					rdc321x_gpio_dev->chip.ngpio);
cfae2c908   Laxman Dewangan   gpio: rdc321x: Us...
194
195
  	return devm_gpiochip_add_data(&pdev->dev, &rdc321x_gpio_dev->chip,
  				      rdc321x_gpio_dev);
9956d02d6   Florian Fainelli   gpio: Add support...
196
197
198
199
  }
  
  static struct platform_driver rdc321x_gpio_driver = {
  	.driver.name	= "rdc321x-gpio",
9956d02d6   Florian Fainelli   gpio: Add support...
200
  	.probe		= rdc321x_gpio_probe,
9956d02d6   Florian Fainelli   gpio: Add support...
201
  };
6f61415e9   Mark Brown   gpio: Convert GPI...
202
  module_platform_driver(rdc321x_gpio_driver);
9956d02d6   Florian Fainelli   gpio: Add support...
203
204
205
206
207
  
  MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
  MODULE_DESCRIPTION("RDC321x GPIO driver");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:rdc321x-gpio");