qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

i2c_mux_pca954x.c (6289B)


      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_new(struct I2CNode, 1);
     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)