Blame view

drivers/gpio/gpio-max730x.c 5.66 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
e952805d2   Wolfram Sang   gpio: add driver ...
2
  /**
e952805d2   Wolfram Sang   gpio: add driver ...
3
4
5
6
   * Copyright (C) 2006 Juergen Beisert, Pengutronix
   * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix
   * Copyright (C) 2009 Wolfram Sang, Pengutronix
   *
e952805d2   Wolfram Sang   gpio: add driver ...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
   * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are
   * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more
   * details
   * Note:
   * - DIN must be stable at the rising edge of clock.
   * - when writing:
   *   - always clock in 16 clocks at once
   *   - at DIN: D15 first, D0 last
   *   - D0..D7 = databyte, D8..D14 = commandbyte
   *   - D15 = low -> write command
   * - when reading
   *   - always clock in 16 clocks at once
   *   - at DIN: D15 first, D0 last
   *   - D0..D7 = dummy, D8..D14 = register address
   *   - D15 = high -> read command
   *   - raise CS and assert it again
   *   - always clock in 16 clocks at once
   *   - at DOUT: D15 first, D0 last
   *   - D0..D7 contains the data from the first cycle
   *
   * The driver exports a standard gpiochip interface
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/platform_device.h>
  #include <linux/mutex.h>
  #include <linux/spi/max7301.h>
25424b864   Linus Walleij   gpio: max730x: In...
35
  #include <linux/gpio/driver.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
36
  #include <linux/slab.h>
e952805d2   Wolfram Sang   gpio: add driver ...
37
38
39
40
41
42
43
44
45
46
47
48
49
  
  /*
   * Pin configurations, see MAX7301 datasheet page 6
   */
  #define PIN_CONFIG_MASK 0x03
  #define PIN_CONFIG_IN_PULLUP 0x03
  #define PIN_CONFIG_IN_WO_PULLUP 0x02
  #define PIN_CONFIG_OUT 0x01
  
  #define PIN_NUMBER 28
  
  static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)
  {
5e45e0191   Linus Walleij   gpio: max730x: us...
50
  	struct max7301 *ts = gpiochip_get_data(chip);
e952805d2   Wolfram Sang   gpio: add driver ...
51
  	u8 *config;
4a22b8a4a   Marc Kleine-Budde   gpio: max730x: ma...
52
  	u8 offset_bits, pin_config;
e952805d2   Wolfram Sang   gpio: add driver ...
53
54
55
56
57
58
59
  	int ret;
  
  	/* First 4 pins are unused in the controller */
  	offset += 4;
  	offset_bits = (offset & 3) << 1;
  
  	config = &ts->port_config[offset >> 2];
4a22b8a4a   Marc Kleine-Budde   gpio: max730x: ma...
60
61
62
63
  	if (ts->input_pullup_active & BIT(offset))
  		pin_config = PIN_CONFIG_IN_PULLUP;
  	else
  		pin_config = PIN_CONFIG_IN_WO_PULLUP;
e952805d2   Wolfram Sang   gpio: add driver ...
64
  	mutex_lock(&ts->lock);
e952805d2   Wolfram Sang   gpio: add driver ...
65
  	*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
4a22b8a4a   Marc Kleine-Budde   gpio: max730x: ma...
66
  			   | (pin_config << offset_bits);
e952805d2   Wolfram Sang   gpio: add driver ...
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
  
  	ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
  
  	mutex_unlock(&ts->lock);
  
  	return ret;
  }
  
  static int __max7301_set(struct max7301 *ts, unsigned offset, int value)
  {
  	if (value) {
  		ts->out_level |= 1 << offset;
  		return ts->write(ts->dev, 0x20 + offset, 0x01);
  	} else {
  		ts->out_level &= ~(1 << offset);
  		return ts->write(ts->dev, 0x20 + offset, 0x00);
  	}
  }
  
  static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,
  				    int value)
  {
5e45e0191   Linus Walleij   gpio: max730x: us...
89
  	struct max7301 *ts = gpiochip_get_data(chip);
e952805d2   Wolfram Sang   gpio: add driver ...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  	u8 *config;
  	u8 offset_bits;
  	int ret;
  
  	/* First 4 pins are unused in the controller */
  	offset += 4;
  	offset_bits = (offset & 3) << 1;
  
  	config = &ts->port_config[offset >> 2];
  
  	mutex_lock(&ts->lock);
  
  	*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))
  			   | (PIN_CONFIG_OUT << offset_bits);
  
  	ret = __max7301_set(ts, offset, value);
  
  	if (!ret)
  		ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);
  
  	mutex_unlock(&ts->lock);
  
  	return ret;
  }
  
  static int max7301_get(struct gpio_chip *chip, unsigned offset)
  {
5e45e0191   Linus Walleij   gpio: max730x: us...
117
  	struct max7301 *ts = gpiochip_get_data(chip);
e952805d2   Wolfram Sang   gpio: add driver ...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  	int config, level = -EINVAL;
  
  	/* First 4 pins are unused in the controller */
  	offset += 4;
  
  	mutex_lock(&ts->lock);
  
  	config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1))
  			& PIN_CONFIG_MASK;
  
  	switch (config) {
  	case PIN_CONFIG_OUT:
  		/* Output: return cached level */
  		level =  !!(ts->out_level & (1 << offset));
  		break;
  	case PIN_CONFIG_IN_WO_PULLUP:
  	case PIN_CONFIG_IN_PULLUP:
  		/* Input: read out */
  		level = ts->read(ts->dev, 0x20 + offset) & 0x01;
  	}
  	mutex_unlock(&ts->lock);
  
  	return level;
  }
  
  static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
  {
5e45e0191   Linus Walleij   gpio: max730x: us...
145
  	struct max7301 *ts = gpiochip_get_data(chip);
e952805d2   Wolfram Sang   gpio: add driver ...
146
147
148
149
150
151
152
153
154
155
  
  	/* First 4 pins are unused in the controller */
  	offset += 4;
  
  	mutex_lock(&ts->lock);
  
  	__max7301_set(ts, offset, value);
  
  	mutex_unlock(&ts->lock);
  }
3836309d9   Bill Pemberton   gpio: remove use ...
156
  int __max730x_probe(struct max7301 *ts)
e952805d2   Wolfram Sang   gpio: add driver ...
157
158
159
160
  {
  	struct device *dev = ts->dev;
  	struct max7301_platform_data *pdata;
  	int i, ret;
e56aee189   Jingoo Han   gpio: use dev_get...
161
  	pdata = dev_get_platdata(dev);
e952805d2   Wolfram Sang   gpio: add driver ...
162
163
164
165
166
167
  
  	mutex_init(&ts->lock);
  	dev_set_drvdata(dev, ts);
  
  	/* Power up the chip and disable IRQ output */
  	ts->write(dev, 0x04, 0x01);
8754fccba   Roland Stigge   gpio: gpio-max710...
168
169
170
171
172
173
  	if (pdata) {
  		ts->input_pullup_active = pdata->input_pullup_active;
  		ts->chip.base = pdata->base;
  	} else {
  		ts->chip.base = -1;
  	}
e952805d2   Wolfram Sang   gpio: add driver ...
174
175
176
177
178
179
  	ts->chip.label = dev->driver->name;
  
  	ts->chip.direction_input = max7301_direction_input;
  	ts->chip.get = max7301_get;
  	ts->chip.direction_output = max7301_direction_output;
  	ts->chip.set = max7301_set;
e952805d2   Wolfram Sang   gpio: add driver ...
180
  	ts->chip.ngpio = PIN_NUMBER;
9fb1f39eb   Linus Walleij   gpio/pinctrl: mak...
181
  	ts->chip.can_sleep = true;
58383c784   Linus Walleij   gpio: change memb...
182
  	ts->chip.parent = dev;
e952805d2   Wolfram Sang   gpio: add driver ...
183
  	ts->chip.owner = THIS_MODULE;
6f4deb18a   Christophe Leroy   gpio: max730x: se...
184
185
186
  	ret = gpiochip_add_data(&ts->chip, ts);
  	if (ret)
  		goto exit_destroy;
e952805d2   Wolfram Sang   gpio: add driver ...
187
  	/*
4a22b8a4a   Marc Kleine-Budde   gpio: max730x: ma...
188
  	 * initialize pullups according to platform data and cache the
e952805d2   Wolfram Sang   gpio: add driver ...
189
190
191
192
  	 * register values for later use.
  	 */
  	for (i = 1; i < 8; i++) {
  		int j;
4a22b8a4a   Marc Kleine-Budde   gpio: max730x: ma...
193
194
195
196
197
198
  		/*
  		 * initialize port_config with "0xAA", which means
  		 * input with internal pullup disabled. This is needed
  		 * to avoid writing zeros (in the inner for loop),
  		 * which is not allowed according to the datasheet.
  		 */
e952805d2   Wolfram Sang   gpio: add driver ...
199
200
201
202
203
204
205
206
  		ts->port_config[i] = 0xAA;
  		for (j = 0; j < 4; j++) {
  			int offset = (i - 1) * 4 + j;
  			ret = max7301_direction_input(&ts->chip, offset);
  			if (ret)
  				goto exit_destroy;
  		}
  	}
e952805d2   Wolfram Sang   gpio: add driver ...
207
208
209
  	return ret;
  
  exit_destroy:
e952805d2   Wolfram Sang   gpio: add driver ...
210
211
212
213
  	mutex_destroy(&ts->lock);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(__max730x_probe);
206210ce6   Bill Pemberton   gpio: remove use ...
214
  int __max730x_remove(struct device *dev)
e952805d2   Wolfram Sang   gpio: add driver ...
215
216
  {
  	struct max7301 *ts = dev_get_drvdata(dev);
e952805d2   Wolfram Sang   gpio: add driver ...
217
218
219
  
  	if (ts == NULL)
  		return -ENODEV;
e952805d2   Wolfram Sang   gpio: add driver ...
220
221
  	/* Power down the chip and disable IRQ output */
  	ts->write(dev, 0x04, 0x00);
9f5132ae8   abdoulaye berthe   gpio: remove all ...
222
223
  	gpiochip_remove(&ts->chip);
  	mutex_destroy(&ts->lock);
9f5132ae8   abdoulaye berthe   gpio: remove all ...
224
  	return 0;
e952805d2   Wolfram Sang   gpio: add driver ...
225
226
  }
  EXPORT_SYMBOL_GPL(__max730x_remove);
06ca02b06   Richard Röjfors   drivers/gpio/max7...
227
228
229
230
  
  MODULE_AUTHOR("Juergen Beisert, Wolfram Sang");
  MODULE_LICENSE("GPL v2");
  MODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts");