Blame view

drivers/gpio/gpio-zevio.c 5.86 KB
9af4d80ba   Fabian Vogt   gpio: New driver ...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * GPIO controller in LSI ZEVIO SoCs.
   *
   * Author: Fabian Vogt <fabian@ritter-vogt.de>
   *
   * 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.
   */
  
  #include <linux/spinlock.h>
  #include <linux/errno.h>
a90295b48   Paul Gortmaker   gpio: zevio: make...
13
  #include <linux/init.h>
9af4d80ba   Fabian Vogt   gpio: New driver ...
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  #include <linux/bitops.h>
  #include <linux/io.h>
  #include <linux/of_device.h>
  #include <linux/of_gpio.h>
  #include <linux/slab.h>
  #include <linux/gpio.h>
  
  /*
   * Memory layout:
   * This chip has four gpio sections, each controls 8 GPIOs.
   * Bit 0 in section 0 is GPIO 0, bit 2 in section 1 is GPIO 10.
   * Disclaimer: Reverse engineered!
   * For more information refer to:
   * http://hackspire.unsads.com/wiki/index.php/Memory-mapped_I/O_ports#90000000_-_General_Purpose_I.2FO_.28GPIO.29
   *
   * 0x00-0x3F: Section 0
   *     +0x00: Masked interrupt status (read-only)
   *     +0x04: R: Interrupt status W: Reset interrupt status
   *     +0x08: R: Interrupt mask W: Mask interrupt
   *     +0x0C: W: Unmask interrupt (write-only)
   *     +0x10: Direction: I/O=1/0
   *     +0x14: Output
   *     +0x18: Input (read-only)
   *     +0x20: R: Level interrupt W: Set as level interrupt
   * 0x40-0x7F: Section 1
   * 0x80-0xBF: Section 2
   * 0xC0-0xFF: Section 3
   */
  
  #define ZEVIO_GPIO_SECTION_SIZE			0x40
  
  /* Offsets to various registers */
  #define ZEVIO_GPIO_INT_MASKED_STATUS	0x00
  #define ZEVIO_GPIO_INT_STATUS		0x04
  #define ZEVIO_GPIO_INT_UNMASK		0x08
  #define ZEVIO_GPIO_INT_MASK		0x0C
  #define ZEVIO_GPIO_DIRECTION		0x10
  #define ZEVIO_GPIO_OUTPUT		0x14
  #define ZEVIO_GPIO_INPUT			0x18
  #define ZEVIO_GPIO_INT_STICKY		0x20
9af4d80ba   Fabian Vogt   gpio: New driver ...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  /* Bit number of GPIO in its section */
  #define ZEVIO_GPIO_BIT(gpio) (gpio&7)
  
  struct zevio_gpio {
  	spinlock_t		lock;
  	struct of_mm_gpio_chip	chip;
  };
  
  static inline u32 zevio_gpio_port_get(struct zevio_gpio *c, unsigned pin,
  					unsigned port_offset)
  {
  	unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
  	return readl(IOMEM(c->chip.regs + section_offset + port_offset));
  }
  
  static inline void zevio_gpio_port_set(struct zevio_gpio *c, unsigned pin,
  					unsigned port_offset, u32 val)
  {
  	unsigned section_offset = ((pin >> 3) & 3)*ZEVIO_GPIO_SECTION_SIZE;
  	writel(val, IOMEM(c->chip.regs + section_offset + port_offset));
  }
  
  /* Functions for struct gpio_chip */
  static int zevio_gpio_get(struct gpio_chip *chip, unsigned pin)
  {
9a3ad668a   Linus Walleij   gpio: zevio: use ...
79
  	struct zevio_gpio *controller = gpiochip_get_data(chip);
ef7de2625   Axel Lin   gpio: zevio: Get ...
80
  	u32 val, dir;
9af4d80ba   Fabian Vogt   gpio: New driver ...
81

ef7de2625   Axel Lin   gpio: zevio: Get ...
82
83
84
85
86
87
88
  	spin_lock(&controller->lock);
  	dir = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
  	if (dir & BIT(ZEVIO_GPIO_BIT(pin)))
  		val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_INPUT);
  	else
  		val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
  	spin_unlock(&controller->lock);
9af4d80ba   Fabian Vogt   gpio: New driver ...
89
90
91
92
93
94
  
  	return (val >> ZEVIO_GPIO_BIT(pin)) & 0x1;
  }
  
  static void zevio_gpio_set(struct gpio_chip *chip, unsigned pin, int value)
  {
9a3ad668a   Linus Walleij   gpio: zevio: use ...
95
  	struct zevio_gpio *controller = gpiochip_get_data(chip);
9af4d80ba   Fabian Vogt   gpio: New driver ...
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  	u32 val;
  
  	spin_lock(&controller->lock);
  	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
  	if (value)
  		val |= BIT(ZEVIO_GPIO_BIT(pin));
  	else
  		val &= ~BIT(ZEVIO_GPIO_BIT(pin));
  
  	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
  	spin_unlock(&controller->lock);
  }
  
  static int zevio_gpio_direction_input(struct gpio_chip *chip, unsigned pin)
  {
9a3ad668a   Linus Walleij   gpio: zevio: use ...
111
  	struct zevio_gpio *controller = gpiochip_get_data(chip);
9af4d80ba   Fabian Vogt   gpio: New driver ...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  	u32 val;
  
  	spin_lock(&controller->lock);
  
  	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
  	val |= BIT(ZEVIO_GPIO_BIT(pin));
  	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
  
  	spin_unlock(&controller->lock);
  
  	return 0;
  }
  
  static int zevio_gpio_direction_output(struct gpio_chip *chip,
  				       unsigned pin, int value)
  {
9a3ad668a   Linus Walleij   gpio: zevio: use ...
128
  	struct zevio_gpio *controller = gpiochip_get_data(chip);
9af4d80ba   Fabian Vogt   gpio: New driver ...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  	u32 val;
  
  	spin_lock(&controller->lock);
  	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_OUTPUT);
  	if (value)
  		val |= BIT(ZEVIO_GPIO_BIT(pin));
  	else
  		val &= ~BIT(ZEVIO_GPIO_BIT(pin));
  
  	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_OUTPUT, val);
  	val = zevio_gpio_port_get(controller, pin, ZEVIO_GPIO_DIRECTION);
  	val &= ~BIT(ZEVIO_GPIO_BIT(pin));
  	zevio_gpio_port_set(controller, pin, ZEVIO_GPIO_DIRECTION, val);
  
  	spin_unlock(&controller->lock);
  
  	return 0;
  }
  
  static int zevio_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
  {
  	/*
  	 * TODO: Implement IRQs.
  	 * Not implemented yet due to weird lockups
  	 */
  
  	return -ENXIO;
  }
  
  static struct gpio_chip zevio_gpio_chip = {
  	.direction_input	= zevio_gpio_direction_input,
  	.direction_output	= zevio_gpio_direction_output,
  	.set			= zevio_gpio_set,
  	.get			= zevio_gpio_get,
  	.to_irq			= zevio_gpio_to_irq,
  	.base			= 0,
  	.owner			= THIS_MODULE,
  	.ngpio			= 32,
  	.of_gpio_n_cells	= 2,
  };
  
  /* Initialization */
  static int zevio_gpio_probe(struct platform_device *pdev)
  {
  	struct zevio_gpio *controller;
  	int status, i;
  
  	controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL);
50908d61e   Jingoo Han   gpio: zevio: remo...
177
  	if (!controller)
9af4d80ba   Fabian Vogt   gpio: New driver ...
178
  		return -ENOMEM;
9af4d80ba   Fabian Vogt   gpio: New driver ...
179

ff00be69f   Ricardo Ribalda Delgado   gpio: zevio: Use ...
180
  	platform_set_drvdata(pdev, controller);
9af4d80ba   Fabian Vogt   gpio: New driver ...
181
182
  	/* Copy our reference */
  	controller->chip.gc = zevio_gpio_chip;
58383c784   Linus Walleij   gpio: change memb...
183
  	controller->chip.gc.parent = &pdev->dev;
9af4d80ba   Fabian Vogt   gpio: New driver ...
184

9a3ad668a   Linus Walleij   gpio: zevio: use ...
185
186
187
  	status = of_mm_gpiochip_add_data(pdev->dev.of_node,
  					 &(controller->chip),
  					 controller);
9af4d80ba   Fabian Vogt   gpio: New driver ...
188
189
190
191
192
193
194
195
196
197
198
  	if (status) {
  		dev_err(&pdev->dev, "failed to add gpiochip: %d
  ", status);
  		return status;
  	}
  
  	spin_lock_init(&controller->lock);
  
  	/* Disable interrupts, they only cause errors */
  	for (i = 0; i < controller->chip.gc.ngpio; i += 8)
  		zevio_gpio_port_set(controller, i, ZEVIO_GPIO_INT_MASK, 0xFF);
58383c784   Linus Walleij   gpio: change memb...
199
200
  	dev_dbg(controller->chip.gc.parent, "ZEVIO GPIO controller set up!
  ");
9af4d80ba   Fabian Vogt   gpio: New driver ...
201
202
203
  
  	return 0;
  }
a49f2e748   Jingoo Han   gpio: zevio: Make...
204
  static const struct of_device_id zevio_gpio_of_match[] = {
9af4d80ba   Fabian Vogt   gpio: New driver ...
205
206
207
  	{ .compatible = "lsi,zevio-gpio", },
  	{ },
  };
9af4d80ba   Fabian Vogt   gpio: New driver ...
208
209
210
  static struct platform_driver zevio_gpio_driver = {
  	.driver		= {
  		.name	= "gpio-zevio",
9ea8d8102   Axel Lin   gpio: zevio: Remo...
211
  		.of_match_table = zevio_gpio_of_match,
a90295b48   Paul Gortmaker   gpio: zevio: make...
212
  		.suppress_bind_attrs = true,
9af4d80ba   Fabian Vogt   gpio: New driver ...
213
214
215
  	},
  	.probe		= zevio_gpio_probe,
  };
a90295b48   Paul Gortmaker   gpio: zevio: make...
216
  builtin_platform_driver(zevio_gpio_driver);