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
|
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright 2025 Oxide Computer Company
*/
/*
* Minimally emulate a TS5111. We use a basic register template for this. We
* allow a minimum number of writable registers, but that doesn't change
* behavior for anything. We will ignore any attempts to go to I3C mode or to
* change the default read address mode and starting point.
*/
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <sys/sysmacros.h>
#include <sys/bitext.h>
#include "i2csimd.h"
#define TS5111_REGLEN 256
typedef struct ts5111 {
uint8_t ts_data[TS5111_REGLEN];
uint8_t ts_curaddr;
} ts5111_t;
/*
* Default register values from the spec, ignoring what we're going to set for
* the temperature when this is created.
*/
static const uint8_t ts5111_tmpl[TS5111_REGLEN] = {
[0x00] = 0x51,
[0x01] = 0x11,
[0x02] = 0x12,
[0x03] = 0x00,
[0x04] = 0x01,
[0x07] = 0x0e,
[0x28] = 0x70,
[0x29] = 0x03,
[0x32] = 0x50,
[0x33] = 0x05
};
/*
* While we could emulate and support writes for a subset of the device space
* that's writable, we just discard everything other than setting the current
* address because we don't need it.
*/
static bool
ts5111_write(void *arg, uint32_t len, const uint8_t *buf)
{
ts5111_t *ts = arg;
if (len > 0) {
ts->ts_curaddr = buf[0];
}
return (true);
}
/*
* If someone reads past the end of the device, we just wrap to the start.
*/
static bool
ts5111_read(void *arg, uint32_t len, uint8_t *buf)
{
ts5111_t *ts = arg;
while (len > 0) {
uint16_t rem = sizeof (ts->ts_data) - ts->ts_curaddr + 1;
uint16_t toread = MIN(rem, len);
(void) memcpy(buf, &ts->ts_data[ts->ts_curaddr], toread);
buf += toread;
len -= toread;
ts->ts_curaddr += toread;
}
return (true);
}
static const i2csimd_ops_t ts5111_ops = {
.sop_write = ts5111_write,
.sop_read = ts5111_read
};
i2csimd_dev_t *
i2csimd_make_ts5111(uint8_t addr, uint16_t temp)
{
ts5111_t *ts = calloc(1, sizeof (ts5111_t));
if (ts == NULL) {
err(EXIT_FAILURE, "failed to allocate a ts5111_t");
}
(void) memcpy(ts->ts_data, ts5111_tmpl, sizeof (ts5111_tmpl));
ts->ts_data[0x31] = bitx16(temp, 7, 0);
ts->ts_data[0x32] = bitx16(temp, 15, 8);
i2csimd_dev_t *dev = calloc(1, sizeof (i2csimd_dev_t));
if (dev == NULL) {
err(EXIT_FAILURE, "failed to allocate i2csimd_dev_t");
}
dev->dev_name = "ts5111";
dev->dev_addr = addr;
dev->dev_arg = ts;
dev->dev_ops = &ts5111_ops;
return (dev);
}
|