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 GPIO_MCP230XX_DEVICE(inst, num_gpios, open_drain, model) \
79 static struct mcp23xxx_drv_data mcp##model##_##inst##_drvdata = { \
80 /* Default for registers according to datasheet */ \
81 .reg_cache.iodir = 0xFFFF, .reg_cache.ipol = 0x0, .reg_cache.gpinten = 0x0, \
82 .reg_cache.defval = 0x0, .reg_cache.intcon = 0x0, .reg_cache.iocon = 0x0, \
83 .reg_cache.gppu = 0x0, .reg_cache.intf = 0x0, .reg_cache.intcap = 0x0, \
84 .reg_cache.gpio = 0x0, .reg_cache.olat = 0x0, \
85 }; \
86 static const struct mcp23xxx_config mcp##model##_##inst##_config = { \
87 .config = { \
88 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \
89 }, \
90 .bus = { \
91 .i2c = I2C_DT_SPEC_INST_GET(inst), \
92 }, \
93 .gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
94 .gpio_reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \
95 .ngpios = num_gpios, \
96 .is_open_drain = open_drain, \
97 .read_fn = mcp230xx_read_port_regs, \
98 .write_fn = mcp230xx_write_port_regs, \
99 .bus_fn = mcp230xx_bus_is_ready, \
100 }; \
101 DEVICE_DT_INST_DEFINE(inst, gpio_mcp23xxx_init, NULL, &mcp##model##_##inst##_drvdata, \
102 &mcp##model##_##inst##_config, POST_KERNEL, \
103 CONFIG_GPIO_MCP230XX_INIT_PRIORITY, &gpio_mcp23xxx_api_table);
104
105 #define DT_DRV_COMPAT microchip_mcp23008
106 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_MCP230XX_DEVICE, 8, false, 23008)
107 #undef DT_DRV_COMPAT
108 #define DT_DRV_COMPAT microchip_mcp23009
109 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_MCP230XX_DEVICE, 8, true, 23009)
110 #undef DT_DRV_COMPAT
111 #define DT_DRV_COMPAT microchip_mcp23016
112 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_MCP230XX_DEVICE, 16, false, 23016)
113 #undef DT_DRV_COMPAT
114 #define DT_DRV_COMPAT microchip_mcp23017
115 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_MCP230XX_DEVICE, 16, false, 23017)
116 #undef DT_DRV_COMPAT
117 #define DT_DRV_COMPAT microchip_mcp23018
118 DT_INST_FOREACH_STATUS_OKAY_VARGS(GPIO_MCP230XX_DEVICE, 16, true, 23018)
119 #undef DT_DRV_COMPAT
120