Blame view

drivers/gpio/gpio-74x164.c 4.79 KB
ead6db084   Miguel Gaio   gpio: add support...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   *  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>
ead6db084   Miguel Gaio   gpio: add support...
15
  #include <linux/gpio.h>
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
16
  #include <linux/of_gpio.h>
ead6db084   Miguel Gaio   gpio: add support...
17
  #include <linux/slab.h>
bb207ef1e   Paul Gortmaker   drivers/gpio: Fix...
18
  #include <linux/module.h>
ead6db084   Miguel Gaio   gpio: add support...
19

20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
20
  #define GEN_74X164_NUMBER_GPIOS	8
ead6db084   Miguel Gaio   gpio: add support...
21
  struct gen_74x164_chip {
ead6db084   Miguel Gaio   gpio: add support...
22
23
  	struct gpio_chip	gpio_chip;
  	struct mutex		lock;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
24
  	u32			registers;
902e7e600   Geert Uytterhoeven   gpio: 74x164: Use...
25
26
27
28
29
30
31
  	/*
  	 * 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 sent will end up in the last
  	 * register at the end of the transfer. So, to have a logical
  	 * numbering, store the bytes in reverse order.
  	 */
410f4574f   Geert Uytterhoeven   gpio: 74x164: All...
32
  	u8			buffer[0];
ead6db084   Miguel Gaio   gpio: add support...
33
  };
ead6db084   Miguel Gaio   gpio: add support...
34
35
  static int __gen_74x164_write_config(struct gen_74x164_chip *chip)
  {
771d899ad   Geert Uytterhoeven   gpio: 74x164: Use...
36
37
  	return spi_write(to_spi_device(chip->gpio_chip.parent), chip->buffer,
  			 chip->registers);
ead6db084   Miguel Gaio   gpio: add support...
38
  }
ead6db084   Miguel Gaio   gpio: add support...
39
40
  static int gen_74x164_get_value(struct gpio_chip *gc, unsigned offset)
  {
b2afc6f35   Linus Walleij   gpio: 74x164: use...
41
  	struct gen_74x164_chip *chip = gpiochip_get_data(gc);
902e7e600   Geert Uytterhoeven   gpio: 74x164: Use...
42
  	u8 bank = chip->registers - 1 - offset / 8;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
43
  	u8 pin = offset % 8;
ead6db084   Miguel Gaio   gpio: add support...
44
45
46
  	int ret;
  
  	mutex_lock(&chip->lock);
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
47
  	ret = (chip->buffer[bank] >> pin) & 0x1;
ead6db084   Miguel Gaio   gpio: add support...
48
49
50
51
52
53
54
55
  	mutex_unlock(&chip->lock);
  
  	return ret;
  }
  
  static void gen_74x164_set_value(struct gpio_chip *gc,
  		unsigned offset, int val)
  {
b2afc6f35   Linus Walleij   gpio: 74x164: use...
56
  	struct gen_74x164_chip *chip = gpiochip_get_data(gc);
902e7e600   Geert Uytterhoeven   gpio: 74x164: Use...
57
  	u8 bank = chip->registers - 1 - offset / 8;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
58
  	u8 pin = offset % 8;
ead6db084   Miguel Gaio   gpio: add support...
59
60
61
  
  	mutex_lock(&chip->lock);
  	if (val)
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
62
  		chip->buffer[bank] |= (1 << pin);
ead6db084   Miguel Gaio   gpio: add support...
63
  	else
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
64
  		chip->buffer[bank] &= ~(1 << pin);
ead6db084   Miguel Gaio   gpio: add support...
65
66
67
68
  
  	__gen_74x164_write_config(chip);
  	mutex_unlock(&chip->lock);
  }
d46ab6823   Geert Uytterhoeven   gpio: 74x164: Imp...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  static void gen_74x164_set_multiple(struct gpio_chip *gc, unsigned long *mask,
  				    unsigned long *bits)
  {
  	struct gen_74x164_chip *chip = gpiochip_get_data(gc);
  	unsigned int i, idx, shift;
  	u8 bank, bankmask;
  
  	mutex_lock(&chip->lock);
  	for (i = 0, bank = chip->registers - 1; i < chip->registers;
  	     i++, bank--) {
  		idx = i / sizeof(*mask);
  		shift = i % sizeof(*mask) * BITS_PER_BYTE;
  		bankmask = mask[idx] >> shift;
  		if (!bankmask)
  			continue;
  
  		chip->buffer[bank] &= ~bankmask;
  		chip->buffer[bank] |= bankmask & (bits[idx] >> shift);
  	}
  	__gen_74x164_write_config(chip);
  	mutex_unlock(&chip->lock);
  }
a3cc68c37   H Hartley Sweeten   gpio/74x164: remo...
91
92
93
94
95
96
  static int gen_74x164_direction_output(struct gpio_chip *gc,
  		unsigned offset, int val)
  {
  	gen_74x164_set_value(gc, offset, val);
  	return 0;
  }
3836309d9   Bill Pemberton   gpio: remove use ...
97
  static int gen_74x164_probe(struct spi_device *spi)
ead6db084   Miguel Gaio   gpio: add support...
98
99
  {
  	struct gen_74x164_chip *chip;
410f4574f   Geert Uytterhoeven   gpio: 74x164: All...
100
  	u32 nregs;
ead6db084   Miguel Gaio   gpio: add support...
101
  	int ret;
ead6db084   Miguel Gaio   gpio: add support...
102
103
104
105
106
107
108
109
  	/*
  	 * bits_per_word cannot be configured in platform data
  	 */
  	spi->bits_per_word = 8;
  
  	ret = spi_setup(spi);
  	if (ret < 0)
  		return ret;
410f4574f   Geert Uytterhoeven   gpio: 74x164: All...
110
111
112
113
114
115
116
117
118
  	if (of_property_read_u32(spi->dev.of_node, "registers-number",
  				 &nregs)) {
  		dev_err(&spi->dev,
  			"Missing registers-number property in the DT.
  ");
  		return -EINVAL;
  	}
  
  	chip = devm_kzalloc(&spi->dev, sizeof(*chip) + nregs, GFP_KERNEL);
ead6db084   Miguel Gaio   gpio: add support...
119
120
  	if (!chip)
  		return -ENOMEM;
6c0cf42be   Jingoo Han   gpio: 74x164: use...
121
  	spi_set_drvdata(spi, chip);
ead6db084   Miguel Gaio   gpio: add support...
122

a3cc68c37   H Hartley Sweeten   gpio/74x164: remo...
123
124
  	chip->gpio_chip.label = spi->modalias;
  	chip->gpio_chip.direction_output = gen_74x164_direction_output;
ead6db084   Miguel Gaio   gpio: add support...
125
126
  	chip->gpio_chip.get = gen_74x164_get_value;
  	chip->gpio_chip.set = gen_74x164_set_value;
d46ab6823   Geert Uytterhoeven   gpio: 74x164: Imp...
127
  	chip->gpio_chip.set_multiple = gen_74x164_set_multiple;
61e738040   Alexander Shiyan   gpio: 74x164: Rem...
128
  	chip->gpio_chip.base = -1;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
129

410f4574f   Geert Uytterhoeven   gpio: 74x164: All...
130
  	chip->registers = nregs;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
131
  	chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
20bc4d5d5   Maxime Ripard   gpio: 74x164: Add...
132

f6be087f8   Sandor Yu   MLK-10449-1: 74x1...
133
134
  	of_property_read_u8_array(spi->dev.of_node, "registers-default",
  				 chip->buffer, chip->registers);
9fb1f39eb   Linus Walleij   gpio/pinctrl: mak...
135
  	chip->gpio_chip.can_sleep = true;
58383c784   Linus Walleij   gpio: change memb...
136
  	chip->gpio_chip.parent = &spi->dev;
ead6db084   Miguel Gaio   gpio: add support...
137
  	chip->gpio_chip.owner = THIS_MODULE;
bcc0562c7   Alexander Shiyan   gpio: 74x164: Dri...
138
  	mutex_init(&chip->lock);
ead6db084   Miguel Gaio   gpio: add support...
139
140
141
142
143
144
  	ret = __gen_74x164_write_config(chip);
  	if (ret) {
  		dev_err(&spi->dev, "Failed writing: %d
  ", ret);
  		goto exit_destroy;
  	}
b2afc6f35   Linus Walleij   gpio: 74x164: use...
145
  	ret = gpiochip_add_data(&chip->gpio_chip, chip);
bcc0562c7   Alexander Shiyan   gpio: 74x164: Dri...
146
147
  	if (!ret)
  		return 0;
ead6db084   Miguel Gaio   gpio: add support...
148
149
  
  exit_destroy:
ead6db084   Miguel Gaio   gpio: add support...
150
  	mutex_destroy(&chip->lock);
bcc0562c7   Alexander Shiyan   gpio: 74x164: Dri...
151

ead6db084   Miguel Gaio   gpio: add support...
152
153
  	return ret;
  }
206210ce6   Bill Pemberton   gpio: remove use ...
154
  static int gen_74x164_remove(struct spi_device *spi)
ead6db084   Miguel Gaio   gpio: add support...
155
  {
bcc0562c7   Alexander Shiyan   gpio: 74x164: Dri...
156
  	struct gen_74x164_chip *chip = spi_get_drvdata(spi);
ead6db084   Miguel Gaio   gpio: add support...
157

9f5132ae8   abdoulaye berthe   gpio: remove all ...
158
159
  	gpiochip_remove(&chip->gpio_chip);
  	mutex_destroy(&chip->lock);
ead6db084   Miguel Gaio   gpio: add support...
160

9f5132ae8   abdoulaye berthe   gpio: remove all ...
161
  	return 0;
ead6db084   Miguel Gaio   gpio: add support...
162
  }
0a90a9fb4   Maxime Ripard   gpio: 74x164: Add...
163
164
  static const struct of_device_id gen_74x164_dt_ids[] = {
  	{ .compatible = "fairchild,74hc595" },
80018bd9c   Nicolas Saenz Julienne   gpio: 74x164: add...
165
  	{ .compatible = "nxp,74lvc594" },
0a90a9fb4   Maxime Ripard   gpio: 74x164: Add...
166
167
168
  	{},
  };
  MODULE_DEVICE_TABLE(of, gen_74x164_dt_ids);
ead6db084   Miguel Gaio   gpio: add support...
169
170
  static struct spi_driver gen_74x164_driver = {
  	.driver = {
a3cc68c37   H Hartley Sweeten   gpio/74x164: remo...
171
  		.name		= "74x164",
187a53a5e   Sachin Kamat   gpio: gpio-74x164...
172
  		.of_match_table	= gen_74x164_dt_ids,
ead6db084   Miguel Gaio   gpio: add support...
173
174
  	},
  	.probe		= gen_74x164_probe,
8283c4ff5   Bill Pemberton   gpio: remove use ...
175
  	.remove		= gen_74x164_remove,
ead6db084   Miguel Gaio   gpio: add support...
176
  };
ab3b87826   Maxime Ripard   gpio: 74x164: Use...
177
  module_spi_driver(gen_74x164_driver);
ead6db084   Miguel Gaio   gpio: add support...
178
179
180
181
182
  
  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");