1/*
2 * GPIO interface for Intel Poulsbo SCH
3 *
4 *  Copyright (c) 2010 CompuLab Ltd
5 *  Author: Denis Turischev <denis@compulab.co.il>
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 2 as published
9 *  by the Free Software Foundation.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; see the file COPYING.  If not, write to
18 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include <linux/init.h>
22#include <linux/kernel.h>
23#include <linux/module.h>
24#include <linux/io.h>
25#include <linux/errno.h>
26#include <linux/acpi.h>
27#include <linux/platform_device.h>
28#include <linux/pci_ids.h>
29
30#include <linux/gpio.h>
31
32#define GEN	0x00
33#define GIO	0x04
34#define GLV	0x08
35
36struct sch_gpio {
37	struct gpio_chip chip;
38	spinlock_t lock;
39	unsigned short iobase;
40	unsigned short core_base;
41	unsigned short resume_base;
42};
43
44#define to_sch_gpio(gc)	container_of(gc, struct sch_gpio, chip)
45
46static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
47				unsigned reg)
48{
49	unsigned base = 0;
50
51	if (gpio >= sch->resume_base) {
52		gpio -= sch->resume_base;
53		base += 0x20;
54	}
55
56	return base + reg + gpio / 8;
57}
58
59static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
60{
61	if (gpio >= sch->resume_base)
62		gpio -= sch->resume_base;
63	return gpio % 8;
64}
65
66static int sch_gpio_reg_get(struct gpio_chip *gc, unsigned gpio, unsigned reg)
67{
68	struct sch_gpio *sch = to_sch_gpio(gc);
69	unsigned short offset, bit;
70	u8 reg_val;
71
72	offset = sch_gpio_offset(sch, gpio, reg);
73	bit = sch_gpio_bit(sch, gpio);
74
75	reg_val = !!(inb(sch->iobase + offset) & BIT(bit));
76
77	return reg_val;
78}
79
80static void sch_gpio_reg_set(struct gpio_chip *gc, unsigned gpio, unsigned reg,
81			     int val)
82{
83	struct sch_gpio *sch = to_sch_gpio(gc);
84	unsigned short offset, bit;
85	u8 reg_val;
86
87	offset = sch_gpio_offset(sch, gpio, reg);
88	bit = sch_gpio_bit(sch, gpio);
89
90	reg_val = inb(sch->iobase + offset);
91
92	if (val)
93		outb(reg_val | BIT(bit), sch->iobase + offset);
94	else
95		outb((reg_val & ~BIT(bit)), sch->iobase + offset);
96}
97
98static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
99{
100	struct sch_gpio *sch = to_sch_gpio(gc);
101
102	spin_lock(&sch->lock);
103	sch_gpio_reg_set(gc, gpio_num, GIO, 1);
104	spin_unlock(&sch->lock);
105	return 0;
106}
107
108static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
109{
110	return sch_gpio_reg_get(gc, gpio_num, GLV);
111}
112
113static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
114{
115	struct sch_gpio *sch = to_sch_gpio(gc);
116
117	spin_lock(&sch->lock);
118	sch_gpio_reg_set(gc, gpio_num, GLV, val);
119	spin_unlock(&sch->lock);
120}
121
122static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
123				  int val)
124{
125	struct sch_gpio *sch = to_sch_gpio(gc);
126
127	spin_lock(&sch->lock);
128	sch_gpio_reg_set(gc, gpio_num, GIO, 0);
129	spin_unlock(&sch->lock);
130
131	/*
132	 * according to the datasheet, writing to the level register has no
133	 * effect when GPIO is programmed as input.
134	 * Actually the the level register is read-only when configured as input.
135	 * Thus presetting the output level before switching to output is _NOT_ possible.
136	 * Hence we set the level after configuring the GPIO as output.
137	 * But we cannot prevent a short low pulse if direction is set to high
138	 * and an external pull-up is connected.
139	 */
140	sch_gpio_set(gc, gpio_num, val);
141	return 0;
142}
143
144static struct gpio_chip sch_gpio_chip = {
145	.label			= "sch_gpio",
146	.owner			= THIS_MODULE,
147	.direction_input	= sch_gpio_direction_in,
148	.get			= sch_gpio_get,
149	.direction_output	= sch_gpio_direction_out,
150	.set			= sch_gpio_set,
151};
152
153static int sch_gpio_probe(struct platform_device *pdev)
154{
155	struct sch_gpio *sch;
156	struct resource *res;
157
158	sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
159	if (!sch)
160		return -ENOMEM;
161
162	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
163	if (!res)
164		return -EBUSY;
165
166	if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
167				 pdev->name))
168		return -EBUSY;
169
170	spin_lock_init(&sch->lock);
171	sch->iobase = res->start;
172	sch->chip = sch_gpio_chip;
173	sch->chip.label = dev_name(&pdev->dev);
174	sch->chip.dev = &pdev->dev;
175
176	switch (pdev->id) {
177	case PCI_DEVICE_ID_INTEL_SCH_LPC:
178		sch->core_base = 0;
179		sch->resume_base = 10;
180		sch->chip.ngpio = 14;
181
182		/*
183		 * GPIO[6:0] enabled by default
184		 * GPIO7 is configured by the CMC as SLPIOVR
185		 * Enable GPIO[9:8] core powered gpios explicitly
186		 */
187		sch_gpio_reg_set(&sch->chip, 8, GEN, 1);
188		sch_gpio_reg_set(&sch->chip, 9, GEN, 1);
189		/*
190		 * SUS_GPIO[2:0] enabled by default
191		 * Enable SUS_GPIO3 resume powered gpio explicitly
192		 */
193		sch_gpio_reg_set(&sch->chip, 13, GEN, 1);
194		break;
195
196	case PCI_DEVICE_ID_INTEL_ITC_LPC:
197		sch->core_base = 0;
198		sch->resume_base = 5;
199		sch->chip.ngpio = 14;
200		break;
201
202	case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
203		sch->core_base = 0;
204		sch->resume_base = 21;
205		sch->chip.ngpio = 30;
206		break;
207
208	case PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB:
209		sch->core_base = 0;
210		sch->resume_base = 2;
211		sch->chip.ngpio = 8;
212		break;
213
214	default:
215		return -ENODEV;
216	}
217
218	platform_set_drvdata(pdev, sch);
219
220	return gpiochip_add(&sch->chip);
221}
222
223static int sch_gpio_remove(struct platform_device *pdev)
224{
225	struct sch_gpio *sch = platform_get_drvdata(pdev);
226
227	gpiochip_remove(&sch->chip);
228	return 0;
229}
230
231static struct platform_driver sch_gpio_driver = {
232	.driver = {
233		.name = "sch_gpio",
234	},
235	.probe		= sch_gpio_probe,
236	.remove		= sch_gpio_remove,
237};
238
239module_platform_driver(sch_gpio_driver);
240
241MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
242MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
243MODULE_LICENSE("GPL");
244MODULE_ALIAS("platform:sch_gpio");
245