]> git.ipfire.org Git - thirdparty/qemu.git/blob - hw/i2c/i2c_mux_pca954x.c
a9517b612aee1150b9f50c37646ac0f80e74be42
[thirdparty/qemu.git] / hw / i2c / i2c_mux_pca954x.c
1 /*
2 * I2C multiplexer for PCA954x series of I2C multiplexer/switch chips.
3 *
4 * Copyright 2021 Google LLC
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "qemu/osdep.h"
18 #include "qapi/error.h"
19 #include "hw/i2c/i2c.h"
20 #include "hw/i2c/i2c_mux_pca954x.h"
21 #include "hw/i2c/smbus_slave.h"
22 #include "hw/qdev-core.h"
23 #include "hw/sysbus.h"
24 #include "qemu/log.h"
25 #include "qemu/module.h"
26 #include "qemu/queue.h"
27 #include "qom/object.h"
28 #include "trace.h"
29
30 #define PCA9548_CHANNEL_COUNT 8
31 #define PCA9546_CHANNEL_COUNT 4
32
33 /*
34 * struct Pca954xState - The pca954x state object.
35 * @control: The value written to the mux control.
36 * @channel: The set of i2c channel buses that act as channels which own the
37 * i2c children.
38 */
39 typedef struct Pca954xState {
40 SMBusDevice parent;
41
42 uint8_t control;
43
44 bool enabled[PCA9548_CHANNEL_COUNT];
45 I2CBus *bus[PCA9548_CHANNEL_COUNT];
46 } Pca954xState;
47
48 /*
49 * struct Pca954xClass - The pca954x class object.
50 * @nchans: The number of i2c channels this device has.
51 */
52 typedef struct Pca954xClass {
53 SMBusDeviceClass parent;
54
55 uint8_t nchans;
56 } Pca954xClass;
57
58 #define TYPE_PCA954X "pca954x"
59 OBJECT_DECLARE_TYPE(Pca954xState, Pca954xClass, PCA954X)
60
61 /*
62 * For each channel, if it's enabled, recursively call match on those children.
63 */
64 static bool pca954x_match(I2CSlave *candidate, uint8_t address,
65 bool broadcast,
66 I2CNodeList *current_devs)
67 {
68 Pca954xState *mux = PCA954X(candidate);
69 Pca954xClass *mc = PCA954X_GET_CLASS(mux);
70 int i;
71
72 /* They are talking to the mux itself (or all devices enabled). */
73 if ((candidate->address == address) || broadcast) {
74 I2CNode *node = g_malloc(sizeof(struct I2CNode));
75 node->elt = candidate;
76 QLIST_INSERT_HEAD(current_devs, node, next);
77 if (!broadcast) {
78 return true;
79 }
80 }
81
82 for (i = 0; i < mc->nchans; i++) {
83 if (!mux->enabled[i]) {
84 continue;
85 }
86
87 if (i2c_scan_bus(mux->bus[i], address, broadcast,
88 current_devs)) {
89 if (!broadcast) {
90 return true;
91 }
92 }
93 }
94
95 /* If we arrived here we didn't find a match, return broadcast. */
96 return broadcast;
97 }
98
99 static void pca954x_enable_channel(Pca954xState *s, uint8_t enable_mask)
100 {
101 Pca954xClass *mc = PCA954X_GET_CLASS(s);
102 int i;
103
104 /*
105 * For each channel, check if their bit is set in enable_mask and if yes,
106 * enable it, otherwise disable, hide it.
107 */
108 for (i = 0; i < mc->nchans; i++) {
109 if (enable_mask & (1 << i)) {
110 s->enabled[i] = true;
111 } else {
112 s->enabled[i] = false;
113 }
114 }
115 }
116
117 static void pca954x_write(Pca954xState *s, uint8_t data)
118 {
119 s->control = data;
120 pca954x_enable_channel(s, data);
121
122 trace_pca954x_write_bytes(data);
123 }
124
125 static int pca954x_write_data(SMBusDevice *d, uint8_t *buf, uint8_t len)
126 {
127 Pca954xState *s = PCA954X(d);
128
129 if (len == 0) {
130 qemu_log_mask(LOG_GUEST_ERROR, "%s: writing empty data\n", __func__);
131 return -1;
132 }
133
134 /*
135 * len should be 1, because they write one byte to enable/disable channels.
136 */
137 if (len > 1) {
138 qemu_log_mask(LOG_GUEST_ERROR,
139 "%s: extra data after channel selection mask\n",
140 __func__);
141 return -1;
142 }
143
144 pca954x_write(s, buf[0]);
145 return 0;
146 }
147
148 static uint8_t pca954x_read_byte(SMBusDevice *d)
149 {
150 Pca954xState *s = PCA954X(d);
151 uint8_t data = s->control;
152 trace_pca954x_read_data(data);
153 return data;
154 }
155
156 static void pca954x_enter_reset(Object *obj, ResetType type)
157 {
158 Pca954xState *s = PCA954X(obj);
159 /* Reset will disable all channels. */
160 pca954x_write(s, 0);
161 }
162
163 I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel)
164 {
165 Pca954xClass *pc = PCA954X_GET_CLASS(mux);
166 Pca954xState *pca954x = PCA954X(mux);
167
168 g_assert(channel < pc->nchans);
169 return pca954x->bus[channel];
170 }
171
172 static void pca9546_class_init(ObjectClass *klass, void *data)
173 {
174 Pca954xClass *s = PCA954X_CLASS(klass);
175 s->nchans = PCA9546_CHANNEL_COUNT;
176 }
177
178 static void pca9548_class_init(ObjectClass *klass, void *data)
179 {
180 Pca954xClass *s = PCA954X_CLASS(klass);
181 s->nchans = PCA9548_CHANNEL_COUNT;
182 }
183
184 static void pca954x_init(Object *obj)
185 {
186 Pca954xState *s = PCA954X(obj);
187 Pca954xClass *c = PCA954X_GET_CLASS(obj);
188 int i;
189
190 /* SMBus modules. Cannot fail. */
191 for (i = 0; i < c->nchans; i++) {
192 g_autofree gchar *bus_name = g_strdup_printf("i2c.%d", i);
193
194 /* start all channels as disabled. */
195 s->enabled[i] = false;
196 s->bus[i] = i2c_init_bus(DEVICE(s), bus_name);
197 }
198 }
199
200 static void pca954x_class_init(ObjectClass *klass, void *data)
201 {
202 I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
203 ResettableClass *rc = RESETTABLE_CLASS(klass);
204 DeviceClass *dc = DEVICE_CLASS(klass);
205 SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass);
206
207 sc->match_and_add = pca954x_match;
208
209 rc->phases.enter = pca954x_enter_reset;
210
211 dc->desc = "Pca954x i2c-mux";
212
213 k->write_data = pca954x_write_data;
214 k->receive_byte = pca954x_read_byte;
215 }
216
217 static const TypeInfo pca954x_info[] = {
218 {
219 .name = TYPE_PCA954X,
220 .parent = TYPE_SMBUS_DEVICE,
221 .instance_size = sizeof(Pca954xState),
222 .instance_init = pca954x_init,
223 .class_size = sizeof(Pca954xClass),
224 .class_init = pca954x_class_init,
225 .abstract = true,
226 },
227 {
228 .name = TYPE_PCA9546,
229 .parent = TYPE_PCA954X,
230 .class_init = pca9546_class_init,
231 },
232 {
233 .name = TYPE_PCA9548,
234 .parent = TYPE_PCA954X,
235 .class_init = pca9548_class_init,
236 },
237 };
238
239 DEFINE_TYPES(pca954x_info)