Blame view

drivers/gpio/gpio-74x164.c 5.26 KB
ead6db084   Miguel Gaio   gpio: add support...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /*
   *  74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
   *
   *  Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
   *  Copyright (C) 2010 Miguel Gaio <miguel.gaio@efixo.com>
   *
   *  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/init.h>
  #include <linux/mutex.h>
  #include <linux/spi/spi.h>
  #include <linux/spi/74x164.h>
  #include <linux/gpio.h>
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
17
  #include <linux/of_gpio.h>
ead6db084   Miguel Gaio   gpio: add support...
18
  #include <linux/slab.h>
bb207ef1e   Paul Gortmaker   drivers/gpio: Fix...
19
  #include <linux/module.h>
ead6db084   Miguel Gaio   gpio: add support...
20

20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
21
  #define GEN_74X164_NUMBER_GPIOS	8
ead6db084   Miguel Gaio   gpio: add support...
22
23
  struct gen_74x164_chip {
  	struct spi_device	*spi;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
24
  	u8			*buffer;
ead6db084   Miguel Gaio   gpio: add support...
25
26
  	struct gpio_chip	gpio_chip;
  	struct mutex		lock;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
27
  	u32			registers;
ead6db084   Miguel Gaio   gpio: add support...
28
  };
12610be33   Linus Walleij   gpio/74x164: fix ...
29
  static struct gen_74x164_chip *gpio_to_74x164_chip(struct gpio_chip *gc)
ead6db084   Miguel Gaio   gpio: add support...
30
31
32
33
34
35
  {
  	return container_of(gc, struct gen_74x164_chip, gpio_chip);
  }
  
  static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
  {
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
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
62
63
64
65
  	struct spi_message message;
  	struct spi_transfer *msg_buf;
  	int i, ret = 0;
  
  	msg_buf = kzalloc(chip->registers * sizeof(struct spi_transfer),
  			GFP_KERNEL);
  	if (!msg_buf)
  		return -ENOMEM;
  
  	spi_message_init(&message);
  
  	/*
  	 * Since the registers are chained, every byte sent will make
  	 * the previous byte shift to the next register in the
  	 * chain. Thus, the first byte send will end up in the last
  	 * register at the end of the transfer. So, to have a logical
  	 * numbering, send the bytes in reverse order so that the last
  	 * byte of the buffer will end up in the last register.
  	 */
  	for (i = chip->registers - 1; i >= 0; i--) {
  		msg_buf[i].tx_buf = chip->buffer +i;
  		msg_buf[i].len = sizeof(u8);
  		spi_message_add_tail(msg_buf + i, &message);
  	}
  
  	ret = spi_sync(chip->spi, &message);
  
  	kfree(msg_buf);
  
  	return ret;
ead6db084   Miguel Gaio   gpio: add support...
66
  }
ead6db084   Miguel Gaio   gpio: add support...
67
68
  static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
  {
12610be33   Linus Walleij   gpio/74x164: fix ...
69
  	struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
70
71
  	u8 bank = offset / 8;
  	u8 pin = offset % 8;
ead6db084   Miguel Gaio   gpio: add support...
72
73
74
  	int ret;
  
  	mutex_lock(&chip->lock);
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
75
  	ret = (chip->buffer[bank] >> pin) & 0x1;
ead6db084   Miguel Gaio   gpio: add support...
76
77
78
79
80
81
82
83
  	mutex_unlock(&chip->lock);
  
  	return ret;
  }
  
  static void gen_74x164_set_value(struct gpio_chip *gc,
  		unsigned offset, int val)
  {
12610be33   Linus Walleij   gpio/74x164: fix ...
84
  	struct gen_74x164_chip *chip = gpio_to_74x164_chip(gc);
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
85
86
  	u8 bank = offset / 8;
  	u8 pin = offset % 8;
ead6db084   Miguel Gaio   gpio: add support...
87
88
89
  
  	mutex_lock(&chip->lock);
  	if (val)
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
90
  		chip->buffer[bank] |= (1 << pin);
ead6db084   Miguel Gaio   gpio: add support...
91
  	else
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
92
  		chip->buffer[bank] &= ~(1 << pin);
ead6db084   Miguel Gaio   gpio: add support...
93
94
95
96
  
  	__gen_74x164_write_config(chip);
  	mutex_unlock(&chip->lock);
  }
a3cc68c37   H Hartley Sweeten   gpio/74x164: remo...
97
98
99
100
101
102
  static int gen_74x164_direction_output(struct gpio_chip *gc,
  		unsigned offset, int val)
  {
  	gen_74x164_set_value(gc, offset, val);
  	return 0;
  }
ead6db084   Miguel Gaio   gpio: add support...
103
104
105
106
107
  static int __devinit gen_74x164_probe(struct spi_device *spi)
  {
  	struct gen_74x164_chip *chip;
  	struct gen_74x164_chip_platform_data *pdata;
  	int ret;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
108
109
110
111
112
  	if (!spi->dev.of_node) {
  		dev_err(&spi->dev, "No device tree data available.
  ");
  		return -EINVAL;
  	}
ead6db084   Miguel Gaio   gpio: add support...
113
114
115
116
117
118
119
120
  	/*
  	 * bits_per_word cannot be configured in platform data
  	 */
  	spi->bits_per_word = 8;
  
  	ret = spi_setup(spi);
  	if (ret < 0)
  		return ret;
72eac3020   Maxime Ripard   gpio: 74x164: Use...
121
  	chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
ead6db084   Miguel Gaio   gpio: add support...
122
123
  	if (!chip)
  		return -ENOMEM;
061505fd3   Maxime Ripard   gpio: 74x164: Use...
124
125
126
127
128
  	pdata = spi->dev.platform_data;
  	if (pdata && pdata->base)
  		chip->gpio_chip.base = pdata->base;
  	else
  		chip->gpio_chip.base = -1;
ead6db084   Miguel Gaio   gpio: add support...
129
130
131
132
133
  	mutex_init(&chip->lock);
  
  	dev_set_drvdata(&spi->dev, chip);
  
  	chip->spi = spi;
a3cc68c37   H Hartley Sweeten   gpio/74x164: remo...
134
135
  	chip->gpio_chip.label = spi->modalias;
  	chip->gpio_chip.direction_output = gen_74x164_direction_output;
ead6db084   Miguel Gaio   gpio: add support...
136
137
  	chip->gpio_chip.get = gen_74x164_get_value;
  	chip->gpio_chip.set = gen_74x164_set_value;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
138
139
140
141
142
143
144
145
146
  
  	if (of_property_read_u32(spi->dev.of_node, "registers-number", &chip->registers)) {
  		dev_err(&spi->dev, "Missing registers-number property in the DT.
  ");
  		ret = -EINVAL;
  		goto exit_destroy;
  	}
  
  	chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
a48221a26   Roland Stigge   gpio-74x164: Fix ...
147
  	chip->buffer = devm_kzalloc(&spi->dev, chip->registers, GFP_KERNEL);
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
148
149
150
151
  	if (!chip->buffer) {
  		ret = -ENOMEM;
  		goto exit_destroy;
  	}
ead6db084   Miguel Gaio   gpio: add support...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  	chip->gpio_chip.can_sleep = 1;
  	chip->gpio_chip.dev = &spi->dev;
  	chip->gpio_chip.owner = THIS_MODULE;
  
  	ret = __gen_74x164_write_config(chip);
  	if (ret) {
  		dev_err(&spi->dev, "Failed writing: %d
  ", ret);
  		goto exit_destroy;
  	}
  
  	ret = gpiochip_add(&chip->gpio_chip);
  	if (ret)
  		goto exit_destroy;
  
  	return ret;
  
  exit_destroy:
  	dev_set_drvdata(&spi->dev, NULL);
  	mutex_destroy(&chip->lock);
ead6db084   Miguel Gaio   gpio: add support...
172
173
  	return ret;
  }
fc1599f7b   Axel Lin   gpio: Use __devex...
174
  static int __devexit gen_74x164_remove(struct spi_device *spi)
ead6db084   Miguel Gaio   gpio: add support...
175
176
177
178
179
180
181
182
183
184
185
  {
  	struct gen_74x164_chip *chip;
  	int ret;
  
  	chip = dev_get_drvdata(&spi->dev);
  	if (chip == NULL)
  		return -ENODEV;
  
  	dev_set_drvdata(&spi->dev, NULL);
  
  	ret = gpiochip_remove(&chip->gpio_chip);
72eac3020   Maxime Ripard   gpio: 74x164: Use...
186
  	if (!ret)
ead6db084   Miguel Gaio   gpio: add support...
187
  		mutex_destroy(&chip->lock);
72eac3020   Maxime Ripard   gpio: 74x164: Use...
188
  	else
ead6db084   Miguel Gaio   gpio: add support...
189
190
191
192
193
194
  		dev_err(&spi->dev, "Failed to remove the GPIO controller: %d
  ",
  				ret);
  
  	return ret;
  }
0a90a9fb4   Maxime Ripard   gpio: 74x164: Add...
195
196
197
198
199
  static const struct of_device_id gen_74x164_dt_ids[] = {
  	{ .compatible = "fairchild,74hc595" },
  	{},
  };
  MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
ead6db084   Miguel Gaio   gpio: add support...
200
201
  static struct spi_driver gen_74x164_driver = {
  	.driver = {
a3cc68c37   H Hartley Sweeten   gpio/74x164: remo...
202
  		.name		= "74x164",
ead6db084   Miguel Gaio   gpio: add support...
203
  		.owner		= THIS_MODULE,
0a90a9fb4   Maxime Ripard   gpio: 74x164: Add...
204
  		.of_match_table	= of_match_ptr(gen_74x164_dt_ids),
ead6db084   Miguel Gaio   gpio: add support...
205
206
207
208
  	},
  	.probe		= gen_74x164_probe,
  	.remove		= __devexit_p(gen_74x164_remove),
  };
ab3b87826   Maxime Ripard   gpio: 74x164: Use...
209
  module_spi_driver(gen_74x164_driver);
ead6db084   Miguel Gaio   gpio: add support...
210
211
212
213
214
  
  MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
  MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
  MODULE_DESCRIPTION("GPIO expander driver for 74X164 8-bits shift register");
  MODULE_LICENSE("GPL v2");