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