[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v1 5/6] hw: pca9548: Device model
From: |
Peter Crosthwaite |
Subject: |
[Qemu-devel] [PATCH v1 5/6] hw: pca9548: Device model |
Date: |
Wed, 20 Feb 2013 15:29:58 +1000 |
Initial version of device model for PCA9548 8 way I2C switch.
Signed-off-by: Peter Crosthwaite <address@hidden>
---
default-configs/arm-softmmu.mak | 1 +
hw/Makefile.objs | 1 +
hw/pca9548.c | 229 +++++++++++++++++++++++++++++++++++++++
3 files changed, 231 insertions(+), 0 deletions(-)
create mode 100644 hw/pca9548.c
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 9114382..27f2a19 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -27,6 +27,7 @@ CONFIG_LAN9118=y
CONFIG_SMC91C111=y
CONFIG_DS1338=y
CONFIG_M24CXX=y
+CONFIG_PCA9548=y
CONFIG_PFLASH_CFI01=y
CONFIG_PFLASH_CFI02=y
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index dc75c9f..56f1c00 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -179,6 +179,7 @@ common-obj-$(CONFIG_MAX111X) += max111x.o
common-obj-$(CONFIG_DS1338) += ds1338.o
common-obj-y += i2c.o smbus.o smbus_eeprom.o
common-obj-$(CONFIG_M24CXX) += m24cxx.o
+common-obj-$(CONFIG_PCA9548) += pca9548.o
common-obj-y += eeprom93xx.o
common-obj-y += scsi-disk.o cdrom.o hd-geometry.o block-common.o
common-obj-y += scsi-generic.o scsi-bus.o
diff --git a/hw/pca9548.c b/hw/pca9548.c
new file mode 100644
index 0000000..c1d57ae
--- /dev/null
+++ b/hw/pca9548.c
@@ -0,0 +1,229 @@
+/*
+ * PCA9548 I2C Switch Dummy model
+ *
+ * Copyright (c) 2012 Xilinx Inc.
+ * Copyright (c) 2012 Peter Crosthwaite <address@hidden>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "i2c.h"
+#include "hw.h"
+#include "sysemu/blockdev.h"
+
+#ifndef PCA9548_DEBUG
+#define PCA9548_DEBUG 0
+#endif
+#define DB_PRINT(fmt, args...) do {\
+ if (PCA9548_DEBUG) {\
+ fprintf(stderr, "PCA9548: "fmt, ## args);\
+ } \
+} while (0);
+
+#define NUM_BUSSES 8
+#define pca9548_CONTROL_ADDR 0x74
+
+typedef struct {
+ I2CSlave i2c;
+ i2c_bus *busses[NUM_BUSSES];
+
+ /*state */
+ uint8_t control_reg;
+ enum i2c_event event;
+ bool control_decoded;
+
+ uint8_t chip_enable; /*property */
+} PCA9548State;
+
+#define TYPE_PCA9548 "pca-9548"
+
+#define PCA9548(obj) \
+ OBJECT_CHECK(PCA9548State, (obj), TYPE_PCA9548)
+
+static void pca9548_reset(DeviceState *dev)
+{
+ PCA9548State *s = PCA9548(dev);
+
+ s->control_reg = 0;
+}
+
+static int pca9548_recv(I2CSlave *i2c)
+{
+ PCA9548State *s = PCA9548(i2c);
+ int i;
+ int ret = 0;
+
+ if (s->control_decoded) {
+ ret |= s->control_reg;
+ DB_PRINT("returning control register: %x\n", ret);
+ } else {
+ for (i = 0; i < NUM_BUSSES; ++i) {
+ if (s->control_reg & (1 << i)) {
+ ret |= i2c_recv(s->busses[i]);
+ DB_PRINT("recieving from active bus %d:%x\n", i, ret);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int pca9548_send(I2CSlave *i2c, uint8_t data)
+{
+ PCA9548State *s = PCA9548(i2c);
+ int i;
+ int ret = -1;
+
+ if (s->control_decoded) {
+ DB_PRINT("setting control register: %x\n", data);
+ s->control_reg = data;
+ ret = 0;
+ } else {
+ for (i = 0; i < NUM_BUSSES; ++i) {
+ if (s->control_reg & (1 << i)) {
+ DB_PRINT("sending to active bus %d:%x\n", i, data);
+ ret &= i2c_send(s->busses[i], data);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void pca9548_event(I2CSlave *i2c, enum i2c_event event)
+{
+ PCA9548State *s = PCA9548(i2c);
+ int i;
+
+ s->event = event;
+ for (i = 0; i < NUM_BUSSES; ++i) {
+ if (s->control_reg & (1 << i)) {
+ switch (event) {
+ /* defer START conditions until we have an address */
+ case I2C_START_SEND:
+ case I2C_START_RECV:
+ break;
+ /* Forward others to sub busses */
+ case I2C_FINISH:
+ if (!s->control_decoded) {
+ DB_PRINT("stopping active bus %d\n", i);
+ i2c_end_transfer(s->busses[i]);
+ }
+ break;
+ case I2C_NACK:
+ if (!s->control_decoded) {
+ DB_PRINT("nacking active bus %d\n", i);
+ i2c_nack(s->busses[i]);
+ }
+ break;
+ }
+ }
+ }
+}
+
+static void pca9548_decode_address(I2CSlave *i2c, uint8_t address)
+{
+ PCA9548State *s = PCA9548(i2c);
+ int i;
+
+ s->control_decoded = address ==
+ (pca9548_CONTROL_ADDR | (s->chip_enable & 0x7));
+ if (s->control_decoded) {
+ return;
+ }
+ for (i = 0; i < NUM_BUSSES; ++i) {
+ if (s->control_reg & (1 << i)) {
+ DB_PRINT("starting active bus %d addr:%02x rnw:%d\n", i, address,
+ s->event == I2C_START_RECV);
+ i2c_start_transfer(s->busses[i], address,
+ s->event == I2C_START_RECV);
+ }
+ }
+}
+
+static void pca9548_init(Object *obj)
+{
+ int i;
+ PCA9548State *s = PCA9548(obj);
+
+ for (i = 0; i < NUM_BUSSES; ++i) {
+ char bus_name[16];
+
+ snprintf(bus_name, sizeof(bus_name), "address@hidden", i);
+ s->busses[i] = i2c_init_bus(DEVICE(s), bus_name);
+ }
+}
+
+static void pca9548_realize(DeviceState *dev, Error **errp)
+{
+ I2CSlave *i2cs = I2C_SLAVE(dev);
+
+ /* Switch decodes the enitre address range, trample any previously set
+ * values for address and range
+ */
+ i2cs->address = 0;
+ i2cs->address_range = 0x80;
+}
+
+static const VMStateDescription vmstate_PCA9548 = {
+ .name = "pca9548",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_I2C_SLAVE(i2c, PCA9548State),
+ VMSTATE_UINT8(control_reg, PCA9548State),
+ VMSTATE_BOOL(control_decoded, PCA9548State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property pca9548_properties[] = {
+ /* These could be GPIOs, but the application is rare, just let machine
model
+ * tie them with props
+ */
+ DEFINE_PROP_UINT8("chip-enable", PCA9548State, chip_enable, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pca9548_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->event = pca9548_event;
+ k->recv = pca9548_recv;
+ k->send = pca9548_send;
+ k->decode_address = pca9548_decode_address;
+
+ dc->realize = pca9548_realize;
+ dc->reset = pca9548_reset;
+ dc->vmsd = &vmstate_PCA9548;
+ dc->props = pca9548_properties;
+}
+
+static TypeInfo pca9548_info = {
+ .name = TYPE_PCA9548,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(PCA9548State),
+ .instance_init = pca9548_init,
+ .class_init = pca9548_class_init,
+};
+
+static void pca9548_register_types(void)
+{
+ type_register_static(&pca9548_info);
+}
+
+type_init(pca9548_register_types)
--
1.7.0.4
- [Qemu-devel] [PATCH v1 0/6] Xilinx Zynq I2C, Peter Crosthwaite, 2013/02/20
- [Qemu-devel] [PATCH v1 2/6] i2c: Add no_init version of i2c_create_slave, Peter Crosthwaite, 2013/02/20
- [Qemu-devel] [PATCH v1 3/6] cadence_i2c: first revision, Peter Crosthwaite, 2013/02/20
- [Qemu-devel] [PATCH v1 4/6] hw: M24Cxx I2C EEPROM device model, Peter Crosthwaite, 2013/02/20
- [Qemu-devel] [PATCH v1 5/6] hw: pca9548: Device model,
Peter Crosthwaite <=
- [Qemu-devel] [PATCH v1 6/6] xilinx_zynq: Add i2c components, Peter Crosthwaite, 2013/02/20