1 /*
2  *
3  * Copyright (c) 2021 metraTec GmbH
4  * Copyright (c) 2021 Peter Johanson
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 /**
10  * @file Driver for MPC230xx I2C-based GPIO driver.
11  */
12 
13 #include <errno.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/device.h>
16 #include <zephyr/sys/byteorder.h>
17 #include <zephyr/drivers/gpio.h>
18 #include <zephyr/drivers/i2c.h>
19 
20 #include <zephyr/drivers/gpio/gpio_utils.h>
21 #include "gpio_mcp23xxx.h"
22 
23 #define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
24 #include <zephyr/logging/log.h>
25 LOG_MODULE_REGISTER(gpio_mcp230xx);
26 
mcp230xx_read_port_regs(const struct device * dev,uint8_t reg,uint16_t * buf)27 static int mcp230xx_read_port_regs(const struct device *dev, uint8_t reg, uint16_t *buf)
28 {
29 	const struct mcp23xxx_config *config = dev->config;
30 	uint16_t port_data = 0;
31 	int ret;
32 
33 	uint8_t nread = (config->ngpios == 8) ? 1 : 2;
34 
35 	ret = i2c_burst_read_dt(&config->bus.i2c, reg, (uint8_t *)&port_data, nread);
36 	if (ret < 0) {
37 		LOG_ERR("i2c_read failed!");
38 		return ret;
39 	}
40 
41 	*buf = sys_le16_to_cpu(port_data);
42 
43 	return 0;
44 }
45 
mcp230xx_write_port_regs(const struct device * dev,uint8_t reg,uint16_t value)46 static int mcp230xx_write_port_regs(const struct device *dev, uint8_t reg, uint16_t value)
47 {
48 	const struct mcp23xxx_config *config = dev->config;
49 	int ret;
50 
51 	uint8_t nwrite = (config->ngpios == 8) ? 2 : 3;
52 	uint8_t buf[3];
53 
54 	buf[0] = reg;
55 	sys_put_le16(value, &buf[1]);
56 
57 	ret = i2c_write_dt(&config->bus.i2c, buf, nwrite);
58 	if (ret < 0) {
59 		LOG_ERR("i2c_write failed!");
60 		return ret;
61 	}
62 
63 	return 0;
64 }
65 
mcp230xx_bus_is_ready(const struct device * dev)66 static int mcp230xx_bus_is_ready(const struct device *dev)
67 {
68 	const struct mcp23xxx_config *config = dev->config;
69 
70 	if (!device_is_ready(config->bus.i2c.bus)) {
71 		LOG_ERR("I2C bus %s not ready", config->bus.i2c.bus->name);
72 		return -ENODEV;
73 	}
74 
75 	return 0;
76 }
77 
78 #define DT_DRV_COMPAT microchip_mcp230xx
79 
80 #define GPIO_MCP230XX_DEVICE(inst)                                                                 \
81 	static struct mcp23xxx_drv_data mcp230xx_##inst##_drvdata = {                              \
82 		/* Default for registers according to datasheet */                                 \
83 		.reg_cache.iodir = 0xFFFF, .reg_cache.ipol = 0x0,   .reg_cache.gpinten = 0x0,      \
84 		.reg_cache.defval = 0x0,   .reg_cache.intcon = 0x0, .reg_cache.iocon = 0x0,        \
85 		.reg_cache.gppu = 0x0,     .reg_cache.intf = 0x0,   .reg_cache.intcap = 0x0,       \
86 		.reg_cache.gpio = 0x0,     .reg_cache.olat = 0x0,                                  \
87 	};                                                                                         \
88 	static const struct mcp23xxx_config mcp230xx_##inst##_config = {                           \
89 		.config = {                                                                        \
90 			.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst),                    \
91 		},                                                                                 \
92 		.bus = {                                                                           \
93 			.i2c = I2C_DT_SPEC_INST_GET(inst),                                         \
94 		},                                                                                 \
95 		.gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}),                        \
96 		.gpio_reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}),                    \
97 		.ngpios =  DT_INST_PROP(inst, ngpios),                                             \
98 		.read_fn = mcp230xx_read_port_regs,                                                \
99 		.write_fn = mcp230xx_write_port_regs,                                              \
100 		.bus_fn = mcp230xx_bus_is_ready,                                                   \
101 	};                                                                                         \
102 	DEVICE_DT_INST_DEFINE(inst, gpio_mcp23xxx_init, NULL, &mcp230xx_##inst##_drvdata,          \
103 		&mcp230xx_##inst##_config, POST_KERNEL,                                            \
104 		CONFIG_GPIO_MCP230XX_INIT_PRIORITY, &gpio_mcp23xxx_api_table);
105 
106 DT_INST_FOREACH_STATUS_OKAY(GPIO_MCP230XX_DEVICE)
107