Blame view

drivers/platform/x86/intel_pmic_gpio.c 8.13 KB
895077870   Alek Du   gpio: Add PMIC GP...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /* Moorestown PMIC GPIO (access through IPC) driver
   * Copyright (c) 2008 - 2009, Intel Corporation.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * 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.
   */
  
  /* Supports:
   * Moorestown platform PMIC chip
   */
9a2ffd168   Joe Perches   intel_pmic_gpio: ...
21
  #define pr_fmt(fmt) "%s: " fmt, __func__
895077870   Alek Du   gpio: Add PMIC GP...
22
23
24
25
26
27
28
29
30
31
  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/interrupt.h>
  #include <linux/delay.h>
  #include <linux/stddef.h>
  #include <linux/slab.h>
  #include <linux/ioport.h>
  #include <linux/init.h>
  #include <linux/io.h>
  #include <linux/gpio.h>
895077870   Alek Du   gpio: Add PMIC GP...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  #include <asm/intel_scu_ipc.h>
  #include <linux/device.h>
  #include <linux/intel_pmic_gpio.h>
  #include <linux/platform_device.h>
  
  #define DRIVER_NAME "pmic_gpio"
  
  /* register offset that IPC driver should use
   * 8 GPIO + 8 GPOSW (6 controllable) + 8GPO
   */
  enum pmic_gpio_register {
  	GPIO0		= 0xE0,
  	GPIO7		= 0xE7,
  	GPIOINT		= 0xE8,
  	GPOSWCTL0	= 0xEC,
  	GPOSWCTL5	= 0xF1,
  	GPO		= 0xF4,
  };
  
  /* bits definition for GPIO & GPOSW */
  #define GPIO_DRV 0x01
  #define GPIO_DIR 0x02
  #define GPIO_DIN 0x04
  #define GPIO_DOU 0x08
  #define GPIO_INTCTL 0x30
  #define GPIO_DBC 0xc0
  
  #define GPOSW_DRV 0x01
  #define GPOSW_DOU 0x08
  #define GPOSW_RDRV 0x30
d4b7de612   Thomas Gleixner   platform-drivers:...
62
  #define GPIO_UPDATE_TYPE	0x80000000
895077870   Alek Du   gpio: Add PMIC GP...
63
64
  
  #define NUM_GPIO 24
895077870   Alek Du   gpio: Add PMIC GP...
65
  struct pmic_gpio {
d4b7de612   Thomas Gleixner   platform-drivers:...
66
  	struct mutex		buslock;
895077870   Alek Du   gpio: Add PMIC GP...
67
  	struct gpio_chip	chip;
895077870   Alek Du   gpio: Add PMIC GP...
68
69
70
  	void			*gpiointr;
  	int			irq;
  	unsigned		irq_base;
d4b7de612   Thomas Gleixner   platform-drivers:...
71
72
  	unsigned int		update_type;
  	u32			trigger_type;
895077870   Alek Du   gpio: Add PMIC GP...
73
  };
65d7ac038   Thomas Gleixner   platform-drivers:...
74
75
76
77
78
79
80
81
82
83
84
85
  static void pmic_program_irqtype(int gpio, int type)
  {
  	if (type & IRQ_TYPE_EDGE_RISING)
  		intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20);
  	else
  		intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20);
  
  	if (type & IRQ_TYPE_EDGE_FALLING)
  		intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10);
  	else
  		intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10);
  };
895077870   Alek Du   gpio: Add PMIC GP...
86
87
88
  static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
  {
  	if (offset > 8) {
9a2ffd168   Joe Perches   intel_pmic_gpio: ...
89
90
  		pr_err("only pin 0-7 support input
  ");
895077870   Alek Du   gpio: Add PMIC GP...
91
92
93
94
95
96
97
98
99
100
101
102
103
  		return -1;/* we only have 8 GPIO can use as input */
  	}
  	return intel_scu_ipc_update_register(GPIO0 + offset,
  							GPIO_DIR, GPIO_DIR);
  }
  
  static int pmic_gpio_direction_output(struct gpio_chip *chip,
  			unsigned offset, int value)
  {
  	int rc = 0;
  
  	if (offset < 8)/* it is GPIO */
  		rc = intel_scu_ipc_update_register(GPIO0 + offset,
ffcfff3a8   Alek Du   intel_pmic_gpio: ...
104
105
  				GPIO_DRV | (value ? GPIO_DOU : 0),
  				GPIO_DRV | GPIO_DOU | GPIO_DIR);
895077870   Alek Du   gpio: Add PMIC GP...
106
107
  	else if (offset < 16)/* it is GPOSW */
  		rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
ffcfff3a8   Alek Du   intel_pmic_gpio: ...
108
109
  				GPOSW_DRV | (value ? GPOSW_DOU : 0),
  				GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
895077870   Alek Du   gpio: Add PMIC GP...
110
111
  	else if (offset > 15 && offset < 24)/* it is GPO */
  		rc = intel_scu_ipc_update_register(GPO,
ffcfff3a8   Alek Du   intel_pmic_gpio: ...
112
113
  				value ? 1 << (offset - 16) : 0,
  				1 << (offset - 16));
895077870   Alek Du   gpio: Add PMIC GP...
114
  	else {
9a2ffd168   Joe Perches   intel_pmic_gpio: ...
115
116
  		pr_err("invalid PMIC GPIO pin %d!
  ", offset);
895077870   Alek Du   gpio: Add PMIC GP...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  		WARN_ON(1);
  	}
  
  	return rc;
  }
  
  static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset)
  {
  	u8 r;
  	int ret;
  
  	/* we only have 8 GPIO pins we can use as input */
  	if (offset > 8)
  		return -EOPNOTSUPP;
  	ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r);
  	if (ret < 0)
  		return ret;
  	return r & GPIO_DIN;
  }
  
  static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  {
  	if (offset < 8)/* it is GPIO */
  		intel_scu_ipc_update_register(GPIO0 + offset,
ffcfff3a8   Alek Du   intel_pmic_gpio: ...
141
142
  			GPIO_DRV | (value ? GPIO_DOU : 0),
  			GPIO_DRV | GPIO_DOU);
895077870   Alek Du   gpio: Add PMIC GP...
143
144
  	else if (offset < 16)/* it is GPOSW */
  		intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8,
ffcfff3a8   Alek Du   intel_pmic_gpio: ...
145
146
  			GPOSW_DRV | (value ? GPOSW_DOU : 0),
  			GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV);
895077870   Alek Du   gpio: Add PMIC GP...
147
148
  	else if (offset > 15 && offset < 24) /* it is GPO */
  		intel_scu_ipc_update_register(GPO,
ffcfff3a8   Alek Du   intel_pmic_gpio: ...
149
150
  			value ? 1 << (offset - 16) : 0,
  			1 << (offset - 16));
895077870   Alek Du   gpio: Add PMIC GP...
151
  }
d4b7de612   Thomas Gleixner   platform-drivers:...
152
153
154
155
156
  /*
   * This is called from genirq with pg->buslock locked and
   * irq_desc->lock held. We can not access the scu bus here, so we
   * store the change and update in the bus_sync_unlock() function below
   */
cb8e5e6a6   Thomas Gleixner   platform-drivers:...
157
  static int pmic_irq_type(struct irq_data *data, unsigned type)
895077870   Alek Du   gpio: Add PMIC GP...
158
  {
cb8e5e6a6   Thomas Gleixner   platform-drivers:...
159
160
  	struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
  	u32 gpio = data->irq - pg->irq_base;
895077870   Alek Du   gpio: Add PMIC GP...
161

411961791   Axel Lin   intel_pmic_gpio: ...
162
  	if (gpio >= pg->chip.ngpio)
895077870   Alek Du   gpio: Add PMIC GP...
163
  		return -EINVAL;
d4b7de612   Thomas Gleixner   platform-drivers:...
164
165
  	pg->trigger_type = type;
  	pg->update_type = gpio | GPIO_UPDATE_TYPE;
895077870   Alek Du   gpio: Add PMIC GP...
166
167
  	return 0;
  }
895077870   Alek Du   gpio: Add PMIC GP...
168
169
170
171
172
173
  static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
  {
  	struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip);
  
  	return pg->irq_base + offset;
  }
65d7ac038   Thomas Gleixner   platform-drivers:...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  static void pmic_bus_lock(struct irq_data *data)
  {
  	struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
  
  	mutex_lock(&pg->buslock);
  }
  
  static void pmic_bus_sync_unlock(struct irq_data *data)
  {
  	struct pmic_gpio *pg = irq_data_get_irq_chip_data(data);
  
  	if (pg->update_type) {
  		unsigned int gpio = pg->update_type & ~GPIO_UPDATE_TYPE;
  
  		pmic_program_irqtype(gpio, pg->trigger_type);
  		pg->update_type = 0;
  	}
  	mutex_unlock(&pg->buslock);
  }
895077870   Alek Du   gpio: Add PMIC GP...
193
  /* the gpiointr register is read-clear, so just do nothing. */
cb8e5e6a6   Thomas Gleixner   platform-drivers:...
194
  static void pmic_irq_unmask(struct irq_data *data) { }
895077870   Alek Du   gpio: Add PMIC GP...
195

cb8e5e6a6   Thomas Gleixner   platform-drivers:...
196
  static void pmic_irq_mask(struct irq_data *data) { }
895077870   Alek Du   gpio: Add PMIC GP...
197
198
  
  static struct irq_chip pmic_irqchip = {
65d7ac038   Thomas Gleixner   platform-drivers:...
199
200
201
202
  	.name			= "PMIC-GPIO",
  	.irq_mask		= pmic_irq_mask,
  	.irq_unmask		= pmic_irq_unmask,
  	.irq_set_type		= pmic_irq_type,
21a8d026e   Matthew Garrett   x86 platform driv...
203
  	.irq_bus_lock		= pmic_bus_lock,
65d7ac038   Thomas Gleixner   platform-drivers:...
204
  	.irq_bus_sync_unlock	= pmic_bus_sync_unlock,
895077870   Alek Du   gpio: Add PMIC GP...
205
  };
98401ae43   Thomas Gleixner   platform-drivers:...
206
  static irqreturn_t pmic_irq_handler(int irq, void *data)
895077870   Alek Du   gpio: Add PMIC GP...
207
  {
98401ae43   Thomas Gleixner   platform-drivers:...
208
  	struct pmic_gpio *pg = data;
895077870   Alek Du   gpio: Add PMIC GP...
209
210
  	u8 intsts = *((u8 *)pg->gpiointr + 4);
  	int gpio;
98401ae43   Thomas Gleixner   platform-drivers:...
211
  	irqreturn_t ret = IRQ_NONE;
895077870   Alek Du   gpio: Add PMIC GP...
212
213
214
215
216
217
  
  	for (gpio = 0; gpio < 8; gpio++) {
  		if (intsts & (1 << gpio)) {
  			pr_debug("pmic pin %d triggered
  ", gpio);
  			generic_handle_irq(pg->irq_base + gpio);
98401ae43   Thomas Gleixner   platform-drivers:...
218
  			ret = IRQ_HANDLED;
895077870   Alek Du   gpio: Add PMIC GP...
219
220
  		}
  	}
98401ae43   Thomas Gleixner   platform-drivers:...
221
  	return ret;
895077870   Alek Du   gpio: Add PMIC GP...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  }
  
  static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
  	int irq = platform_get_irq(pdev, 0);
  	struct intel_pmic_gpio_platform_data *pdata = dev->platform_data;
  
  	struct pmic_gpio *pg;
  	int retval;
  	int i;
  
  	if (irq < 0) {
  		dev_dbg(dev, "no IRQ line
  ");
  		return -EINVAL;
  	}
  
  	if (!pdata || !pdata->gpio_base || !pdata->irq_base) {
  		dev_dbg(dev, "incorrect or missing platform data
  ");
  		return -EINVAL;
  	}
  
  	pg = kzalloc(sizeof(*pg), GFP_KERNEL);
  	if (!pg)
  		return -ENOMEM;
  
  	dev_set_drvdata(dev, pg);
  
  	pg->irq = irq;
  	/* setting up SRAM mapping for GPIOINT register */
  	pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8);
  	if (!pg->gpiointr) {
9a2ffd168   Joe Perches   intel_pmic_gpio: ...
256
257
  		pr_err("Can not map GPIOINT
  ");
895077870   Alek Du   gpio: Add PMIC GP...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  		retval = -EINVAL;
  		goto err2;
  	}
  	pg->irq_base = pdata->irq_base;
  	pg->chip.label = "intel_pmic";
  	pg->chip.direction_input = pmic_gpio_direction_input;
  	pg->chip.direction_output = pmic_gpio_direction_output;
  	pg->chip.get = pmic_gpio_get;
  	pg->chip.set = pmic_gpio_set;
  	pg->chip.to_irq = pmic_gpio_to_irq;
  	pg->chip.base = pdata->gpio_base;
  	pg->chip.ngpio = NUM_GPIO;
  	pg->chip.can_sleep = 1;
  	pg->chip.dev = dev;
d4b7de612   Thomas Gleixner   platform-drivers:...
272
  	mutex_init(&pg->buslock);
895077870   Alek Du   gpio: Add PMIC GP...
273
274
275
276
  
  	pg->chip.dev = dev;
  	retval = gpiochip_add(&pg->chip);
  	if (retval) {
9a2ffd168   Joe Perches   intel_pmic_gpio: ...
277
278
  		pr_err("Can not add pmic gpio chip
  ");
895077870   Alek Du   gpio: Add PMIC GP...
279
280
  		goto err;
  	}
98401ae43   Thomas Gleixner   platform-drivers:...
281
282
283
  
  	retval = request_irq(pg->irq, pmic_irq_handler, 0, "pmic", pg);
  	if (retval) {
9a2ffd168   Joe Perches   intel_pmic_gpio: ...
284
285
  		pr_warn("Interrupt request failed
  ");
98401ae43   Thomas Gleixner   platform-drivers:...
286
287
  		goto err;
  	}
895077870   Alek Du   gpio: Add PMIC GP...
288
  	for (i = 0; i < 8; i++) {
dced35aeb   Thomas Gleixner   drivers: Final ir...
289
290
291
292
293
  		irq_set_chip_and_handler_name(i + pg->irq_base,
  					      &pmic_irqchip,
  					      handle_simple_irq,
  					      "demux");
  		irq_set_chip_data(i + pg->irq_base, pg);
895077870   Alek Du   gpio: Add PMIC GP...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
  	}
  	return 0;
  err:
  	iounmap(pg->gpiointr);
  err2:
  	kfree(pg);
  	return retval;
  }
  
  /* at the same time, register a platform driver
   * this supports the sfi 0.81 fw */
  static struct platform_driver platform_pmic_gpio_driver = {
  	.driver = {
  		.name		= DRIVER_NAME,
  		.owner		= THIS_MODULE,
  	},
  	.probe		= platform_pmic_gpio_probe,
  };
  
  static int __init platform_pmic_gpio_init(void)
  {
  	return platform_driver_register(&platform_pmic_gpio_driver);
  }
  
  subsys_initcall(platform_pmic_gpio_init);
  
  MODULE_AUTHOR("Alek Du <alek.du@intel.com>");
  MODULE_DESCRIPTION("Intel Moorestown PMIC GPIO driver");
  MODULE_LICENSE("GPL v2");