1 /*
2  * Copyright (c) 2020 Geanix ApS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT microchip_mcp23s17
8 
9 #include <errno.h>
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/init.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/drivers/gpio.h>
16 #include <zephyr/drivers/spi.h>
17 
18 #include <zephyr/drivers/gpio/gpio_utils.h>
19 
20 #define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(gpio_mcp23s17);
23 
24 /* Register definitions */
25 #define REG_IODIR_PORTA                 0x00
26 #define REG_IODIR_PORTB                 0x01
27 #define REG_IPOL_PORTA                  0x02
28 #define REG_IPOL_PORTB                  0x03
29 #define REG_GPINTEN_PORTA               0x04
30 #define REG_GPINTEN_PORTB               0x05
31 #define REG_DEFVAL_PORTA                0x06
32 #define REG_DEFVAL_PORTB                0x07
33 #define REG_INTCON_PORTA                0x08
34 #define REG_INTCON_PORTB                0x09
35 #define REG_GPPU_PORTA                  0x0C
36 #define REG_GPPU_PORTB                  0x0D
37 #define REG_INTF_PORTA                  0x0E
38 #define REG_INTF_PORTB                  0x0F
39 #define REG_INTCAP_PORTA                0x10
40 #define REG_INTCAP_PORTB                0x11
41 #define REG_GPIO_PORTA                  0x12
42 #define REG_GPIO_PORTB                  0x13
43 #define REG_OLAT_PORTA                  0x14
44 #define REG_OLAT_PORTB                  0x15
45 
46 #define MCP23S17_ADDR                   0x40
47 #define MCP23S17_READBIT                0x01
48 
49 /** Configuration data */
50 struct mcp23s17_config {
51 	/* gpio_driver_config needs to be first */
52 	struct gpio_driver_config common;
53 
54 	struct spi_dt_spec bus;
55 };
56 
57 /** Runtime driver data */
58 struct mcp23s17_drv_data {
59 	/* gpio_driver_data needs to be first */
60 	struct gpio_driver_data data;
61 
62 	struct k_sem lock;
63 
64 	struct {
65 		uint16_t iodir;
66 		uint16_t ipol;
67 		uint16_t gpinten;
68 		uint16_t defval;
69 		uint16_t intcon;
70 		uint16_t iocon;
71 		uint16_t gppu;
72 		uint16_t intf;
73 		uint16_t intcap;
74 		uint16_t gpio;
75 		uint16_t olat;
76 	} reg_cache;
77 };
78 
read_port_regs(const struct device * dev,uint8_t reg,uint16_t * buf)79 static int read_port_regs(const struct device *dev, uint8_t reg,
80 			  uint16_t *buf)
81 {
82 	const struct mcp23s17_config *config = dev->config;
83 	int ret;
84 	uint16_t port_data;
85 
86 	uint8_t addr = MCP23S17_ADDR | MCP23S17_READBIT;
87 	uint8_t buffer_tx[4] = { addr, reg, 0, 0 };
88 
89 	const struct spi_buf tx_buf = {
90 		.buf = buffer_tx,
91 		.len = 4,
92 	};
93 	const struct spi_buf_set tx = {
94 		.buffers = &tx_buf,
95 		.count = 1,
96 	};
97 	const struct spi_buf rx_buf[2] = {
98 		{
99 			.buf = NULL,
100 			.len = 2
101 		},
102 		{
103 			.buf = (uint8_t *)&port_data,
104 			.len = 2
105 		}
106 	};
107 	const struct spi_buf_set rx = {
108 		.buffers = rx_buf,
109 		.count = ARRAY_SIZE(rx_buf),
110 	};
111 
112 	ret = spi_transceive_dt(&config->bus, &tx, &rx);
113 	if (ret) {
114 		LOG_DBG("spi_transceive FAIL %d\n", ret);
115 		return ret;
116 	}
117 
118 	*buf = sys_le16_to_cpu(port_data);
119 
120 	LOG_DBG("MCP23S17: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X",
121 		reg, (*buf & 0xFF), (reg + 1), (*buf >> 8));
122 
123 	return 0;
124 }
125 
write_port_regs(const struct device * dev,uint8_t reg,uint16_t value)126 static int write_port_regs(const struct device *dev, uint8_t reg,
127 			   uint16_t value)
128 {
129 	const struct mcp23s17_config *config = dev->config;
130 	int ret;
131 	uint16_t port_data;
132 
133 	LOG_DBG("MCP23S17: Write: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X",
134 		reg, (value & 0xFF), (reg + 1), (value >> 8));
135 
136 	port_data = sys_cpu_to_le16(value);
137 
138 	uint8_t addr = MCP23S17_ADDR;
139 	uint8_t buffer_tx[2] = { addr, reg };
140 
141 	const struct spi_buf tx_buf[2] = {
142 		{
143 			.buf = buffer_tx,
144 			.len = 2,
145 		},
146 		{
147 			.buf = (uint8_t *)&port_data,
148 			.len = 2,
149 		}
150 	};
151 	const struct spi_buf_set tx = {
152 		.buffers = tx_buf,
153 		.count = ARRAY_SIZE(tx_buf),
154 	};
155 
156 	ret = spi_write_dt(&config->bus, &tx);
157 	if (ret) {
158 		LOG_DBG("spi_write FAIL %d\n", ret);
159 		return ret;
160 	}
161 
162 	return 0;
163 }
164 
setup_pin_dir(const struct device * dev,uint32_t pin,int flags)165 static int setup_pin_dir(const struct device *dev, uint32_t pin, int flags)
166 {
167 	struct mcp23s17_drv_data *drv_data = dev->data;
168 	uint16_t *dir = &drv_data->reg_cache.iodir;
169 	uint16_t *output = &drv_data->reg_cache.gpio;
170 	int ret;
171 
172 	if ((flags & GPIO_OUTPUT) != 0U) {
173 		if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) {
174 			*output |= BIT(pin);
175 		} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) {
176 			*output &= ~BIT(pin);
177 		}
178 		*dir &= ~BIT(pin);
179 	} else {
180 		*dir |= BIT(pin);
181 	}
182 
183 	ret = write_port_regs(dev, REG_GPIO_PORTA, *output);
184 	if (ret != 0) {
185 		return ret;
186 	}
187 
188 	ret = write_port_regs(dev, REG_IODIR_PORTA, *dir);
189 
190 	return ret;
191 }
192 
setup_pin_pullupdown(const struct device * dev,uint32_t pin,int flags)193 static int setup_pin_pullupdown(const struct device *dev, uint32_t pin,
194 				int flags)
195 {
196 	struct mcp23s17_drv_data *drv_data = dev->data;
197 	uint16_t port;
198 	int ret;
199 
200 	/* Setup pin pull up or pull down */
201 	port = drv_data->reg_cache.gppu;
202 
203 	/* pull down == 0, pull up == 1 */
204 	if ((flags & GPIO_PULL_DOWN) != 0U) {
205 		return -ENOTSUP;
206 	}
207 
208 	WRITE_BIT(port, pin, (flags & GPIO_PULL_UP) != 0U);
209 
210 	ret = write_port_regs(dev, REG_GPPU_PORTA, port);
211 	if (ret == 0) {
212 		drv_data->reg_cache.gppu = port;
213 	}
214 
215 	return ret;
216 }
217 
mcp23s17_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)218 static int mcp23s17_config(const struct device *dev,
219 			   gpio_pin_t pin, gpio_flags_t flags)
220 {
221 	struct mcp23s17_drv_data *drv_data = dev->data;
222 	int ret;
223 
224 	/* Can't do SPI bus operations from an ISR */
225 	if (k_is_in_isr()) {
226 		return -EWOULDBLOCK;
227 	}
228 
229 	k_sem_take(&drv_data->lock, K_FOREVER);
230 
231 	if ((flags & GPIO_OPEN_DRAIN) != 0U) {
232 		ret = -ENOTSUP;
233 		goto done;
234 	}
235 
236 	ret = setup_pin_dir(dev, pin, flags);
237 	if (ret) {
238 		LOG_ERR("MCP23S17: error setting pin direction (%d)", ret);
239 		goto done;
240 	}
241 
242 	ret = setup_pin_pullupdown(dev, pin, flags);
243 	if (ret) {
244 		LOG_ERR("MCP23S17: error setting pin pull up/down (%d)", ret);
245 		goto done;
246 	}
247 
248 done:
249 	k_sem_give(&drv_data->lock);
250 	return ret;
251 }
252 
mcp23s17_port_get_raw(const struct device * dev,uint32_t * value)253 static int mcp23s17_port_get_raw(const struct device *dev, uint32_t *value)
254 {
255 	struct mcp23s17_drv_data *drv_data = dev->data;
256 	uint16_t buf;
257 	int ret;
258 
259 	/* Can't do SPI bus operations from an ISR */
260 	if (k_is_in_isr()) {
261 		return -EWOULDBLOCK;
262 	}
263 
264 	k_sem_take(&drv_data->lock, K_FOREVER);
265 
266 	ret = read_port_regs(dev, REG_GPIO_PORTA, &buf);
267 	if (ret != 0) {
268 		goto done;
269 	}
270 
271 	*value = buf;
272 
273 done:
274 	k_sem_give(&drv_data->lock);
275 	return ret;
276 }
277 
mcp23s17_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)278 static int mcp23s17_port_set_masked_raw(const struct device *dev,
279 					uint32_t mask, uint32_t value)
280 {
281 	struct mcp23s17_drv_data *drv_data = dev->data;
282 	uint16_t buf;
283 	int ret;
284 
285 	/* Can't do SPI bus operations from an ISR */
286 	if (k_is_in_isr()) {
287 		return -EWOULDBLOCK;
288 	}
289 
290 	k_sem_take(&drv_data->lock, K_FOREVER);
291 
292 	buf = drv_data->reg_cache.gpio;
293 	buf = (buf & ~mask) | (mask & value);
294 
295 	ret = write_port_regs(dev, REG_GPIO_PORTA, buf);
296 	if (ret == 0) {
297 		drv_data->reg_cache.gpio = buf;
298 	}
299 
300 	k_sem_give(&drv_data->lock);
301 
302 	return ret;
303 }
304 
mcp23s17_port_set_bits_raw(const struct device * dev,uint32_t mask)305 static int mcp23s17_port_set_bits_raw(const struct device *dev, uint32_t mask)
306 {
307 	return mcp23s17_port_set_masked_raw(dev, mask, mask);
308 }
309 
mcp23s17_port_clear_bits_raw(const struct device * dev,uint32_t mask)310 static int mcp23s17_port_clear_bits_raw(const struct device *dev,
311 					uint32_t mask)
312 {
313 	return mcp23s17_port_set_masked_raw(dev, mask, 0);
314 }
315 
mcp23s17_port_toggle_bits(const struct device * dev,uint32_t mask)316 static int mcp23s17_port_toggle_bits(const struct device *dev, uint32_t mask)
317 {
318 	struct mcp23s17_drv_data *const drv_data =
319 		(struct mcp23s17_drv_data *const)dev->data;
320 	uint16_t buf;
321 	int ret;
322 
323 	/* Can't do SPI bus operations from an ISR */
324 	if (k_is_in_isr()) {
325 		return -EWOULDBLOCK;
326 	}
327 
328 	k_sem_take(&drv_data->lock, K_FOREVER);
329 
330 	buf = drv_data->reg_cache.gpio;
331 	buf ^= mask;
332 
333 	ret = write_port_regs(dev, REG_GPIO_PORTA, buf);
334 	if (ret == 0) {
335 		drv_data->reg_cache.gpio = buf;
336 	}
337 
338 	k_sem_give(&drv_data->lock);
339 
340 	return ret;
341 }
342 
343 static const struct gpio_driver_api api_table = {
344 	.pin_configure = mcp23s17_config,
345 	.port_get_raw = mcp23s17_port_get_raw,
346 	.port_set_masked_raw = mcp23s17_port_set_masked_raw,
347 	.port_set_bits_raw = mcp23s17_port_set_bits_raw,
348 	.port_clear_bits_raw = mcp23s17_port_clear_bits_raw,
349 	.port_toggle_bits = mcp23s17_port_toggle_bits,
350 };
351 
mcp23s17_init(const struct device * dev)352 static int mcp23s17_init(const struct device *dev)
353 {
354 	const struct mcp23s17_config *config = dev->config;
355 	struct mcp23s17_drv_data *drv_data = dev->data;
356 
357 	if (!spi_is_ready_dt(&config->bus)) {
358 		LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
359 		return -ENODEV;
360 	}
361 
362 	k_sem_init(&drv_data->lock, 1, 1);
363 
364 	return 0;
365 }
366 
367 #define MCP23S17_INIT(inst)						 \
368 	static const struct mcp23s17_config mcp23s17_##inst##_config = { \
369 		.common = {						 \
370 			.port_pin_mask =				 \
371 				GPIO_PORT_PIN_MASK_FROM_DT_INST(inst),	 \
372 		},							 \
373 		.bus = SPI_DT_SPEC_INST_GET(				 \
374 			inst,						 \
375 			SPI_OP_MODE_MASTER | SPI_MODE_CPOL |		 \
376 			SPI_MODE_CPHA | SPI_WORD_SET(8), 0),		 \
377 	};								 \
378 									 \
379 	static struct mcp23s17_drv_data mcp23s17_##inst##_drvdata = {	 \
380 		/* Default for registers according to datasheet */	 \
381 		.reg_cache.iodir = 0xFFFF,				 \
382 		.reg_cache.ipol = 0x0,					 \
383 		.reg_cache.gpinten = 0x0,				 \
384 		.reg_cache.defval = 0x0,				 \
385 		.reg_cache.intcon = 0x0,				 \
386 		.reg_cache.iocon = 0x0,					 \
387 		.reg_cache.gppu = 0x0,					 \
388 		.reg_cache.intf = 0x0,					 \
389 		.reg_cache.intcap = 0x0,				 \
390 		.reg_cache.gpio = 0x0,					 \
391 		.reg_cache.olat = 0x0,					 \
392 	};								 \
393 									 \
394 	/* This has to init after SPI master */				 \
395 	DEVICE_DT_INST_DEFINE(inst, mcp23s17_init,			 \
396 			      NULL,					 \
397 			      &mcp23s17_##inst##_drvdata,		 \
398 			      &mcp23s17_##inst##_config,		 \
399 			      POST_KERNEL,				 \
400 			      CONFIG_GPIO_MCP23S17_INIT_PRIORITY,	 \
401 			      &api_table);
402 
403 DT_INST_FOREACH_STATUS_OKAY(MCP23S17_INIT)
404