summaryrefslogtreecommitdiff
path: root/zephyr/emul/emul_sn5s330.c
blob: f957cd9e0529be476c55a2c903741a23ecd27d94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
/* Copyright 2021 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#define DT_DRV_COMPAT cros_sn5s330_emul

#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/i2c_emul.h>
#include <zephyr/drivers/emul.h>
#include <errno.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/devicetree/gpio.h>
#include <zephyr/drivers/gpio/gpio_emul.h>

#include "driver/ppc/sn5s330.h"
#include "driver/ppc/sn5s330_public.h"
#include "emul/emul_common_i2c.h"
#include "emul/emul_sn5s330.h"
#include "i2c.h"
#include "emul/emul_stub_device.h"

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sn5s330_emul, CONFIG_SN5S330_EMUL_LOG_LEVEL);

struct sn5s330_emul_data {
	/** Common I2C data */
	struct i2c_common_emul_data common;
	/** Emulated int-pin port */
	const struct device *gpio_int_port;
	/** Emulated int-pin pin */
	gpio_pin_t gpio_int_pin;
	/** Emulated FUNC_SET1 register */
	uint8_t func_set1_reg;
	/** Emulated FUNC_SET2 register */
	uint8_t func_set2_reg;
	/** Emulated FUNC_SET3 register */
	uint8_t func_set3_reg;
	/** Emulated FUNC_SET4 register */
	uint8_t func_set4_reg;
	/** Emulated FUNC_SET5 register */
	uint8_t func_set5_reg;
	/** Emulated FUNC_SET6 register */
	uint8_t func_set6_reg;
	/** Emulated FUNC_SET7 register */
	uint8_t func_set7_reg;
	/** Emulated FUNC_SET8 register */
	uint8_t func_set8_reg;
	/** Emulated FUNC_SET9 register */
	uint8_t func_set9_reg;
	/** Emulated FUNC_SET10 register */
	uint8_t func_set10_reg;
	/** Emulated FUNC_SET11 register */
	uint8_t func_set11_reg;
	/** Emulated FUNC_SET12 register */
	uint8_t func_set12_reg;
	/** Emulated INT_STATUS_REG1 register */
	uint8_t int_status_reg1;
	/** Emulated INT_STATUS_REG2 register */
	uint8_t int_status_reg2;
	/** Emulated INT_STATUS_REG3 register */
	uint8_t int_status_reg3;
	/** Emulated INT_STATUS_REG4 register */
	/*
	 * TODO(b/205754232): Register name discrepancy
	 */
	uint8_t int_status_reg4;
	/*
	 * TODO(b/203364783): For all falling edge registers, implement
	 * interrupt and bit change to correspond to change in interrupt status
	 * registers.
	 */
	/** Emulated INT_MASK_RISE_REG1 register */
	uint8_t int_mask_rise_reg1;
	/** Emulated INT_MASK_RISE_REG2 register */
	uint8_t int_mask_rise_reg2;
	/** Emulated INT_MASK_RISE_REG3 register */
	uint8_t int_mask_rise_reg3;
	/** Emulated INT_MASK_FALL_REG1 register */
	uint8_t int_mask_fall_reg1;
	/** Emulated INT_MASK_FALL_REG2 register */
	uint8_t int_mask_fall_reg2;
	/** Emulated INT_MASK_FALL_REG3 register */
	uint8_t int_mask_fall_reg3;
	/** Emulated INT_TRIP_RISE_REG1 register */
	uint8_t int_trip_rise_reg1;
	/** Emulated INT_TRIP_RISE_REG2 register */
	uint8_t int_trip_rise_reg2;
	/** Emulated INT_TRIP_RISE_REG3 register */
	uint8_t int_trip_rise_reg3;
	/** Emulated INT_TRIP_FALL_REG1 register */
	uint8_t int_trip_fall_reg1;
	/** Emulated INT_TRIP_FALL_REG2 register */
	uint8_t int_trip_fall_reg2;
	/** Emulated INT_TRIP_FALL_REG3 register */
	uint8_t int_trip_fall_reg3;
};

struct sn5s330_emul_cfg {
	/** Common I2C config */
	const struct i2c_common_emul_cfg common;
};

test_mockable_static void sn5s330_emul_interrupt_set_stub(void)
{
	/* Stub to be used by fff fakes during test */
}

/* Workhorse for mapping i2c reg to internal emulator data access */
static uint8_t *sn5s330_emul_get_reg_ptr(struct sn5s330_emul_data *data,
					 int reg)
{
	switch (reg) {
	case SN5S330_FUNC_SET1:
		return &(data->func_set1_reg);
	case SN5S330_FUNC_SET2:
		return &(data->func_set2_reg);
	case SN5S330_FUNC_SET3:
		return &(data->func_set3_reg);
	case SN5S330_FUNC_SET4:
		return &(data->func_set4_reg);
	case SN5S330_FUNC_SET5:
		return &(data->func_set5_reg);
	case SN5S330_FUNC_SET6:
		return &(data->func_set6_reg);
	case SN5S330_FUNC_SET7:
		return &(data->func_set7_reg);
	case SN5S330_FUNC_SET8:
		return &(data->func_set8_reg);
	case SN5S330_FUNC_SET9:
		return &(data->func_set9_reg);
	case SN5S330_FUNC_SET10:
		return &(data->func_set10_reg);
	case SN5S330_FUNC_SET11:
		return &(data->func_set11_reg);
	case SN5S330_FUNC_SET12:
		return &(data->func_set12_reg);
	case SN5S330_INT_STATUS_REG1:
		return &(data->int_status_reg1);
	case SN5S330_INT_STATUS_REG2:
		return &(data->int_status_reg2);
	case SN5S330_INT_STATUS_REG3:
		return &(data->int_status_reg3);
	case SN5S330_INT_STATUS_REG4:
		return &(data->int_status_reg4);
	case SN5S330_INT_MASK_RISE_REG1:
		return &(data->int_mask_rise_reg1);
	case SN5S330_INT_MASK_RISE_REG2:
		return &(data->int_mask_rise_reg2);
	case SN5S330_INT_MASK_RISE_REG3:
		return &(data->int_mask_rise_reg3);
	case SN5S330_INT_MASK_FALL_REG1:
		return &(data->int_mask_fall_reg1);
	case SN5S330_INT_MASK_FALL_REG2:
		return &(data->int_mask_fall_reg2);
	case SN5S330_INT_MASK_FALL_REG3:
		return &(data->int_mask_fall_reg3);
	case SN5S330_INT_TRIP_RISE_REG1:
		return &(data->int_trip_rise_reg1);
	case SN5S330_INT_TRIP_RISE_REG2:
		return &(data->int_trip_rise_reg2);
	case SN5S330_INT_TRIP_RISE_REG3:
		return &(data->int_trip_rise_reg3);
	case SN5S330_INT_TRIP_FALL_REG1:
		return &(data->int_trip_fall_reg1);
	case SN5S330_INT_TRIP_FALL_REG2:
		return &(data->int_trip_fall_reg2);
	case SN5S330_INT_TRIP_FALL_REG3:
		return &(data->int_trip_fall_reg3);
	default:
		__ASSERT(false, "Unimplemented Register Access Error on 0x%x",
			 reg);
		/* Statement never reached, required for compiler warnings */
		return NULL;
	}
}

void sn5s330_emul_peek_reg(const struct emul *emul, uint32_t reg, uint8_t *val)
{
	struct sn5s330_emul_data *data = emul->data;
	uint8_t *data_reg = sn5s330_emul_get_reg_ptr(data, reg);

	*val = *data_reg;
}

static void sn5s330_emul_set_int_pin(const struct emul *emul, bool val)
{
	struct sn5s330_emul_data *data = emul->data;
	int res = gpio_emul_input_set(data->gpio_int_port, data->gpio_int_pin,
				      val);
	__ASSERT_NO_MSG(res == 0);
}

static void sn5s330_emul_assert_interrupt(const struct emul *emul)
{
	sn5s330_emul_interrupt_set_stub();
	sn5s330_emul_set_int_pin(emul, false);
}

static void sn5s330_emul_deassert_interrupt(const struct emul *emul)
{
	sn5s330_emul_set_int_pin(emul, true);
}

static int sn5s330_emul_read_byte(const struct emul *emul, int reg,
				  uint8_t *val, int bytes)
{
	struct sn5s330_emul_data *data = emul->data;
	uint8_t *reg_to_read = sn5s330_emul_get_reg_ptr(data, reg);

	__ASSERT(bytes == 0, "bytes 0x%x != 0x0 on reg 0x%x", bytes, reg);
	*val = *reg_to_read;

	return 0;
}

static int sn5s330_emul_write_byte(const struct emul *emul, int reg,
				   uint8_t val, int bytes)
{
	struct sn5s330_emul_data *data = emul->data;
	uint8_t *reg_to_write;
	bool deassert_int = false;

	__ASSERT(bytes == 1, "bytes 0x%x != 0x1 on reg 0x%x", bytes, reg);

	/* Specially check for read-only reg */
	switch (reg) {
	case SN5S330_INT_TRIP_RISE_REG1:
		/* fallthrough */
	case SN5S330_INT_TRIP_RISE_REG2:
		/* fallthrough */
	case SN5S330_INT_TRIP_RISE_REG3:
		/* fallthrough */
	case SN5S330_INT_TRIP_FALL_REG1:
		/* fallthrough */
	case SN5S330_INT_TRIP_FALL_REG2:
		/* fallthrough */
	case SN5S330_INT_TRIP_FALL_REG3:
		reg_to_write = sn5s330_emul_get_reg_ptr(data, reg);
		/* Clearing any bit deasserts /INT interrupt signal */
		deassert_int = (*reg_to_write & val) != 0;
		/* Write 0 is noop and 1 clears the bit. */
		val = (~val & *reg_to_write);
		*reg_to_write = val;
		break;
	case SN5S330_INT_STATUS_REG1:
		/* fallthrough */
	case SN5S330_INT_STATUS_REG2:
		/* fallthrough */
	case SN5S330_INT_STATUS_REG3:
		__ASSERT(false,
			 "Write to an unverified-as-safe read-only register on "
			 "0x%x",
			 reg);
		/* fallthrough for checkpath */
	default:
		reg_to_write = sn5s330_emul_get_reg_ptr(data, reg);
		*reg_to_write = val;
	}

	if (deassert_int)
		sn5s330_emul_deassert_interrupt(emul);

	return 0;
}

void sn5s330_emul_make_vbus_overcurrent(const struct emul *emul)
{
	struct sn5s330_emul_data *data = emul->data;

	data->int_status_reg1 |= SN5S330_ILIM_PP1_MASK;
	data->int_trip_rise_reg1 |= SN5S330_ILIM_PP1_MASK;

	/* driver disabled this interrupt trigger */
	if (data->int_mask_rise_reg1 & SN5S330_ILIM_PP1_MASK)
		return;

	sn5s330_emul_assert_interrupt(emul);
}

void sn5s330_emul_lower_vbus_below_minv(const struct emul *emul)
{
	struct sn5s330_emul_data *data = emul->data;

	data->int_status_reg4 |= SN5S330_VSAFE0V_STAT;

	/* driver disabled this interrupt trigger */
	if (data->int_status_reg4 & SN5S330_VSAFE0V_MASK)
		return;

	sn5s330_emul_assert_interrupt(emul);
}

void sn5s330_emul_reset(const struct emul *emul)
{
	struct sn5s330_emul_data *data = emul->data;
	struct i2c_common_emul_data common = data->common;
	const struct device *gpio_int_port = data->gpio_int_port;
	gpio_pin_t gpio_int_pin = data->gpio_int_pin;

	sn5s330_emul_deassert_interrupt(emul);

	/*
	 * TODO(b/203364783): Some registers reset with set bits; this should be
	 * reflected in the emul_reset function.
	 */

	/* Only Reset the sn5s330 Register Data */
	memset(data, 0, sizeof(struct sn5s330_emul_data));
	data->common = common;
	data->gpio_int_port = gpio_int_port;
	data->gpio_int_pin = gpio_int_pin;
}

static int emul_sn5s330_init(const struct emul *emul,
			     const struct device *parent)
{
	struct sn5s330_emul_data *data = emul->data;

	sn5s330_emul_deassert_interrupt(emul);

	data->common.i2c = parent;
	i2c_common_emul_init(&data->common);

	return 0;
}

#define SN5S330_GET_GPIO_INT_PORT(n) \
	DEVICE_DT_GET(DT_GPIO_CTLR(DT_INST_PROP(n, int_pin), gpios))

#define SN5S330_GET_GPIO_INT_PIN(n) DT_GPIO_PIN(DT_INST_PROP(n, int_pin), gpios)

#define INIT_SN5S330(n)                                                          \
	static struct sn5s330_emul_data sn5s330_emul_data_##n = { \
		.common = {                                                    \
			.write_byte = sn5s330_emul_write_byte,                 \
			.read_byte = sn5s330_emul_read_byte,                   \
		},                                                             \
		.gpio_int_port = SN5S330_GET_GPIO_INT_PORT(n),		       \
		.gpio_int_pin = SN5S330_GET_GPIO_INT_PIN(n),		       \
	};              \
	static struct sn5s330_emul_cfg sn5s330_emul_cfg_##n = {                \
		.common = {                                                    \
			.dev_label = DT_NODE_FULL_NAME(DT_DRV_INST(n)),        \
			.addr = DT_INST_REG_ADDR(n),                           \
		},                                                             \
	}; \
	EMUL_DT_INST_DEFINE(n, emul_sn5s330_init, &sn5s330_emul_data_##n,        \
			    &sn5s330_emul_cfg_##n, &i2c_common_emul_api)

DT_INST_FOREACH_STATUS_OKAY(INIT_SN5S330)
DT_INST_FOREACH_STATUS_OKAY(EMUL_STUB_DEVICE);

struct i2c_common_emul_data *
emul_sn5s330_get_i2c_common_data(const struct emul *emul)
{
	return emul->data;
}