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 MPC23Sxx SPI-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/spi.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_mcp23sxx);
26
mcp23sxx_read_port_regs(const struct device * dev,uint8_t reg,uint16_t * buf)27 static int mcp23sxx_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 uint8_t addr = MCP23SXX_ADDR | MCP23SXX_READBIT;
36 uint8_t buffer_tx[4] = { addr, reg, 0, 0 };
37 uint8_t buffer_rx[4] = { 0 };
38
39 const struct spi_buf tx_buf = {
40 .buf = buffer_tx,
41 .len = 4,
42 };
43 const struct spi_buf_set tx = {
44 .buffers = &tx_buf,
45 .count = 1,
46 };
47 const struct spi_buf rx_buf = {
48 .buf = buffer_rx,
49 .len = 2 + nread,
50 };
51 const struct spi_buf_set rx = {
52 .buffers = &rx_buf,
53 .count = 1,
54 };
55
56 ret = spi_transceive_dt(&config->bus.spi, &tx, &rx);
57 if (ret < 0) {
58 LOG_ERR("spi_transceive FAIL %d\n", ret);
59 return ret;
60 }
61
62 port_data = ((uint16_t)buffer_rx[3] << 8 | buffer_rx[2]);
63
64 *buf = sys_le16_to_cpu(port_data);
65
66 return 0;
67 }
68
mcp23sxx_write_port_regs(const struct device * dev,uint8_t reg,uint16_t value)69 static int mcp23sxx_write_port_regs(const struct device *dev, uint8_t reg, uint16_t value)
70 {
71 const struct mcp23xxx_config *config = dev->config;
72 int ret;
73
74 uint8_t nwrite = (config->ngpios == 8) ? 1 : 2;
75 uint16_t port_data = sys_cpu_to_le16(value);
76 uint8_t port_a_data = port_data & 0xFF;
77 uint8_t port_b_data = (port_data >> 8) & 0xFF;
78
79 port_data = sys_cpu_to_le16(value);
80
81 uint8_t addr = MCP23SXX_ADDR;
82 uint8_t buffer_tx[4] = { addr, reg, port_a_data, port_b_data };
83
84 const struct spi_buf tx_buf[1] = {
85 {
86 .buf = buffer_tx,
87 .len = nwrite + 2,
88 }
89 };
90 const struct spi_buf_set tx = {
91 .buffers = tx_buf,
92 .count = ARRAY_SIZE(tx_buf),
93 };
94
95 ret = spi_write_dt(&config->bus.spi, &tx);
96 if (ret < 0) {
97 LOG_ERR("spi_write FAIL %d\n", ret);
98 return ret;
99 }
100
101 return 0;
102 }
103
mcp23sxx_bus_is_ready(const struct device * dev)104 static int mcp23sxx_bus_is_ready(const struct device *dev)
105 {
106 const struct mcp23xxx_config *config = dev->config;
107
108 if (!spi_is_ready_dt(&config->bus.spi)) {
109 LOG_ERR("SPI bus %s not ready", config->bus.spi.bus->name);
110 return -ENODEV;
111 }
112
113 return 0;
114 }
115
116 #define DT_DRV_COMPAT microchip_mcp23sxx
117
118 #define GPIO_MCP23SXX_DEVICE(inst) \
119 static struct mcp23xxx_drv_data mcp23sxx_##inst##_drvdata = { \
120 /* Default for registers according to datasheet */ \
121 .reg_cache.iodir = 0xFFFF, .reg_cache.ipol = 0x0, .reg_cache.gpinten = 0x0, \
122 .reg_cache.defval = 0x0, .reg_cache.intcon = 0x0, .reg_cache.iocon = 0x0, \
123 .reg_cache.gppu = 0x0, .reg_cache.intf = 0x0, .reg_cache.intcap = 0x0, \
124 .reg_cache.gpio = 0x0, .reg_cache.olat = 0x0, \
125 }; \
126 static struct mcp23xxx_config mcp23sxx_##inst##_config = { \
127 .config = { \
128 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \
129 }, \
130 .bus = { \
131 .spi = SPI_DT_SPEC_INST_GET(inst, \
132 SPI_OP_MODE_MASTER | SPI_MODE_CPOL | \
133 SPI_MODE_CPHA | SPI_WORD_SET(8), 0) \
134 }, \
135 .gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
136 .gpio_reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \
137 .ngpios = DT_INST_PROP(inst, ngpios), \
138 .read_fn = mcp23sxx_read_port_regs, \
139 .write_fn = mcp23sxx_write_port_regs, \
140 .bus_fn = mcp23sxx_bus_is_ready \
141 }; \
142 DEVICE_DT_INST_DEFINE(inst, gpio_mcp23xxx_init, NULL, &mcp23sxx_##inst##_drvdata, \
143 &mcp23sxx_##inst##_config, POST_KERNEL, \
144 CONFIG_GPIO_MCP23SXX_INIT_PRIORITY, &gpio_mcp23xxx_api_table);
145
146 DT_INST_FOREACH_STATUS_OKAY(GPIO_MCP23SXX_DEVICE)
147