1 /*
2 * Copyright 2023 Cirrus Logic, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Emulator for SBS 1.1 compliant smart battery charger.
7 */
8
9 #define DT_DRV_COMPAT sbs_sbs_charger
10
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/emul.h>
13 #include <zephyr/drivers/i2c.h>
14 #include <zephyr/drivers/i2c_emul.h>
15 #include <zephyr/logging/log.h>
16 #include <zephyr/sys/byteorder.h>
17
18 #include "sbs_charger.h"
19
20 LOG_MODULE_REGISTER(sbs_sbs_charger);
21
22 /** Static configuration for the emulator */
23 struct sbs_charger_emul_cfg {
24 /** I2C address of emulator */
25 uint16_t addr;
26 };
27
28 /** Run-time data used by the emulator */
29 struct sbs_charger_emul_data {
30 uint16_t reg_charger_mode;
31 };
32
emul_sbs_charger_reg_write(const struct emul * target,int reg,int val)33 static int emul_sbs_charger_reg_write(const struct emul *target, int reg, int val)
34 {
35 struct sbs_charger_emul_data *data = target->data;
36
37 LOG_INF("write %x = %x", reg, val);
38 switch (reg) {
39 case SBS_CHARGER_REG_CHARGER_MODE:
40 data->reg_charger_mode = val;
41 break;
42 default:
43 LOG_ERR("Unknown write %x", reg);
44 return -EIO;
45 }
46
47 return 0;
48 }
49
emul_sbs_charger_reg_read(const struct emul * target,int reg,int * val)50 static int emul_sbs_charger_reg_read(const struct emul *target, int reg, int *val)
51 {
52 switch (reg) {
53 case SBS_CHARGER_REG_SPEC_INFO:
54 case SBS_CHARGER_REG_CHARGER_MODE:
55 case SBS_CHARGER_REG_STATUS:
56 case SBS_CHARGER_REG_ALARM_WARNING:
57 /* Arbitrary stub value. */
58 *val = 1;
59 break;
60 default:
61 LOG_ERR("Unknown register 0x%x read", reg);
62 return -EIO;
63 }
64 LOG_INF("read 0x%x = 0x%x", reg, *val);
65
66 return 0;
67 }
68
sbs_charger_emul_transfer_i2c(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)69 static int sbs_charger_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs,
70 int num_msgs, int addr)
71 {
72 /* Largely copied from emul_sbs_gauge.c */
73 struct sbs_charger_emul_data *data;
74 unsigned int val;
75 int reg;
76 int rc;
77
78 data = target->data;
79
80 i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
81 switch (num_msgs) {
82 case 2:
83 if (msgs->flags & I2C_MSG_READ) {
84 LOG_ERR("Unexpected read");
85 return -EIO;
86 }
87 if (msgs->len != 1) {
88 LOG_ERR("Unexpected msg0 length %d", msgs->len);
89 return -EIO;
90 }
91 reg = msgs->buf[0];
92
93 /* Now process the 'read' part of the message */
94 msgs++;
95 if (msgs->flags & I2C_MSG_READ) {
96 switch (msgs->len - 1) {
97 case 1:
98 rc = emul_sbs_charger_reg_read(target, reg, &val);
99 if (rc) {
100 /* Return before writing bad value to message buffer */
101 return rc;
102 }
103
104 /* SBS uses SMBus, which sends data in little-endian format. */
105 sys_put_le16(val, msgs->buf);
106 break;
107 default:
108 LOG_ERR("Unexpected msg1 length %d", msgs->len);
109 return -EIO;
110 }
111 } else {
112 /* We write a word (2 bytes by the SBS spec) */
113 if (msgs->len != 2) {
114 LOG_ERR("Unexpected msg1 length %d", msgs->len);
115 }
116 uint16_t value = sys_get_le16(msgs->buf);
117
118 rc = emul_sbs_charger_reg_write(target, reg, value);
119 }
120 break;
121 default:
122 LOG_ERR("Invalid number of messages: %d", num_msgs);
123 return -EIO;
124 }
125
126 return rc;
127 }
128
129 static const struct i2c_emul_api sbs_charger_emul_api_i2c = {
130 .transfer = sbs_charger_emul_transfer_i2c,
131 };
132
emul_sbs_sbs_charger_init(const struct emul * target,const struct device * parent)133 static int emul_sbs_sbs_charger_init(const struct emul *target, const struct device *parent)
134 {
135 ARG_UNUSED(target);
136 ARG_UNUSED(parent);
137
138 return 0;
139 }
140
141 /*
142 * Main instantiation macro. SBS Charger Emulator only implemented for I2C
143 */
144 #define SBS_CHARGER_EMUL(n) \
145 static struct sbs_charger_emul_data sbs_charger_emul_data_##n; \
146 \
147 static const struct sbs_charger_emul_cfg sbs_charger_emul_cfg_##n = { \
148 .addr = DT_INST_REG_ADDR(n), \
149 }; \
150 EMUL_DT_INST_DEFINE(n, emul_sbs_sbs_charger_init, &sbs_charger_emul_data_##n, \
151 &sbs_charger_emul_cfg_##n, &sbs_charger_emul_api_i2c, NULL)
152
153 DT_INST_FOREACH_STATUS_OKAY(SBS_CHARGER_EMUL)
154