Skip to content

Commit 5af7550

Browse files
committed
WIP: Add MFD seesaw
Signed-off-by: Titouan Christophe <[email protected]>
1 parent 29197ac commit 5af7550

File tree

6 files changed

+269
-0
lines changed

6 files changed

+269
-0
lines changed

drivers/mfd/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ zephyr_library_sources_ifdef(CONFIG_MFD_AW9523B mfd_aw9523b.c)
2424
zephyr_library_sources_ifdef(CONFIG_MFD_DS3231 mfd_ds3231.c)
2525
zephyr_library_sources_ifdef(CONFIG_MFD_MAX22017 mfd_max22017.c)
2626
zephyr_library_sources_ifdef(CONFIG_MFD_PF1550 mfd_pf1550.c)
27+
zephyr_library_sources_ifdef(CONFIG_MFD_SEESAW mfd_seesaw.c)

drivers/mfd/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ source "drivers/mfd/Kconfig.pf1550"
3636
source "drivers/mfd/Kconfig.lpflexcomm"
3737
source "drivers/mfd/Kconfig.tle9104"
3838
source "drivers/mfd/Kconfig.it8801"
39+
source "drivers/mfd/Kconfig.seesaw"
3940

4041
endif # MFD

drivers/mfd/Kconfig.seesaw

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2025 Titouan Christophe
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config MFD_SEESAW
5+
bool "Adafruit Seesaw multifunction device"
6+
default y
7+
depends on DT_HAS_ADAFRUIT_SEESAW_ENABLED
8+
help
9+
Enable driver for Adafruit Seesaw.
10+
11+
if MFD_SEESAW
12+
13+
module = MFD_SEESAW
14+
module-str = seesaw
15+
source "subsys/logging/Kconfig.template.log_config"
16+
17+
endif

drivers/mfd/mfd_seesaw.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) 2025 Titouan Christophe
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT adafruit_seesaw
8+
9+
#include <zephyr/drivers/i2c.h>
10+
#include <zephyr/drivers/mfd/mfd_seesaw.h>
11+
#include <zephyr/logging/log.h>
12+
13+
LOG_MODULE_REGISTER(mfd_seesaw, CONFIG_MFD_SEESAW_LOG_LEVEL);
14+
15+
static const char * const module_names[] = {
16+
[SEESAW_MOD_STATUS] = "STATUS",
17+
[SEESAW_MOD_GPIO] = "GPIO",
18+
[SEESAW_MOD_SERCOM0] = "SERCOM0",
19+
[SEESAW_MOD_SERCOM1] = "SERCOM1",
20+
[SEESAW_MOD_SERCOM2] = "SERCOM2",
21+
[SEESAW_MOD_SERCOM3] = "SERCOM3",
22+
[SEESAW_MOD_SERCOM4] = "SERCOM4",
23+
[SEESAW_MOD_SERCOM5] = "SERCOM5",
24+
[SEESAW_MOD_PWM] = "PWM",
25+
[SEESAW_MOD_ADC] = "ADC",
26+
[SEESAW_MOD_DAC] = "DAC",
27+
[SEESAW_MOD_INTERRUPT] = "INTERRUPT",
28+
[SEESAW_MOD_DAP] = "DAP",
29+
[SEESAW_MOD_EEPROM] = "EEPROM",
30+
[SEESAW_MOD_NEOPIXEL] = "NEOPIXEL",
31+
[SEESAW_MOD_TOUCH] = "TOUCH",
32+
[SEESAW_MOD_KEYPAD] = "KEYPAD",
33+
[SEESAW_MOD_ENCODER] = "ENCODER",
34+
};
35+
36+
#define SEESAW_MODULE_NAME(_mod) \
37+
((_mod) < SEESAW_MOD_MAX_NUM) ? module_names[_mod] : "(unknown)"
38+
39+
enum seesaw_mod_status {
40+
STATUS_HW_ID = 0x01,
41+
STATUS_VERSION = 0x02,
42+
STATUS_OPTIONS = 0x03,
43+
STATUS_TEMP = 0x04,
44+
STATUS_SWRST = 0x7F,
45+
};
46+
47+
struct seesaw_config {
48+
struct i2c_dt_spec i2c;
49+
};
50+
51+
struct seesaw_data {
52+
struct k_sem sem;
53+
uint32_t modules;
54+
const struct device *claimed_modules[SEESAW_MOD_MAX_NUM];
55+
};
56+
57+
int seesaw_claim_module(const struct device *dev, uint32_t module,
58+
const struct device *subdevice)
59+
{
60+
struct seesaw_data *drv_data = dev->data;
61+
62+
if (module > SEESAW_MOD_MAX_NUM) {
63+
LOG_ERR("Cannot claim module %d: invalid module number", module);
64+
return -EINVAL;
65+
}
66+
67+
if (!(drv_data->modules & BIT(module))) {
68+
LOG_ERR("Cannot claim %s: not implemented", SEESAW_MODULE_NAME(module));
69+
return -ENOTSUP;
70+
}
71+
72+
if (drv_data->claimed_modules[module]) {
73+
LOG_ERR("Cannot claim %s: already in use", SEESAW_MODULE_NAME(module));
74+
return -EBUSY;
75+
}
76+
77+
drv_data->claimed_modules[module] = subdevice;
78+
LOG_DBG("Module %s claimed by %s", SEESAW_MODULE_NAME(module), subdevice->name);
79+
return 0;
80+
}
81+
82+
int seesaw_cmd(const struct device *dev, enum seesaw_mod module, uint8_t reg)
83+
{
84+
int ret;
85+
struct seesaw_data *drv_data = dev->data;
86+
const struct seesaw_config *const config = dev->config;
87+
uint8_t buf[] = {module, reg};
88+
89+
k_sem_take(&drv_data->sem, K_FOREVER);
90+
ret = i2c_write_dt(&config->i2c, buf, sizeof(buf));
91+
k_sem_give(&drv_data->sem);
92+
93+
return ret;
94+
}
95+
96+
int seesaw_write(const struct device *dev, enum seesaw_mod module, uint8_t reg,
97+
const uint8_t *buf, size_t len)
98+
{
99+
int ret;
100+
struct seesaw_data *drv_data = dev->data;
101+
const struct seesaw_config *const config = dev->config;
102+
const uint8_t request[] = {module, reg};
103+
struct i2c_msg transaction[] = {
104+
{.buf = request, .len = sizeof(request), .flags = I2C_MSG_WRITE},
105+
{.buf = buf, .len = len, .flags = I2C_MSG_WRITE | I2C_MSG_STOP},
106+
};
107+
108+
k_sem_take(&drv_data->sem, K_FOREVER);
109+
ret = i2c_transfer_dt(&config->i2c, transaction, ARRAY_SIZE(transaction));
110+
k_sem_give(&drv_data->sem);
111+
112+
return ret;
113+
}
114+
115+
int seesaw_read(const struct device *dev, enum seesaw_mod module, uint8_t reg,
116+
uint8_t *buf, size_t len)
117+
{
118+
int ret;
119+
struct seesaw_data *drv_data = dev->data;
120+
const struct seesaw_config *const config = dev->config;
121+
const uint8_t request[] = {module, reg};
122+
123+
k_sem_take(&drv_data->sem, K_FOREVER);
124+
125+
ret = i2c_write_dt(&config->i2c, request, sizeof(request));
126+
if (ret == 0) {
127+
k_sleep(K_MSEC(1)); /* Wait for seesaw to prepare response */
128+
ret = i2c_read_dt(&config->i2c, buf, len);
129+
}
130+
131+
k_sem_give(&drv_data->sem);
132+
return ret;
133+
}
134+
135+
static int seesaw_init(const struct device *dev)
136+
{
137+
int ret;
138+
uint32_t data;
139+
struct seesaw_data *drv_data = dev->data;
140+
141+
k_sem_init(&drv_data->sem, 1, 1);
142+
143+
ret = seesaw_cmd(dev, SEESAW_MOD_STATUS, STATUS_SWRST);
144+
if (ret) {
145+
LOG_ERR("[%s] Unable to software reset", dev->name);
146+
return ret;
147+
}
148+
k_sleep(K_MSEC(10));
149+
150+
ret = seesaw_read(dev, SEESAW_MOD_STATUS, STATUS_OPTIONS,
151+
(uint8_t *)&data, sizeof(data));
152+
if (ret) {
153+
LOG_ERR("[%s] Unable to query options", dev->name);
154+
return ret;
155+
}
156+
drv_data->modules = sys_be32_to_cpu(data);
157+
158+
LOG_DBG("[%s] Available modules: %08X", dev->name, drv_data->modules);
159+
for (size_t i = 0; i < SEESAW_MOD_MAX_NUM; i++) {
160+
if (drv_data->modules & BIT(i)) {
161+
LOG_DBG("- %s", module_names[i]);
162+
}
163+
}
164+
165+
return 0;
166+
}
167+
168+
#define SEESAW_INIT(inst) \
169+
static struct seesaw_data seesaw_##inst##_data; \
170+
static const struct seesaw_config seesaw_##inst##_config = { \
171+
.i2c = I2C_DT_SPEC_INST_GET(inst), \
172+
}; \
173+
DEVICE_DT_INST_DEFINE(inst, seesaw_init, NULL, &seesaw_##inst##_data, \
174+
&seesaw_##inst##_config, POST_KERNEL, \
175+
CONFIG_MFD_INIT_PRIORITY, NULL);
176+
177+
DT_INST_FOREACH_STATUS_OKAY(SEESAW_INIT)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (C) 2025 Titouan Christophe
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: Adafruit Seesaw
5+
6+
compatible: "adafruit,seesaw"
7+
8+
include: [i2c-device.yaml]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2025 Titouan Christophe
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_DRIVERS_MFD_SEESAW_H_
8+
#define ZEPHYR_DRIVERS_MFD_SEESAW_H_
9+
10+
#include <stdint.h>
11+
#include <stdlib.h>
12+
#include <zephyr/device.h>
13+
#include <zephyr/sys/byteorder.h>
14+
15+
/* See https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout/using-the-seesaw-platform */
16+
enum seesaw_mod {
17+
SEESAW_MOD_STATUS = 0x00,
18+
SEESAW_MOD_GPIO = 0x01,
19+
SEESAW_MOD_SERCOM0 = 0x02,
20+
SEESAW_MOD_SERCOM1 = 0x03,
21+
SEESAW_MOD_SERCOM2 = 0x04,
22+
SEESAW_MOD_SERCOM3 = 0x05,
23+
SEESAW_MOD_SERCOM4 = 0x06,
24+
SEESAW_MOD_SERCOM5 = 0x07,
25+
SEESAW_MOD_PWM = 0x08,
26+
SEESAW_MOD_ADC = 0x09,
27+
SEESAW_MOD_DAC = 0x0a,
28+
SEESAW_MOD_INTERRUPT = 0x0b,
29+
SEESAW_MOD_DAP = 0x0c,
30+
SEESAW_MOD_EEPROM = 0x0d,
31+
SEESAW_MOD_NEOPIXEL = 0x0e,
32+
SEESAW_MOD_TOUCH = 0x0f,
33+
SEESAW_MOD_KEYPAD = 0x10,
34+
SEESAW_MOD_ENCODER = 0x11,
35+
SEESAW_MOD_MAX_NUM,
36+
};
37+
38+
int seesaw_claim_module(const struct device *dev, uint32_t module,
39+
const struct device *subdevice);
40+
41+
int seesaw_cmd(const struct device *dev, enum seesaw_mod module, uint8_t reg);
42+
43+
int seesaw_write(const struct device *dev, enum seesaw_mod module, uint8_t reg,
44+
const uint8_t *buf, size_t len);
45+
46+
int seesaw_read(const struct device *dev, enum seesaw_mod module, uint8_t reg,
47+
uint8_t *buf, size_t len);
48+
49+
static inline int seesaw_write_uint32(const struct device *dev, enum seesaw_mod module,
50+
uint8_t reg, uint32_t value)
51+
{
52+
uint32_t buf = sys_cpu_to_be32(value);
53+
54+
return seesaw_write(dev, module, reg, (const uint8_t *) &buf, 4);
55+
}
56+
57+
static inline int seesaw_write_uint16(const struct device *dev, enum seesaw_mod module,
58+
uint8_t reg, uint16_t value)
59+
{
60+
uint16_t buf = sys_cpu_to_be16(value);
61+
62+
return seesaw_write(dev, module, reg, (const uint8_t *) &buf, 2);
63+
}
64+
65+
#endif

0 commit comments

Comments
 (0)