1 /*
2 * Copyright 2023, Alvaro Garcia <maxpowel@gmail.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Emulator for max17048 fuel gauge
7 */
8
9
10 #define DT_DRV_COMPAT maxim_max17048
11
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(maxim_max17048);
15
16 #include <zephyr/device.h>
17 #include <zephyr/drivers/emul.h>
18 #include <zephyr/drivers/i2c.h>
19 #include <zephyr/drivers/i2c_emul.h>
20 #include <zephyr/sys/byteorder.h>
21
22 #include "max17048.h"
23
24 static int crate_value = 0x4000;
25
emul_max17048_set_crate_status(int value)26 void emul_max17048_set_crate_status(int value)
27 {
28 crate_value = value;
29 }
30
31 /** Static configuration for the emulator */
32 struct max17048_emul_cfg {
33 /** I2C address of emulator */
34 uint16_t addr;
35 };
36
emul_max17048_reg_write(const struct emul * target,int reg,int val)37 static int emul_max17048_reg_write(const struct emul *target, int reg, int val)
38 {
39
40 return -EIO;
41 }
42
emul_max17048_reg_read(const struct emul * target,int reg,int * val)43 static int emul_max17048_reg_read(const struct emul *target, int reg, int *val)
44 {
45
46 switch (reg) {
47 case REGISTER_VERSION:
48 *val = 0x1000;
49 break;
50 case REGISTER_CRATE:
51 *val = crate_value;
52 break;
53 case REGISTER_SOC:
54 *val = 0x3525;
55 break;
56 case REGISTER_VCELL:
57 *val = 0x4387;
58 break;
59 default:
60 LOG_ERR("Unknown register 0x%x read", reg);
61 return -EIO;
62 }
63 LOG_INF("read 0x%x = 0x%x", reg, *val);
64
65 return 0;
66 }
67
max17048_emul_transfer_i2c(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)68 static int max17048_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
69 int num_msgs, int addr)
70 {
71 /* Largely copied from emul_bmi160.c */
72 unsigned int val;
73 int reg;
74 int rc;
75
76 __ASSERT_NO_MSG(msgs && num_msgs);
77
78 i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
79 switch (num_msgs) {
80 case 2:
81 if (msgs->flags & I2C_MSG_READ) {
82 LOG_ERR("Unexpected read");
83 return -EIO;
84 }
85 if (msgs->len != 1) {
86 LOG_ERR("Unexpected msg0 length %d", msgs->len);
87 return -EIO;
88 }
89 reg = msgs->buf[0];
90
91 /* Now process the 'read' part of the message */
92 msgs++;
93 if (msgs->flags & I2C_MSG_READ) {
94 switch (msgs->len - 1) {
95 case 1:
96 rc = emul_max17048_reg_read(target, reg, &val);
97 if (rc) {
98 /* Return before writing bad value to message buffer */
99 return rc;
100 }
101
102 /* SBS uses SMBus, which sends data in little-endian format. */
103 sys_put_le16(val, msgs->buf);
104 break;
105 default:
106 LOG_ERR("Unexpected msg1 length %d", msgs->len);
107 return -EIO;
108 }
109 } else {
110 /* We write a word (2 bytes by the SBS spec) */
111 if (msgs->len != 2) {
112 LOG_ERR("Unexpected msg1 length %d", msgs->len);
113 }
114 uint16_t value = sys_get_le16(msgs->buf);
115
116 rc = emul_max17048_reg_write(target, reg, value);
117 }
118 break;
119 default:
120 LOG_ERR("Invalid number of messages: %d", num_msgs);
121 return -EIO;
122 }
123
124 return rc;
125 }
126
127 static const struct i2c_emul_api max17048_emul_api_i2c = {
128 .transfer = max17048_emul_transfer_i2c,
129 };
130
131 /**
132 * Set up a new emulator (I2C)
133 *
134 * @param emul Emulation information
135 * @param parent Device to emulate
136 * @return 0 indicating success (always)
137 */
emul_max17048_init(const struct emul * target,const struct device * parent)138 static int emul_max17048_init(const struct emul *target, const struct device *parent)
139 {
140 ARG_UNUSED(target);
141 ARG_UNUSED(parent);
142
143 return 0;
144 }
145
146 /*
147 * Main instantiation macro.
148 */
149 #define MAX17048_EMUL(n) \
150 static const struct max17048_emul_cfg max17048_emul_cfg_##n = { \
151 .addr = DT_INST_REG_ADDR(n), \
152 }; \
153 EMUL_DT_INST_DEFINE(n, emul_max17048_init, NULL, \
154 &max17048_emul_cfg_##n, &max17048_emul_api_i2c, NULL)
155
156 DT_INST_FOREACH_STATUS_OKAY(MAX17048_EMUL)
157