Skip to contentSkip to conversation

Add MVME3100 PPC SBC

PATCH Applied Michael Davidsaver 3 participants 33 messages Nov 20, 2017 – Dec 6, 2017 1 message not in the archive

lore
Share & tools
lore
#0 lore raw

cover Add MVME3100 PPC SBC

This series adds simulation of MVME3100 powerpc SBCs, originally from Motorola, and now sold by Artesyn[1]. There are two variants differing in CPU speed and memory size.

I've been working on this sporadically for the past 2 year. Recently I've finished all the features which I have in mind. If this series is accepted there is a continuation which adds VME bus. I've found it useful in software compatibility testing. I wonder if there is any interest at large?

There are two main parts of this series. 1-5 are changing code common with the "ppce500" and "mpc8544ds" boards, with the remainder being additions.

The changes are to how the CCSR region is handled in order to support the CCSRBAR register which allows the whole region to be relocated. Also added are a couple of memory and clock configuration registers which RTEMS guests read.

#3 is actually a minor issue I found recently with the mpc8544 PCI host bridge, which I'm uncertain how to address. The host bridge device 0:0 identifies itself as a bridge, but doesn't properly implement the bridge config registers. This confuses Linux, which then does a full re-enumeration (successfully).

The rest are additions of an I2C controller, an I2C eeprom, an I2C RTC, and new board code.

My testing has been almost exclusively with an RTEMS guest[2]. Though I have recently done a little with Linux.

RTEMS guests (and Linux too for now) require a stub bootloader[3] to put the system in the same state as the real bootloader. RTEMS has an unfortunately strong dependence on bootloader provided configuration (eg. it doesn't re-enumerate the PCI bus).

[1] https://www.artesyn.com/computing/products/product/mvme3100-vme-board-with-freescale-mpc8540-system-on-chip-processor

[2] https://www.rtems.org/

[3] https://github.com/mdavidsaver/qemu/wiki

Michael Davidsaver (12):
e500: add board config options
e500: consolidate mpc8540 guts with e500-ccsr
e500: note possible bug with host bridge
e500: additional CCSR registers
e500: name openpic and pci host bridge
i2c: add mpc8540 i2c controller
qtest: add e500_i2c_create()
e500: add mpc8540 i2c controller to ccsr
nvram: add AT24Cx i2c eeprom
timer: add ds1375 RTC
ppc: add mvme3100 machine
tests: add mvme3100-test

 default-configs/ppc-softmmu.mak |   1 +
 hw/i2c/Makefile.objs            |   1 +
 hw/i2c/mpc8540_i2c.c            | 287 +++++++++++++++++
 hw/nvram/Makefile.objs          |   1 +
 hw/nvram/eeprom_at24c.c         | 205 ++++++++++++
 hw/pci-host/ppce500.c           |  13 +-
 hw/ppc/Makefile.objs            |   4 +-
 hw/ppc/e500-ccsr.h              |  17 -
 hw/ppc/e500.c                   |  59 ++--
 hw/ppc/e500.h                   |   4 +
 hw/ppc/e500_ccsr.c              | 220 +++++++++++++
 hw/ppc/e500plat.c               |   2 +
 hw/ppc/mpc8544_guts.c           | 143 ---------
 hw/ppc/mpc8544ds.c              |   2 +
 hw/ppc/mvme3100.c               | 688 ++++++++++++++++++++++++++++++++++++++++
 hw/ppc/mvme3100_cpld.c          | 192 +++++++++++
 hw/timer/Makefile.objs          |   1 +
 hw/timer/ds1375-i2c.c           | 293 +++++++++++++++++
 tests/Makefile.include          |   4 +
 tests/libqos/i2c-e500.c         |  66 ++++
 tests/libqos/i2c.h              |   3 +
 tests/mvme3100-test.c           |  79 +++++
 22 files changed, 2083 insertions(+), 202 deletions(-)
 create mode 100644 hw/i2c/mpc8540_i2c.c
 create mode 100644 hw/nvram/eeprom_at24c.c
 delete mode 100644 hw/ppc/e500-ccsr.h
 create mode 100644 hw/ppc/e500_ccsr.c
 delete mode 100644 hw/ppc/mpc8544_guts.c
 create mode 100644 hw/ppc/mvme3100.c
 create mode 100644 hw/ppc/mvme3100_cpld.c
 create mode 100644 hw/timer/ds1375-i2c.c
 create mode 100644 tests/libqos/i2c-e500.c
 create mode 100644 tests/mvme3100-test.c
Signature · 1 line
2.11.0
#1 lore raw

PATCH 1/12 e500: add board config options

allow board code to skip common NIC and guest image setup and configure decrementor frequency.
Existing boards unchanged.

---
 hw/ppc/e500.c      | 8 ++++++--
 hw/ppc/e500.h      | 3 +++
 hw/ppc/e500plat.c  | 1 +
 hw/ppc/mpc8544ds.c | 1 +
 4 files changed, 11 insertions(+), 2 deletions(-)
hw/ppc/e500.c +6 -2
index 5cf0dabef3..9e7e1b29c4 100644
@@ -826,7 +826,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         env->mpic_iack = params->ccsrbar_base +
                          MPC8544_MPIC_REGS_OFFSET + 0xa0;
 
-        ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500);
+        ppc_booke_timers_init(cpu, params->decrementor_freq, PPC_TIMER_E500);
 
         /* Register reset handler */
         if (!i) {
@@ -899,7 +899,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     if (!pci_bus)
         printf("couldn't create PCI controller!\n");
 
-    if (pci_bus) {
+    if (pci_bus && !params->tsec_nic) {
         /* Register network interfaces. */
         for (i = 0; i < nb_nics; i++) {
             pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
@@ -948,6 +948,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
                                     sysbus_mmio_get_region(s, 0));
     }
 
+    if (params->skip_load) {
+        return;
+    }
+
     /* Load kernel. */
     if (machine->kernel_filename) {
         kernel_base = cur_base;
hw/ppc/e500.h +3 -0
index 70ba1d8f4f..40f72f2de2 100644
@@ -22,6 +22,9 @@ typedef struct PPCE500Params {
     hwaddr pci_mmio_base;
     hwaddr pci_mmio_bus_base;
     hwaddr spin_base;
+    uint32_t decrementor_freq; /* in Hz */
+    bool skip_load;
+    bool tsec_nic;
 } PPCE500Params;
 
 void ppce500_init(MachineState *machine, PPCE500Params *params);
hw/ppc/e500plat.c +1 -0
index e59e80fb9e..3d07987bd1 100644
@@ -47,6 +47,7 @@ static void e500plat_init(MachineState *machine)
         .pci_mmio_base = 0xC00000000ULL,
         .pci_mmio_bus_base = 0xE0000000ULL,
         .spin_base = 0xFEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     /* Older KVM versions don't support EPR which breaks guests when we announce
hw/ppc/mpc8544ds.c +1 -0
index 1717953ec7..6d9931c475 100644
@@ -40,6 +40,7 @@ static void mpc8544ds_init(MachineState *machine)
         .pci_mmio_bus_base = 0xC0000000ULL,
         .pci_pio_base = 0xE1000000ULL,
         .spin_base = 0xEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     if (machine->ram_size > 0xc0000000) {
Signature · 1 line
2.11.0
#2 lore raw

Re: [Qemu-devel] [PATCH 01/12] e500: add board config options

Quoted 5 lines · Michael Davidsaver

allow board code to skip common NIC and guest image setup and configure decrementor frequency.
Existing boards unchanged.

So, it's spelled "decrementer".

Other than that, the patch looks correct. However having a big common function for overall init with a pile of ad-hoc configuration parameters is usually not a great way to go. I think what we want instead is to eliminate ppce500_init(), instead doing the setup logic
separately in each of the e500 machines. The large common slabs of
code can be helpers in e500.c, but the overall logic - including most of the things controlled by the current params - would be under the individual machine's control.

Quoted 78 lines
---
 hw/ppc/e500.c      | 8 ++++++--
 hw/ppc/e500.h      | 3 +++
 hw/ppc/e500plat.c  | 1 +
 hw/ppc/mpc8544ds.c | 1 +
 4 files changed, 11 insertions(+), 2 deletions(-)
hw/ppc/e500.c +6 -2
index 5cf0dabef3..9e7e1b29c4 100644
@@ -826,7 +826,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         env->mpic_iack = params->ccsrbar_base +
                          MPC8544_MPIC_REGS_OFFSET + 0xa0;
 
-        ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500);
+        ppc_booke_timers_init(cpu, params->decrementor_freq, PPC_TIMER_E500);
 
         /* Register reset handler */
         if (!i) {
@@ -899,7 +899,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     if (!pci_bus)
         printf("couldn't create PCI controller!\n");
 
-    if (pci_bus) {
+    if (pci_bus && !params->tsec_nic) {
         /* Register network interfaces. */
         for (i = 0; i < nb_nics; i++) {
             pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
@@ -948,6 +948,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
                                     sysbus_mmio_get_region(s, 0));
     }
 
+    if (params->skip_load) {
+        return;
+    }
+
     /* Load kernel. */
     if (machine->kernel_filename) {
         kernel_base = cur_base;
hw/ppc/e500.h +3 -0
index 70ba1d8f4f..40f72f2de2 100644
@@ -22,6 +22,9 @@ typedef struct PPCE500Params {
     hwaddr pci_mmio_base;
     hwaddr pci_mmio_bus_base;
     hwaddr spin_base;
+    uint32_t decrementor_freq; /* in Hz */
+    bool skip_load;
+    bool tsec_nic;
 } PPCE500Params;
 
 void ppce500_init(MachineState *machine, PPCE500Params *params);
hw/ppc/e500plat.c +1 -0
index e59e80fb9e..3d07987bd1 100644
@@ -47,6 +47,7 @@ static void e500plat_init(MachineState *machine)
         .pci_mmio_base = 0xC00000000ULL,
         .pci_mmio_bus_base = 0xE0000000ULL,
         .spin_base = 0xFEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     /* Older KVM versions don't support EPR which breaks guests when we announce
hw/ppc/mpc8544ds.c +1 -0
index 1717953ec7..6d9931c475 100644
@@ -40,6 +40,7 @@ static void mpc8544ds_init(MachineState *machine)
         .pci_mmio_bus_base = 0xC0000000ULL,
         .pci_pio_base = 0xE1000000ULL,
         .spin_base = 0xEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     if (machine->ram_size > 0xc0000000) {
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#3 lore raw

Re: [Qemu-devel] [PATCH 01/12] e500: add board config options

Quoted 9 lines · David Gibson
Quoted 5 lines · Michael Davidsaver

allow board code to skip common NIC and guest image setup and configure decrementor frequency.
Existing boards unchanged.

So, it's spelled "decrementer".

Quoted 8 lines

Other than that, the patch looks correct. However having a big common function for overall init with a pile of ad-hoc configuration parameters is usually not a great way to go. I think what we want instead is to eliminate ppce500_init(), instead doing the setup logic
separately in each of the e500 machines. The large common slabs of
code can be helpers in e500.c, but the overall logic - including most of the things controlled by the current params - would be under the individual machine's control.

I agree that ppce500_init() is unwieldy, and am willing to spend some time on cleanup. I'm just not sure what to do.

I can see moving initialization of at least the serial, i2c, gpio, and possibly MPIC and PCI host bridge into the e500-ccsr class.

I'm not sure what to do with all the device tree construction code in e500.c, which has a bunch of hard coded register offsets. A comment here summarizes the problem nicely.

Quoted 3 lines

/* TODO: parameterize */
#define MPC8544_CCSRBAR_SIZE 0x00100000ULL
#define MPC8544_MPIC_REGS_OFFSET 0x40000ULL

It seems desirable to avoid having these offsets appear in two different files, which could allow them to get out of sync.

I had the idea of using an Interface to split up device tree construction, and was encouraged to find PnvXScomInterfaceClass which seems be a way of combining device tree construction in a device class. Is this the way to go?

Some guidance would be appreciated.

Michael

Quoted 79 lines
Quoted 78 lines
---
 hw/ppc/e500.c      | 8 ++++++--
 hw/ppc/e500.h      | 3 +++
 hw/ppc/e500plat.c  | 1 +
 hw/ppc/mpc8544ds.c | 1 +
 4 files changed, 11 insertions(+), 2 deletions(-)
hw/ppc/e500.c +6 -2
index 5cf0dabef3..9e7e1b29c4 100644
@@ -826,7 +826,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         env->mpic_iack = params->ccsrbar_base +
                          MPC8544_MPIC_REGS_OFFSET + 0xa0;
 
-        ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500);
+        ppc_booke_timers_init(cpu, params->decrementor_freq, PPC_TIMER_E500);
 
         /* Register reset handler */
         if (!i) {
@@ -899,7 +899,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     if (!pci_bus)
         printf("couldn't create PCI controller!\n");
 
-    if (pci_bus) {
+    if (pci_bus && !params->tsec_nic) {
         /* Register network interfaces. */
         for (i = 0; i < nb_nics; i++) {
             pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
@@ -948,6 +948,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
                                     sysbus_mmio_get_region(s, 0));
     }
 
+    if (params->skip_load) {
+        return;
+    }
+
     /* Load kernel. */
     if (machine->kernel_filename) {
         kernel_base = cur_base;
hw/ppc/e500.h +3 -0
index 70ba1d8f4f..40f72f2de2 100644
@@ -22,6 +22,9 @@ typedef struct PPCE500Params {
     hwaddr pci_mmio_base;
     hwaddr pci_mmio_bus_base;
     hwaddr spin_base;
+    uint32_t decrementor_freq; /* in Hz */
+    bool skip_load;
+    bool tsec_nic;
 } PPCE500Params;
 
 void ppce500_init(MachineState *machine, PPCE500Params *params);
hw/ppc/e500plat.c +1 -0
index e59e80fb9e..3d07987bd1 100644
@@ -47,6 +47,7 @@ static void e500plat_init(MachineState *machine)
         .pci_mmio_base = 0xC00000000ULL,
         .pci_mmio_bus_base = 0xE0000000ULL,
         .spin_base = 0xFEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     /* Older KVM versions don't support EPR which breaks guests when we announce
hw/ppc/mpc8544ds.c +1 -0
index 1717953ec7..6d9931c475 100644
@@ -40,6 +40,7 @@ static void mpc8544ds_init(MachineState *machine)
         .pci_mmio_bus_base = 0xC0000000ULL,
         .pci_pio_base = 0xE1000000ULL,
         .spin_base = 0xEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     if (machine->ram_size > 0xc0000000) {
#4 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 01/12] e500: add board config options

Quoted 19 lines · Michael Davidsaver
Quoted 9 lines · David Gibson
Quoted 5 lines · Michael Davidsaver

allow board code to skip common NIC and guest image setup and configure decrementor frequency.
Existing boards unchanged.

So, it's spelled "decrementer".

Quoted 8 lines

Other than that, the patch looks correct. However having a big common function for overall init with a pile of ad-hoc configuration parameters is usually not a great way to go. I think what we want instead is to eliminate ppce500_init(), instead doing the setup logic
separately in each of the e500 machines. The large common slabs of
code can be helpers in e500.c, but the overall logic - including most of the things controlled by the current params - would be under the individual machine's control.

Quoted 5 lines

I agree that ppce500_init() is unwieldy, and am willing to spend some time on cleanup. I'm just not sure what to do.

I can see moving initialization of at least the serial, i2c, gpio, and possibly MPIC and PCI host bridge into the e500-ccsr class.

each of these controllers should have a QOM model possibly.

I think that the part above :

	    /* Load kernel. */

is machine specific and the part below is generic and could be called from each machine init routine. You would still need some op/struct for the device tree.

Quoted 8 lines

I'm not sure what to do with all the device tree construction code in e500.c, which has a bunch of hard coded register offsets. A comment here summarizes the problem nicely.

Quoted 3 lines

/* TODO: parameterize */
#define MPC8544_CCSRBAR_SIZE 0x00100000ULL
#define MPC8544_MPIC_REGS_OFFSET 0x40000ULL

Quoted 7 lines

It seems desirable to avoid having these offsets appear in two different files, which could allow them to get out of sync.

I had the idea of using an Interface to split up device tree construction, and was encouraged to find PnvXScomInterfaceClass which seems be a way of combining device tree construction in a device class. Is this the way to go?

yes. You would need to QOM'ify the whole machine and have all controllers be children of a 'board' object to loop on them.

Quoted 1 line

Some guidance would be appreciated.

The models of the ARM SoC are good examples to follow. You could check the Aspeed machines which have been slowly growing and which have had some good reviews from Peter. The ARM machines are all QOM'ified and that might be difficult to apply to the e500 boards quickly. At least, the models for the new controllers should follow QOM. It will ease the future work.

Thanks,

C.

Quoted 85 lines

Michael

Quoted 79 lines
Quoted 78 lines
---
 hw/ppc/e500.c      | 8 ++++++--
 hw/ppc/e500.h      | 3 +++
 hw/ppc/e500plat.c  | 1 +
 hw/ppc/mpc8544ds.c | 1 +
 4 files changed, 11 insertions(+), 2 deletions(-)
hw/ppc/e500.c +6 -2
index 5cf0dabef3..9e7e1b29c4 100644
@@ -826,7 +826,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
         env->mpic_iack = params->ccsrbar_base +
                          MPC8544_MPIC_REGS_OFFSET + 0xa0;
 
-        ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500);
+        ppc_booke_timers_init(cpu, params->decrementor_freq, PPC_TIMER_E500);
 
         /* Register reset handler */
         if (!i) {
@@ -899,7 +899,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     if (!pci_bus)
         printf("couldn't create PCI controller!\n");
 
-    if (pci_bus) {
+    if (pci_bus && !params->tsec_nic) {
         /* Register network interfaces. */
         for (i = 0; i < nb_nics; i++) {
             pci_nic_init_nofail(&nd_table[i], pci_bus, "virtio", NULL);
@@ -948,6 +948,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
                                     sysbus_mmio_get_region(s, 0));
     }
 
+    if (params->skip_load) {
+        return;
+    }
+
     /* Load kernel. */
     if (machine->kernel_filename) {
         kernel_base = cur_base;
hw/ppc/e500.h +3 -0
index 70ba1d8f4f..40f72f2de2 100644
@@ -22,6 +22,9 @@ typedef struct PPCE500Params {
     hwaddr pci_mmio_base;
     hwaddr pci_mmio_bus_base;
     hwaddr spin_base;
+    uint32_t decrementor_freq; /* in Hz */
+    bool skip_load;
+    bool tsec_nic;
 } PPCE500Params;
 
 void ppce500_init(MachineState *machine, PPCE500Params *params);
hw/ppc/e500plat.c +1 -0
index e59e80fb9e..3d07987bd1 100644
@@ -47,6 +47,7 @@ static void e500plat_init(MachineState *machine)
         .pci_mmio_base = 0xC00000000ULL,
         .pci_mmio_bus_base = 0xE0000000ULL,
         .spin_base = 0xFEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     /* Older KVM versions don't support EPR which breaks guests when we announce
hw/ppc/mpc8544ds.c +1 -0
index 1717953ec7..6d9931c475 100644
@@ -40,6 +40,7 @@ static void mpc8544ds_init(MachineState *machine)
         .pci_mmio_bus_base = 0xC0000000ULL,
         .pci_pio_base = 0xE1000000ULL,
         .spin_base = 0xEF000000ULL,
+        .decrementor_freq = 400000000,
     };
 
     if (machine->ram_size > 0xc0000000) {
#5 lore raw

Re: [Qemu-devel] [PATCH 01/12] e500: add board config options

In reply to Michael Davidsaver
Quoted 22 lines · Michael Davidsaver
Quoted 9 lines · David Gibson
Quoted 5 lines · Michael Davidsaver

allow board code to skip common NIC and guest image setup and configure decrementor frequency.
Existing boards unchanged.

So, it's spelled "decrementer".

Quoted 8 lines

Other than that, the patch looks correct. However having a big common function for overall init with a pile of ad-hoc configuration parameters is usually not a great way to go. I think what we want instead is to eliminate ppce500_init(), instead doing the setup logic
separately in each of the e500 machines. The large common slabs of
code can be helpers in e500.c, but the overall logic - including most of the things controlled by the current params - would be under the individual machine's control.

I agree that ppce500_init() is unwieldy, and am willing to spend some time on cleanup. I'm just not sure what to do.

Quoted 6 lines

I can see moving initialization of at least the serial, i2c, gpio, and possibly MPIC and PCI host bridge into the e500-ccsr class.

I'm not sure what to do with all the device tree construction code in e500.c, which has a bunch of hard coded register offsets. A comment here summarizes the problem nicely.

So, those aren't bad ideas for cleanups, but I'm not going to insist on that before merging this stuff. What I'm after is something simpler. At the moment the structure looks something like this:

	machine_init() {
		common_init(big pile of parameters);
	}

	common_init() {
		chunk1;
		if (param1)
			chunk2;
		chunk3;
		if (param2)
			chunk4;
		...
	}

What I would prefer is:

	machine_init1() {
		chunk1();
		chunk2(param1)
		chunk3(param3);
	}

	machine_init2() {
		chunk1();
		chunk3(param3);
		chunk4(param2);
	}
Quoted 5 lines
Quoted 4 lines

/* TODO: parameterize */
#define MPC8544_CCSRBAR_SIZE 0x00100000ULL
#define MPC8544_MPIC_REGS_OFFSET 0x40000ULL

Quoted 7 lines

It seems desirable to avoid having these offsets appear in two different files, which could allow them to get out of sync.

I had the idea of using an Interface to split up device tree construction, and was encouraged to find PnvXScomInterfaceClass which seems be a way of combining device tree construction in a device class. Is this the way to go?

At some point, quite likely (I've actually had thoughts on making devicetree construction more convenient qemu wide, but haven't had time to do much about it).

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#6 lore raw

PATCH 3/12 e500: note possible bug with host bridge

In reply to Michael Davidsaver
---
 hw/pci-host/ppce500.c | 3 +++
 1 file changed, 3 insertions(+)
hw/pci-host/ppce500.c +3 -0
index f2d108bc8a..0e2833bd98 100644
@@ -424,6 +424,9 @@ static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp)
     MemoryRegion *ccsr_mr = sysbus_mmio_get_region(ccsr, 0);
 
     pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
+    /* BUG? identifies as PCI_HEADER_TYPE_BRIDGE but uses
+     * standard device config read/write
+     */
     d->config[PCI_HEADER_TYPE] =
         (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
         PCI_HEADER_TYPE_BRIDGE;
Signature · 1 line
2.11.0
#7 lore raw

Re: [Qemu-devel] [PATCH 03/12] e500: note possible bug with host bridge

Quoted 1 line · Michael Davidsaver

I'm not sure if you're saying you think there is a hardware bug which we're faithfully emulating, or a software bug.

Quoted 18 lines
---
 hw/pci-host/ppce500.c | 3 +++
 1 file changed, 3 insertions(+)
hw/pci-host/ppce500.c +3 -0
index f2d108bc8a..0e2833bd98 100644
@@ -424,6 +424,9 @@ static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp)
     MemoryRegion *ccsr_mr = sysbus_mmio_get_region(ccsr, 0);
 
     pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
+    /* BUG? identifies as PCI_HEADER_TYPE_BRIDGE but uses
+     * standard device config read/write
+     */
     d->config[PCI_HEADER_TYPE] =
         (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
         PCI_HEADER_TYPE_BRIDGE;
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#8 lore raw

Re: [Qemu-devel] [PATCH 03/12] e500: note possible bug with host bridge

Quoted 5 lines · David Gibson
Quoted 1 line · Michael Davidsaver

I'm not sure if you're saying you think there is a hardware bug which we're faithfully emulating, or a software bug.

I mean that the emulation is incorrect in that it just sets config[PCI_HEADER_TYPE]==PCI_HEADER_TYPE_BRIDGE but does none of the other initialization of the base-pci-bridge class.

I specifically observed Linux being confused by the fact that the primary, secondary, and subordinate bus registers don't work right because they're actually the BAR2 address register.

Further, it seems odd that a host bridge would identify itself as a pci-to-pci bridge. The mpc8540 doesn't. The mpc8544 docs aren't clear, and I don't have a real one to test. My inclination is to remove the line changing PCI_HEADER_TYPE, but I'm hesitant about breaking things. Especially since this doesn't trigger mis-behavior in Linux or RTEMS.

Quoted 19 lines
Quoted 18 lines
---
 hw/pci-host/ppce500.c | 3 +++
 1 file changed, 3 insertions(+)
hw/pci-host/ppce500.c +3 -0
index f2d108bc8a..0e2833bd98 100644
@@ -424,6 +424,9 @@ static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp)
     MemoryRegion *ccsr_mr = sysbus_mmio_get_region(ccsr, 0);
 
     pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
+    /* BUG? identifies as PCI_HEADER_TYPE_BRIDGE but uses
+     * standard device config read/write
+     */
     d->config[PCI_HEADER_TYPE] =
         (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
         PCI_HEADER_TYPE_BRIDGE;
#9 lore raw

PATCH 4/12 e500: additional CCSR registers

In reply to Michael Davidsaver

Add CCSRBAR to allow CCSR region to be relocated.
Guest memory size introspection.
Dummy RAM error controls.
Guest clock introspection.

---
 hw/ppc/e500.c      |  2 ++
 hw/ppc/e500.h      |  1 +
 hw/ppc/e500_ccsr.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/ppc/e500plat.c  |  1 +
 hw/ppc/mpc8544ds.c |  1 +
 5 files changed, 75 insertions(+), 2 deletions(-)
hw/ppc/e500.c +2 -0
index 474a46a985..057be1751b 100644
@@ -853,7 +853,9 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     dev = qdev_create(NULL, "e500-ccsr");
     object_property_add_child(qdev_get_machine(), "e500-ccsr",
                               OBJECT(dev), NULL);
+    qdev_prop_set_uint32(dev, "porpllsr", params->porpllsr);
     qdev_prop_set_uint32(dev, "base", params->ccsrbar_base);
+    qdev_prop_set_uint32(dev, "ram-size", ram_size);
     qdev_init_nofail(dev);
     ccsr_addr_space = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 
hw/ppc/e500.h +1 -0
index 40f72f2de2..1f39095dfa 100644
@@ -22,6 +22,7 @@ typedef struct PPCE500Params {
     hwaddr pci_mmio_base;
     hwaddr pci_mmio_bus_base;
     hwaddr spin_base;
+    uint32_t porpllsr; /* value of PORPLLSR register */
     uint32_t decrementor_freq; /* in Hz */
     bool skip_load;
     bool tsec_nic;
hw/ppc/e500_ccsr.c +70 -2

129 lines not on this page · raw message

hw/ppc/e500plat.c +1 -0
index 3d07987bd1..4e763d9c09 100644
@@ -47,6 +47,7 @@ static void e500plat_init(MachineState *machine)
         .pci_mmio_base = 0xC00000000ULL,
         .pci_mmio_bus_base = 0xE0000000ULL,
         .spin_base = 0xFEF000000ULL,
+        .porpllsr = 0, /* TODO missing valid value */
         .decrementor_freq = 400000000,
     };
 
hw/ppc/mpc8544ds.c +1 -0
index 6d9931c475..f5e0042245 100644
@@ -40,6 +40,7 @@ static void mpc8544ds_init(MachineState *machine)
         .pci_mmio_bus_base = 0xC0000000ULL,
         .pci_pio_base = 0xE1000000ULL,
         .spin_base = 0xEF000000ULL,
+        .porpllsr = 0, /* TODO missing valid value */
         .decrementor_freq = 400000000,
     };
 
Signature · 1 line
2.11.0
Reviewed-by on v1 4/12
#10 lore raw

Re: [Qemu-devel] [PATCH 04/12] e500: additional CCSR registers

Quoted 6 lines · Michael Davidsaver

Add CCSRBAR to allow CCSR region to be relocated.
Guest memory size introspection.
Dummy RAM error controls.
Guest clock introspection.

Looks fine from the POV of someone who doesn't know details about e500.

Quoted 197 lines
---
 hw/ppc/e500.c      |  2 ++
 hw/ppc/e500.h      |  1 +
 hw/ppc/e500_ccsr.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/ppc/e500plat.c  |  1 +
 hw/ppc/mpc8544ds.c |  1 +
 5 files changed, 75 insertions(+), 2 deletions(-)
hw/ppc/e500.c +2 -0
index 474a46a985..057be1751b 100644
@@ -853,7 +853,9 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     dev = qdev_create(NULL, "e500-ccsr");
     object_property_add_child(qdev_get_machine(), "e500-ccsr",
                               OBJECT(dev), NULL);
+    qdev_prop_set_uint32(dev, "porpllsr", params->porpllsr);
     qdev_prop_set_uint32(dev, "base", params->ccsrbar_base);
+    qdev_prop_set_uint32(dev, "ram-size", ram_size);
     qdev_init_nofail(dev);
     ccsr_addr_space = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 
hw/ppc/e500.h +1 -0
index 40f72f2de2..1f39095dfa 100644
@@ -22,6 +22,7 @@ typedef struct PPCE500Params {
     hwaddr pci_mmio_base;
     hwaddr pci_mmio_bus_base;
     hwaddr spin_base;
+    uint32_t porpllsr; /* value of PORPLLSR register */
     uint32_t decrementor_freq; /* in Hz */
     bool skip_load;
     bool tsec_nic;
hw/ppc/e500_ccsr.c +70 -2
index 1b586c3f42..c58b17f06b 100644
@@ -31,6 +31,16 @@
 
 /* E500_ denotes registers common to all */
 
+#define E500_CCSRBAR     (0)
+
+#define E500_CS0_BNDS    (0x2000)
+
+#define E500_CS0_CONFIG  (0x2080)
+
+#define E500_ERR_DETECT  (0x2e40)
+#define E500_ERR_DISABLE (0x2e44)
+
+#define E500_PORPLLSR    (0xE0000)
 #define E500_PVR         (0xE00A0)
 #define E500_SVR         (0xE00A4)
 
@@ -44,7 +54,11 @@ typedef struct {
 
     MemoryRegion iomem;
 
-    uint32_t defbase;
+    uint32_t defbase, base;
+    uint32_t ram_size;
+    uint32_t merrd;
+
+    uint32_t porpllsr;
 } CCSRState;
 
 #define TYPE_E500_CCSR "e500-ccsr"
@@ -53,10 +67,28 @@ typedef struct {
 static uint64_t e500_ccsr_read(void *opaque, hwaddr addr,
                                   unsigned size)
 {
+    CCSRState *ccsr = opaque;
     PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
     CPUPPCState *env = &cpu->env;
 
     switch (addr) {
+    case E500_CCSRBAR:
+        return ccsr->base >> 12;
+    case E500_CS0_BNDS:
+        /* we model all RAM in a single chip with addresses [0, ram_size) */
+        return (ccsr->ram_size - 1) >> 24;
+    case E500_CS0_CONFIG:
+        return 1 << 31;
+    case E500_ERR_DETECT:
+        return 0; /* (errors not modeled) */
+    case E500_ERR_DISABLE:
+        return ccsr->merrd;
+    case E500_PORPLLSR:
+        if (!ccsr->porpllsr) {
+            qemu_log_mask(LOG_UNIMP,
+                          "Machine does not provide valid PORPLLSR\n");
+        }
+        return ccsr->porpllsr;
     case E500_PVR:
         return env->spr[SPR_PVR];
     case E500_SVR:
@@ -72,10 +104,22 @@ static uint64_t e500_ccsr_read(void *opaque, hwaddr addr,
 static void e500_ccsr_write(void *opaque, hwaddr addr,
                                uint64_t value, unsigned size)
 {
+    CCSRState *ccsr = opaque;
     PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
     CPUPPCState *env = &cpu->env;
     uint32_t svr = env->spr[SPR_E500_SVR] >> 16;
 
+    switch (addr) {
+    case E500_CCSRBAR:
+        value &= 0x000fff00;
+        ccsr->base = value << 12;
+        sysbus_mmio_map(SYS_BUS_DEVICE(ccsr), 0, ccsr->base);
+        return;
+    case E500_ERR_DISABLE:
+        ccsr->merrd = value & 0xd;
+        return;
+    }
+
     switch (svr) {
     case 0: /* generic.  assumed to be mpc8544ds or e500plat board */
     case 0x8034: /* mpc8544 */
@@ -104,11 +148,20 @@ static const MemoryRegionOps e500_ccsr_ops = {
     }
 };
 
+static int e500_ccsr_post_load(void *opaque, int version_id)
+{
+    CCSRState *ccsr = opaque;
+
+    sysbus_mmio_map(SYS_BUS_DEVICE(ccsr), 0, ccsr->base);
+    return 0;
+}
+
 static void e500_ccsr_reset(DeviceState *dev)
 {
     CCSRState *ccsr = E500_CCSR(dev);
 
-    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ccsr->defbase);
+    ccsr->base = ccsr->defbase;
+    e500_ccsr_post_load(ccsr, 1);
 }
 
 static void e500_ccsr_initfn(Object *obj)
@@ -123,15 +176,30 @@ static void e500_ccsr_initfn(Object *obj)
 
 static Property e500_ccsr_props[] = {
     DEFINE_PROP_UINT32("base", CCSRState, defbase, 0xff700000),
+    DEFINE_PROP_UINT32("ram-size", CCSRState, ram_size, 0),
+    DEFINE_PROP_UINT32("porpllsr", CCSRState, porpllsr, 0),
     DEFINE_PROP_END_OF_LIST()
 };
 
+static const VMStateDescription vmstate_e500_ccsr = {
+    .name = TYPE_E500_CCSR,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = e500_ccsr_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(base, CCSRState),
+        VMSTATE_UINT32(merrd, CCSRState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static
 void e500_ccsr_class_initfn(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->props = e500_ccsr_props;
+    dc->vmsd = &vmstate_e500_ccsr;
     dc->reset = e500_ccsr_reset;
 }
 
hw/ppc/e500plat.c +1 -0
index 3d07987bd1..4e763d9c09 100644
@@ -47,6 +47,7 @@ static void e500plat_init(MachineState *machine)
         .pci_mmio_base = 0xC00000000ULL,
         .pci_mmio_bus_base = 0xE0000000ULL,
         .spin_base = 0xFEF000000ULL,
+        .porpllsr = 0, /* TODO missing valid value */
         .decrementor_freq = 400000000,
     };
 
hw/ppc/mpc8544ds.c +1 -0
index 6d9931c475..f5e0042245 100644
@@ -40,6 +40,7 @@ static void mpc8544ds_init(MachineState *machine)
         .pci_mmio_bus_base = 0xC0000000ULL,
         .pci_pio_base = 0xE1000000ULL,
         .spin_base = 0xEF000000ULL,
+        .porpllsr = 0, /* TODO missing valid value */
         .decrementor_freq = 400000000,
     };
 
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#11 lore raw

PATCH 5/12 e500: name openpic and pci host bridge

In reply to Michael Davidsaver
---
 hw/ppc/e500.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/ppc/e500.c +4 -0
index 057be1751b..6f77844303 100644
@@ -685,6 +685,8 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params,
     int i, j, k;
 
     dev = qdev_create(NULL, TYPE_OPENPIC);
+    object_property_add_child(qdev_get_machine(), "pic", OBJECT(dev),
+                              &error_fatal);
     qdev_prop_set_uint32(dev, "model", params->mpic_version);
     qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus);
 
@@ -876,6 +878,8 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
 
     /* PCI */
     dev = qdev_create(NULL, "e500-pcihost");
+    object_property_add_child(qdev_get_machine(), "pci-host", OBJECT(dev),
+                              &error_abort);
     qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot);
     qdev_prop_set_uint32(dev, "first_pin_irq", pci_irq_nrs[0]);
     qdev_init_nofail(dev);
Signature · 1 line
2.11.0
Applied
#12 lore raw

Re: [Qemu-devel] [PATCH 05/12] e500: name openpic and pci host bridge

Quoted 1 line · Michael Davidsaver

Applied to ppc-for-2.12.

Quoted 26 lines
---
 hw/ppc/e500.c | 4 ++++
 1 file changed, 4 insertions(+)
hw/ppc/e500.c +4 -0
index 057be1751b..6f77844303 100644
@@ -685,6 +685,8 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params,
     int i, j, k;
 
     dev = qdev_create(NULL, TYPE_OPENPIC);
+    object_property_add_child(qdev_get_machine(), "pic", OBJECT(dev),
+                              &error_fatal);
     qdev_prop_set_uint32(dev, "model", params->mpic_version);
     qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus);
 
@@ -876,6 +878,8 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
 
     /* PCI */
     dev = qdev_create(NULL, "e500-pcihost");
+    object_property_add_child(qdev_get_machine(), "pci-host", OBJECT(dev),
+                              &error_abort);
     qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot);
     qdev_prop_set_uint32(dev, "first_pin_irq", pci_irq_nrs[0]);
     qdev_init_nofail(dev);
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#13 lore raw

PATCH 6/12 i2c: add mpc8540 i2c controller

In reply to Michael Davidsaver
---
 hw/i2c/Makefile.objs |   1 +
 hw/i2c/mpc8540_i2c.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 288 insertions(+)
 create mode 100644 hw/i2c/mpc8540_i2c.c
hw/i2c/Makefile.objs +1 -0
index 0594dea3ae..79af1dd901 100644
@@ -9,3 +9,4 @@ common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
 obj-$(CONFIG_OMAP) += omap_i2c.o
 obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o
+obj-$(CONFIG_E500) += mpc8540_i2c.o
hw/i2c/mpc8540_i2c.c new file +287 -0

287 lines not on this page · raw message

Signature · 1 line
2.11.0
#14 lore raw

Re: [Qemu-devel] [PATCH 06/12] i2c: add mpc8540 i2c controller

Quoted 1 line · Michael Davidsaver

I can't speak to the accuracy of the emulation, but it's presumably better than nothing at all. Therefore, applied to ppc-for-2.12.

Quoted 308 lines
---
 hw/i2c/Makefile.objs |   1 +
 hw/i2c/mpc8540_i2c.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 288 insertions(+)
 create mode 100644 hw/i2c/mpc8540_i2c.c
hw/i2c/Makefile.objs +1 -0
index 0594dea3ae..79af1dd901 100644
@@ -9,3 +9,4 @@ common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
 obj-$(CONFIG_OMAP) += omap_i2c.o
 obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o
+obj-$(CONFIG_E500) += mpc8540_i2c.o
hw/i2c/mpc8540_i2c.c new file +287 -0
new file mode 100644
index 0000000000..884052cc9b
@@ -0,0 +1,287 @@
+/*
+ * MPC8540 I2C bus interface
+ * As described in
+ * MPC8540 PowerQUICC III Integrated Host Processor Reference Manual, Rev. 1
+ * Part 2 chapter 11
+ *
+ * Copyright (c) 2015 Michael Davidsaver
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the LICENSE file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/hw.h"
+#include "hw/registerfields.h"
+#include "hw/i2c/i2c.h"
+#include "hw/sysbus.h"
+
+/* #define DEBUG_LVL 0 */
+
+#ifdef DEBUG_LVL
+#define DPRINTK(LVL, FMT, ...) do { if ((LVL) <= DEBUG_LVL) { \
+    printf(TYPE_MPC8540_I2C " : " FMT, ## __VA_ARGS__); } } while (0)
+#else
+#define DPRINTK(LVL, FMT, ...) do {} while (0)
+#endif
+
+#define LOG(MSK, FMT, ...) qemu_log_mask(MSK, TYPE_MPC8540_I2C \
+    " : " FMT, ## __VA_ARGS__)
+
+#define TYPE_MPC8540_I2C "mpc8540-i2c"
+#define MPC8540_I2C(obj) OBJECT_CHECK(I2CState, (obj), TYPE_MPC8540_I2C)
+
+/* offsets relative to CCSR offset 0x3000 */
+#define R_I2CADR (0)
+#define R_I2CFDR (4)
+#define R_I2CCR  (8)
+#define R_I2CSR  (0xc)
+#define R_I2CDR  (0x10)
+#define R_I2CDFSRR (0x14)
+
+FIELD(I2CCR, MEN, 7, 1)
+FIELD(I2CCR, MIEN, 6, 1)
+FIELD(I2CCR, MSTA, 5, 1)
+FIELD(I2CCR, MTX, 4, 1)
+FIELD(I2CCR, TXAK, 3, 1)
+FIELD(I2CCR, RSTA, 2, 1)
+FIELD(I2CCR, BCST, 0, 1)
+
+FIELD(I2CSR, MCF, 7, 1)
+FIELD(I2CSR, MAAS, 6, 1)
+FIELD(I2CSR, MBB, 5, 1)
+FIELD(I2CSR, MAL, 4, 1)
+FIELD(I2CSR, BCSTM, 3, 1)
+FIELD(I2CSR, SRW, 2, 1)
+FIELD(I2CSR, MIF, 1, 1)
+FIELD(I2CSR, RXAK, 0, 1)
+
+typedef struct I2CState {
+    SysBusDevice parent_obj;
+
+    I2CBus *bus;
+
+    uint8_t ctrl, sts;
+    uint8_t freq, filt;
+    /* Reads are pipelined, this is the next data value */
+    uint8_t dbuf;
+
+    qemu_irq irq;
+
+    MemoryRegion mmio;
+} I2CState;
+
+#define I2CCR(BIT) FIELD_EX32(i2c->ctrl, I2CCR, BIT)
+#define I2CSR(BIT) FIELD_EX32(i2c->sts, I2CSR, BIT)
+
+#define I2CSR_SET(BIT, VAL) do {\
+        i2c->sts = FIELD_DP32(i2c->sts, I2CSR, BIT, VAL);\
+    } while (0)
+
+static
+void mpc8540_update_irq(I2CState *i2c)
+{
+    int ena = i2c->ctrl & 0x40,
+        sts = i2c->sts & 0x02,
+        act = !!(ena && sts);
+
+    DPRINTK(1, "IRQ %c ena %c sts %c\n",
+            act ? 'X' : '_',
+            ena ? 'X' : '_',
+            sts ? 'X' : '_');
+
+    qemu_set_irq(i2c->irq, act);
+}
+
+static
+uint64_t mpc8540_i2c_read(void *opaque, hwaddr addr, unsigned size)
+{
+    I2CState *i2c = opaque;
+    uint32_t val, offset = addr;
+
+    switch (offset) {
+    case R_I2CADR: /* ADDR */
+        val = 0;
+        break;
+    case R_I2CFDR: /* Freq Div. */
+        val = i2c->freq;
+        break;
+    case R_I2CCR: /* CONTROL */
+        val = i2c->ctrl & ~0x06;
+        break;
+    case R_I2CSR: /* STATUS */
+        val = i2c->sts;
+        break;
+    case R_I2CDR: /* DATA */
+        /* Reads are "pipelined" and so return the previous value of the
+         * register
+         */
+        val = i2c->dbuf;
+        if (I2CCR(MEN) && I2CSR(MBB)) { /* enabled and busy */
+            if (!i2c_bus_busy(i2c->bus) || I2CCR(MTX)) {
+                LOG(LOG_GUEST_ERROR, "Read during addr or tx\n");
+                i2c->dbuf = 0xff;
+            } else {
+                int ret = i2c_recv(i2c->bus);
+                i2c->dbuf = (uint8_t)ret;
+                DPRINTK(0, "READ %02x ('%c')\n", i2c->dbuf, (char)i2c->dbuf);
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+                mpc8540_update_irq(i2c);
+            }
+        } else {
+            i2c->dbuf = 0xff;
+            LOG(LOG_GUEST_ERROR, "Read when not enabled or busy\n");
+        }
+        break;
+    case R_I2CDFSRR: /* FILTER */
+        val = i2c->filt;
+        break;
+    default:
+        val = 0xff;
+    }
+
+    DPRINTK(offset == 0xc ? 2 : 1, " read %08x -> %08x\n",
+            (unsigned)offset, (unsigned)val);
+    return val;
+}
+
+static
+void mpc8540_i2c_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    I2CState *i2c = opaque;
+    uint32_t offset = addr;
+
+    DPRINTK(1, " write %08x <- %08x\n", (unsigned)offset, (unsigned)val);
+
+    switch (offset) {
+    case R_I2CADR: /* ADDR */
+        break;
+    case R_I2CFDR: /* Freq Div. */
+        i2c->freq = val & 0x3f;
+        break;
+    case R_I2CCR: /* CONTROL CCR */
+        if (!FIELD_EX32(val, I2CCR, MEN)) {
+            DPRINTK(0, "Not Enabled\n");
+
+        } else if (!I2CCR(MSTA) && FIELD_EX32(val, I2CCR, MSTA)) {
+            /* MSTA 0 -> 1 is START */
+
+            I2CSR_SET(MBB, 1);
+            DPRINTK(0, "START\n");
+            i2c_end_transfer(i2c->bus); /* paranoia */
+
+        } else if (I2CCR(MSTA) && !FIELD_EX32(val, I2CCR, MSTA)) {
+            /* MSTA 1 -> 0 is STOP */
+
+            I2CSR_SET(MBB, 0);
+            DPRINTK(0, "STOP\n");
+            i2c_end_transfer(i2c->bus);
+
+        } else if (I2CCR(MSTA) && FIELD_EX32(val, I2CCR, RSTA)) {
+            i2c_end_transfer(i2c->bus);
+            I2CSR_SET(MBB, 1);
+            DPRINTK(0, "REP START\n");
+
+        }
+        /* RSTA always reads zero, bit 1 unusd */
+        val &= 0xf9;
+        i2c->ctrl = val;
+        mpc8540_update_irq(i2c);
+        break;
+    case R_I2CSR: /* STATUS CSR */
+        /* only MAL and MIF are writable */
+        val &= 0x12;
+        i2c->sts &= ~0x12;
+        i2c->sts |= val;
+        mpc8540_update_irq(i2c);
+        break;
+    case R_I2CDR: /* DATA CDR */
+        if (I2CCR(MEN) && I2CSR(MBB)) { /* enabled and busy */
+            if (!i2c_bus_busy(i2c->bus)) {
+                if (i2c_start_transfer(i2c->bus, val >> 1, val & 1)) {
+                    LOG(LOG_GUEST_ERROR, "I2C no device %02x\n",
+                        (unsigned)(val & 0xfe));
+                } else {
+                    DPRINTK(0, "ADDR %02x\n", (unsigned)(val & 0xfe));
+                }
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+
+            } else if (I2CCR(MTX)) {
+                DPRINTK(0, "WRITE %02x\n", (unsigned)val);
+                i2c_send(i2c->bus, val);
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+            } else {
+                LOG(LOG_GUEST_ERROR, "I2CDR Write during read\n");
+            }
+            mpc8540_update_irq(i2c);
+        } else {
+            LOG(LOG_GUEST_ERROR, "I2CDR Write when not enabled or busy\n");
+        }
+        break;
+    case R_I2CDFSRR: /* FILTER */
+        val &= 0x3f;
+        i2c->filt = val;
+        break;
+    }
+
+    DPRINTK(1, "I2CCR = %02x I2SCR = %02x\n", i2c->ctrl, i2c->sts);
+}
+
+static const MemoryRegionOps i2c_ops = {
+    .read = mpc8540_i2c_read,
+    .write = mpc8540_i2c_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static
+void mpc8540_i2c_reset(DeviceState *dev)
+{
+    I2CState *i2c = MPC8540_I2C(dev);
+
+    i2c->sts = 0x81; /* transfer complete and ack received */
+}
+
+static int mpc8540_i2c_inst_init(SysBusDevice *dev)
+{
+    I2CState *i2c = MPC8540_I2C(dev);
+
+    i2c->bus = i2c_init_bus(&dev->parent_obj, "bus");
+
+    memory_region_init_io(&i2c->mmio, &dev->parent_obj.parent_obj,
+                          &i2c_ops, i2c, TYPE_MPC8540_I2C, 0x18);
+
+    sysbus_init_mmio(dev, &i2c->mmio);
+    sysbus_init_irq(dev, &i2c->irq);
+    return 0;
+}
+
+static void mpc8540_i2c_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = &mpc8540_i2c_inst_init;
+    dc->reset = &mpc8540_i2c_reset;
+}
+
+static const TypeInfo mpc8540_i2c_type = {
+    .name = TYPE_MPC8540_I2C,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(I2CState),
+    .class_size = sizeof(SysBusDeviceClass),
+    .class_init = mpc8540_i2c_class_init,
+};
+
+static void mpc8540_i2c_register(void)
+{
+    type_register_static(&mpc8540_i2c_type);
+}
+
+type_init(mpc8540_i2c_register)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#15 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 06/12] i2c: add mpc8540 i2c controller

In reply to Michael Davidsaver

Hello,

The model should be QOM'ified.

Quoted 35 lines · Michael Davidsaver
---
 hw/i2c/Makefile.objs |   1 +
 hw/i2c/mpc8540_i2c.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 288 insertions(+)
 create mode 100644 hw/i2c/mpc8540_i2c.c
hw/i2c/Makefile.objs +1 -0
index 0594dea3ae..79af1dd901 100644
@@ -9,3 +9,4 @@ common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
 obj-$(CONFIG_OMAP) += omap_i2c.o
 obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o
+obj-$(CONFIG_E500) += mpc8540_i2c.o
hw/i2c/mpc8540_i2c.c new file +13 -0
new file mode 100644
index 0000000000..884052cc9b
@@ -0,0 +1,287 @@
+/*
+ * MPC8540 I2C bus interface
+ * As described in
+ * MPC8540 PowerQUICC III Integrated Host Processor Reference Manual, Rev. 1
+ * Part 2 chapter 11
+ *
+ * Copyright (c) 2015 Michael Davidsaver
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the LICENSE file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"

hw/i2c/mpc8540_i2c.c line 23

Quoted 10 lines
hw/i2c/mpc8540_i2c.c new file +10 -0
+#include "hw/hw.h"
+#include "hw/registerfields.h"
+#include "hw/i2c/i2c.h"
+#include "hw/sysbus.h"
+
+/* #define DEBUG_LVL 0 */
+
+#ifdef DEBUG_LVL
+#define DPRINTK(LVL, FMT, ...) do { if ((LVL) <= DEBUG_LVL) { \
+    printf(TYPE_MPC8540_I2C " : " FMT, ## __VA_ARGS__); } } while (0)

hmm, I am not sure 'printfs' are welcomed in QEMU.

Quoted 39 lines
hw/i2c/mpc8540_i2c.c new file +38 -0
+#else
+#define DPRINTK(LVL, FMT, ...) do {} while (0)
+#endif
+
+#define LOG(MSK, FMT, ...) qemu_log_mask(MSK, TYPE_MPC8540_I2C \
+    " : " FMT, ## __VA_ARGS__)
 
+#define TYPE_MPC8540_I2C "mpc8540-i2c"
+#define MPC8540_I2C(obj) OBJECT_CHECK(I2CState, (obj), TYPE_MPC8540_I2C)
+
+/* offsets relative to CCSR offset 0x3000 */
+#define R_I2CADR (0)
+#define R_I2CFDR (4)
+#define R_I2CCR  (8)
+#define R_I2CSR  (0xc)
+#define R_I2CDR  (0x10)
+#define R_I2CDFSRR (0x14)
+
+FIELD(I2CCR, MEN, 7, 1)
+FIELD(I2CCR, MIEN, 6, 1)
+FIELD(I2CCR, MSTA, 5, 1)
+FIELD(I2CCR, MTX, 4, 1)
+FIELD(I2CCR, TXAK, 3, 1)
+FIELD(I2CCR, RSTA, 2, 1)
+FIELD(I2CCR, BCST, 0, 1)
+
+FIELD(I2CSR, MCF, 7, 1)
+FIELD(I2CSR, MAAS, 6, 1)
+FIELD(I2CSR, MBB, 5, 1)
+FIELD(I2CSR, MAL, 4, 1)
+FIELD(I2CSR, BCSTM, 3, 1)
+FIELD(I2CSR, SRW, 2, 1)
+FIELD(I2CSR, MIF, 1, 1)
+FIELD(I2CSR, RXAK, 0, 1)
+
+typedef struct I2CState {
+    SysBusDevice parent_obj;
+
+    I2CBus *bus;

hw/i2c/mpc8540_i2c.c line 72

Quoted 10 lines
hw/i2c/mpc8540_i2c.c new file +10 -0
+
+    uint8_t ctrl, sts;
+    uint8_t freq, filt;
+    /* Reads are pipelined, this is the next data value */
+    uint8_t dbuf;
+
+    qemu_irq irq;
+
+    MemoryRegion mmio;
+} I2CState;

May be use MPC8540I2CState ? I would put the definition in a header file

hw/i2c/mpc8540_i2c.c line 86

Quoted 13 lines
hw/i2c/mpc8540_i2c.c new file +13 -0
+#define I2CCR(BIT) FIELD_EX32(i2c->ctrl, I2CCR, BIT)
+#define I2CSR(BIT) FIELD_EX32(i2c->sts, I2CSR, BIT)
+
+#define I2CSR_SET(BIT, VAL) do {\
+        i2c->sts = FIELD_DP32(i2c->sts, I2CSR, BIT, VAL);\
+    } while (0)
+
+static
+void mpc8540_update_irq(I2CState *i2c)
+{
+    int ena = i2c->ctrl & 0x40,
+        sts = i2c->sts & 0x02,
+        act = !!(ena && sts);

OK. why not :) Three distinct lines would be better. This is minor.

hw/i2c/mpc8540_i2c.c line 100

Quoted 13 lines
hw/i2c/mpc8540_i2c.c new file +13 -0
+    DPRINTK(1, "IRQ %c ena %c sts %c\n",
+            act ? 'X' : '_',
+            ena ? 'X' : '_',
+            sts ? 'X' : '_');
+
+    qemu_set_irq(i2c->irq, act);
+}
+
+static
+uint64_t mpc8540_i2c_read(void *opaque, hwaddr addr, unsigned size)
+{
+    I2CState *i2c = opaque;
+    uint32_t val, offset = addr;

This 'offset = addr' looks useless. do you really need it ?

Quoted 153 lines
hw/i2c/mpc8540_i2c.c new file +153 -0
+
+    switch (offset) {
+    case R_I2CADR: /* ADDR */
+        val = 0;
+        break;
+    case R_I2CFDR: /* Freq Div. */
+        val = i2c->freq;
+        break;
+    case R_I2CCR: /* CONTROL */
+        val = i2c->ctrl & ~0x06;
+        break;
+    case R_I2CSR: /* STATUS */
+        val = i2c->sts;
+        break;
+    case R_I2CDR: /* DATA */
+        /* Reads are "pipelined" and so return the previous value of the
+         * register
+         */
+        val = i2c->dbuf;
+        if (I2CCR(MEN) && I2CSR(MBB)) { /* enabled and busy */
+            if (!i2c_bus_busy(i2c->bus) || I2CCR(MTX)) {
+                LOG(LOG_GUEST_ERROR, "Read during addr or tx\n");
+                i2c->dbuf = 0xff;
+            } else {
+                int ret = i2c_recv(i2c->bus);
+                i2c->dbuf = (uint8_t)ret;
+                DPRINTK(0, "READ %02x ('%c')\n", i2c->dbuf, (char)i2c->dbuf);
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+                mpc8540_update_irq(i2c);
+            }
+        } else {
+            i2c->dbuf = 0xff;
+            LOG(LOG_GUEST_ERROR, "Read when not enabled or busy\n");
+        }
+        break;
+    case R_I2CDFSRR: /* FILTER */
+        val = i2c->filt;
+        break;
+    default:
+        val = 0xff;
+    }
+
+    DPRINTK(offset == 0xc ? 2 : 1, " read %08x -> %08x\n",
+            (unsigned)offset, (unsigned)val);
+    return val;
+}
+
+static
+void mpc8540_i2c_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    I2CState *i2c = opaque;
+    uint32_t offset = addr;
+
+    DPRINTK(1, " write %08x <- %08x\n", (unsigned)offset, (unsigned)val);
+
+    switch (offset) {
+    case R_I2CADR: /* ADDR */
+        break;
+    case R_I2CFDR: /* Freq Div. */
+        i2c->freq = val & 0x3f;
+        break;
+    case R_I2CCR: /* CONTROL CCR */
+        if (!FIELD_EX32(val, I2CCR, MEN)) {
+            DPRINTK(0, "Not Enabled\n");
+
+        } else if (!I2CCR(MSTA) && FIELD_EX32(val, I2CCR, MSTA)) {
+            /* MSTA 0 -> 1 is START */
+
+            I2CSR_SET(MBB, 1);
+            DPRINTK(0, "START\n");
+            i2c_end_transfer(i2c->bus); /* paranoia */
+
+        } else if (I2CCR(MSTA) && !FIELD_EX32(val, I2CCR, MSTA)) {
+            /* MSTA 1 -> 0 is STOP */
+
+            I2CSR_SET(MBB, 0);
+            DPRINTK(0, "STOP\n");
+            i2c_end_transfer(i2c->bus);
+
+        } else if (I2CCR(MSTA) && FIELD_EX32(val, I2CCR, RSTA)) {
+            i2c_end_transfer(i2c->bus);
+            I2CSR_SET(MBB, 1);
+            DPRINTK(0, "REP START\n");
+
+        }
+        /* RSTA always reads zero, bit 1 unusd */
+        val &= 0xf9;
+        i2c->ctrl = val;
+        mpc8540_update_irq(i2c);
+        break;
+    case R_I2CSR: /* STATUS CSR */
+        /* only MAL and MIF are writable */
+        val &= 0x12;
+        i2c->sts &= ~0x12;
+        i2c->sts |= val;
+        mpc8540_update_irq(i2c);
+        break;
+    case R_I2CDR: /* DATA CDR */
+        if (I2CCR(MEN) && I2CSR(MBB)) { /* enabled and busy */
+            if (!i2c_bus_busy(i2c->bus)) {
+                if (i2c_start_transfer(i2c->bus, val >> 1, val & 1)) {
+                    LOG(LOG_GUEST_ERROR, "I2C no device %02x\n",
+                        (unsigned)(val & 0xfe));
+                } else {
+                    DPRINTK(0, "ADDR %02x\n", (unsigned)(val & 0xfe));
+                }
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+
+            } else if (I2CCR(MTX)) {
+                DPRINTK(0, "WRITE %02x\n", (unsigned)val);
+                i2c_send(i2c->bus, val);
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+            } else {
+                LOG(LOG_GUEST_ERROR, "I2CDR Write during read\n");
+            }
+            mpc8540_update_irq(i2c);
+        } else {
+            LOG(LOG_GUEST_ERROR, "I2CDR Write when not enabled or busy\n");
+        }
+        break;
+    case R_I2CDFSRR: /* FILTER */
+        val &= 0x3f;
+        i2c->filt = val;
+        break;
+    }
+
+    DPRINTK(1, "I2CCR = %02x I2SCR = %02x\n", i2c->ctrl, i2c->sts);
+}
+
+static const MemoryRegionOps i2c_ops = {
+    .read = mpc8540_i2c_read,
+    .write = mpc8540_i2c_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static
+void mpc8540_i2c_reset(DeviceState *dev)
+{
+    I2CState *i2c = MPC8540_I2C(dev);
+
+    i2c->sts = 0x81; /* transfer complete and ack received */
+}
+
+static int mpc8540_i2c_inst_init(SysBusDevice *dev)
+{
+    I2CState *i2c = MPC8540_I2C(dev);

hw/i2c/mpc8540_i2c.c line 263

Quoted 10 lines
hw/i2c/mpc8540_i2c.c new file +10 -0
+
+    i2c->bus = i2c_init_bus(&dev->parent_obj, "bus");
+
+    memory_region_init_io(&i2c->mmio, &dev->parent_obj.parent_obj,
+                          &i2c_ops, i2c, TYPE_MPC8540_I2C, 0x18);
+
+    sysbus_init_mmio(dev, &i2c->mmio);
+    sysbus_init_irq(dev, &i2c->irq);
+    return 0;
+}

This should be a realize routine.

C.

Quoted 24 lines
hw/i2c/mpc8540_i2c.c new file +23 -0
+static void mpc8540_i2c_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = &mpc8540_i2c_inst_init;
+    dc->reset = &mpc8540_i2c_reset;
+}
+
+static const TypeInfo mpc8540_i2c_type = {
+    .name = TYPE_MPC8540_I2C,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(I2CState),
+    .class_size = sizeof(SysBusDeviceClass),
+    .class_init = mpc8540_i2c_class_init,
+};
+
+static void mpc8540_i2c_register(void)
+{
+    type_register_static(&mpc8540_i2c_type);
+}
+
+type_init(mpc8540_i2c_register)
#16 lore raw

Re: [Qemu-devel] [Qemu-ppc] [PATCH 06/12] i2c: add mpc8540 i2c controller

Quoted 44 lines · Cédric Le Goater

Hello,

The model should be QOM'ified.

Quoted 39 lines · Michael Davidsaver
---
 hw/i2c/Makefile.objs |   1 +
 hw/i2c/mpc8540_i2c.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 288 insertions(+)
 create mode 100644 hw/i2c/mpc8540_i2c.c
hw/i2c/Makefile.objs +1 -0
index 0594dea3ae..79af1dd901 100644
@@ -9,3 +9,4 @@ common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
 obj-$(CONFIG_OMAP) += omap_i2c.o
 obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o
+obj-$(CONFIG_E500) += mpc8540_i2c.o
hw/i2c/mpc8540_i2c.c new file +17 -0
new file mode 100644
index 0000000000..884052cc9b
@@ -0,0 +1,287 @@
+/*
+ * MPC8540 I2C bus interface
+ * As described in
+ * MPC8540 PowerQUICC III Integrated Host Processor Reference Manual, Rev. 1
+ * Part 2 chapter 11
+ *
+ * Copyright (c) 2015 Michael Davidsaver
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the LICENSE file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/hw.h"
+#include "hw/registerfields.h"
+#include "hw/i2c/i2c.h"
+#include "hw/sysbus.h"
Quoted 8 lines

hw/i2c/mpc8540_i2c.c line 23

Quoted 6 lines
hw/i2c/mpc8540_i2c.c new file +6 -0
+
+/* #define DEBUG_LVL 0 */
+
+#ifdef DEBUG_LVL
+#define DPRINTK(LVL, FMT, ...) do { if ((LVL) <= DEBUG_LVL) { \
+    printf(TYPE_MPC8540_I2C " : " FMT, ## __VA_ARGS__); } } while (0)

hmm, I am not sure 'printfs' are welcomed in QEMU.

They're not, tracepoints are preferred. I tend to be ok with debug macros like this, though, since they can be convenient. It should at least be fprintf(stderr, ...) though.

Quoted 243 lines
Quoted 40 lines
hw/i2c/mpc8540_i2c.c new file +38 -0
+#else
+#define DPRINTK(LVL, FMT, ...) do {} while (0)
+#endif
+
+#define LOG(MSK, FMT, ...) qemu_log_mask(MSK, TYPE_MPC8540_I2C \
+    " : " FMT, ## __VA_ARGS__)
 
+#define TYPE_MPC8540_I2C "mpc8540-i2c"
+#define MPC8540_I2C(obj) OBJECT_CHECK(I2CState, (obj), TYPE_MPC8540_I2C)
+
+/* offsets relative to CCSR offset 0x3000 */
+#define R_I2CADR (0)
+#define R_I2CFDR (4)
+#define R_I2CCR  (8)
+#define R_I2CSR  (0xc)
+#define R_I2CDR  (0x10)
+#define R_I2CDFSRR (0x14)
+
+FIELD(I2CCR, MEN, 7, 1)
+FIELD(I2CCR, MIEN, 6, 1)
+FIELD(I2CCR, MSTA, 5, 1)
+FIELD(I2CCR, MTX, 4, 1)
+FIELD(I2CCR, TXAK, 3, 1)
+FIELD(I2CCR, RSTA, 2, 1)
+FIELD(I2CCR, BCST, 0, 1)
+
+FIELD(I2CSR, MCF, 7, 1)
+FIELD(I2CSR, MAAS, 6, 1)
+FIELD(I2CSR, MBB, 5, 1)
+FIELD(I2CSR, MAL, 4, 1)
+FIELD(I2CSR, BCSTM, 3, 1)
+FIELD(I2CSR, SRW, 2, 1)
+FIELD(I2CSR, MIF, 1, 1)
+FIELD(I2CSR, RXAK, 0, 1)
+
+typedef struct I2CState {
+    SysBusDevice parent_obj;
+
+    I2CBus *bus;

hw/i2c/mpc8540_i2c.c line 72

Quoted 10 lines
hw/i2c/mpc8540_i2c.c new file +10 -0
+
+    uint8_t ctrl, sts;
+    uint8_t freq, filt;
+    /* Reads are pipelined, this is the next data value */
+    uint8_t dbuf;
+
+    qemu_irq irq;
+
+    MemoryRegion mmio;
+} I2CState;

May be use MPC8540I2CState ? I would put the definition in a header file

hw/i2c/mpc8540_i2c.c line 86

Quoted 13 lines
hw/i2c/mpc8540_i2c.c new file +13 -0
+#define I2CCR(BIT) FIELD_EX32(i2c->ctrl, I2CCR, BIT)
+#define I2CSR(BIT) FIELD_EX32(i2c->sts, I2CSR, BIT)
+
+#define I2CSR_SET(BIT, VAL) do {\
+        i2c->sts = FIELD_DP32(i2c->sts, I2CSR, BIT, VAL);\
+    } while (0)
+
+static
+void mpc8540_update_irq(I2CState *i2c)
+{
+    int ena = i2c->ctrl & 0x40,
+        sts = i2c->sts & 0x02,
+        act = !!(ena && sts);

OK. why not :) Three distinct lines would be better. This is minor.

hw/i2c/mpc8540_i2c.c line 100

Quoted 13 lines
hw/i2c/mpc8540_i2c.c new file +13 -0
+    DPRINTK(1, "IRQ %c ena %c sts %c\n",
+            act ? 'X' : '_',
+            ena ? 'X' : '_',
+            sts ? 'X' : '_');
+
+    qemu_set_irq(i2c->irq, act);
+}
+
+static
+uint64_t mpc8540_i2c_read(void *opaque, hwaddr addr, unsigned size)
+{
+    I2CState *i2c = opaque;
+    uint32_t val, offset = addr;

This 'offset = addr' looks useless. do you really need it ?

Quoted 157 lines
hw/i2c/mpc8540_i2c.c new file +157 -0
+
+    switch (offset) {
+    case R_I2CADR: /* ADDR */
+        val = 0;
+        break;
+    case R_I2CFDR: /* Freq Div. */
+        val = i2c->freq;
+        break;
+    case R_I2CCR: /* CONTROL */
+        val = i2c->ctrl & ~0x06;
+        break;
+    case R_I2CSR: /* STATUS */
+        val = i2c->sts;
+        break;
+    case R_I2CDR: /* DATA */
+        /* Reads are "pipelined" and so return the previous value of the
+         * register
+         */
+        val = i2c->dbuf;
+        if (I2CCR(MEN) && I2CSR(MBB)) { /* enabled and busy */
+            if (!i2c_bus_busy(i2c->bus) || I2CCR(MTX)) {
+                LOG(LOG_GUEST_ERROR, "Read during addr or tx\n");
+                i2c->dbuf = 0xff;
+            } else {
+                int ret = i2c_recv(i2c->bus);
+                i2c->dbuf = (uint8_t)ret;
+                DPRINTK(0, "READ %02x ('%c')\n", i2c->dbuf, (char)i2c->dbuf);
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+                mpc8540_update_irq(i2c);
+            }
+        } else {
+            i2c->dbuf = 0xff;
+            LOG(LOG_GUEST_ERROR, "Read when not enabled or busy\n");
+        }
+        break;
+    case R_I2CDFSRR: /* FILTER */
+        val = i2c->filt;
+        break;
+    default:
+        val = 0xff;
+    }
+
+    DPRINTK(offset == 0xc ? 2 : 1, " read %08x -> %08x\n",
+            (unsigned)offset, (unsigned)val);
+    return val;
+}
+
+static
+void mpc8540_i2c_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    I2CState *i2c = opaque;
+    uint32_t offset = addr;
+
+    DPRINTK(1, " write %08x <- %08x\n", (unsigned)offset, (unsigned)val);
+
+    switch (offset) {
+    case R_I2CADR: /* ADDR */
+        break;
+    case R_I2CFDR: /* Freq Div. */
+        i2c->freq = val & 0x3f;
+        break;
+    case R_I2CCR: /* CONTROL CCR */
+        if (!FIELD_EX32(val, I2CCR, MEN)) {
+            DPRINTK(0, "Not Enabled\n");
+
+        } else if (!I2CCR(MSTA) && FIELD_EX32(val, I2CCR, MSTA)) {
+            /* MSTA 0 -> 1 is START */
+
+            I2CSR_SET(MBB, 1);
+            DPRINTK(0, "START\n");
+            i2c_end_transfer(i2c->bus); /* paranoia */
+
+        } else if (I2CCR(MSTA) && !FIELD_EX32(val, I2CCR, MSTA)) {
+            /* MSTA 1 -> 0 is STOP */
+
+            I2CSR_SET(MBB, 0);
+            DPRINTK(0, "STOP\n");
+            i2c_end_transfer(i2c->bus);
+
+        } else if (I2CCR(MSTA) && FIELD_EX32(val, I2CCR, RSTA)) {
+            i2c_end_transfer(i2c->bus);
+            I2CSR_SET(MBB, 1);
+            DPRINTK(0, "REP START\n");
+
+        }
+        /* RSTA always reads zero, bit 1 unusd */
+        val &= 0xf9;
+        i2c->ctrl = val;
+        mpc8540_update_irq(i2c);
+        break;
+    case R_I2CSR: /* STATUS CSR */
+        /* only MAL and MIF are writable */
+        val &= 0x12;
+        i2c->sts &= ~0x12;
+        i2c->sts |= val;
+        mpc8540_update_irq(i2c);
+        break;
+    case R_I2CDR: /* DATA CDR */
+        if (I2CCR(MEN) && I2CSR(MBB)) { /* enabled and busy */
+            if (!i2c_bus_busy(i2c->bus)) {
+                if (i2c_start_transfer(i2c->bus, val >> 1, val & 1)) {
+                    LOG(LOG_GUEST_ERROR, "I2C no device %02x\n",
+                        (unsigned)(val & 0xfe));
+                } else {
+                    DPRINTK(0, "ADDR %02x\n", (unsigned)(val & 0xfe));
+                }
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+
+            } else if (I2CCR(MTX)) {
+                DPRINTK(0, "WRITE %02x\n", (unsigned)val);
+                i2c_send(i2c->bus, val);
+                I2CSR_SET(MIF, 1);
+                I2CSR_SET(RXAK, 0);
+            } else {
+                LOG(LOG_GUEST_ERROR, "I2CDR Write during read\n");
+            }
+            mpc8540_update_irq(i2c);
+        } else {
+            LOG(LOG_GUEST_ERROR, "I2CDR Write when not enabled or busy\n");
+        }
+        break;
+    case R_I2CDFSRR: /* FILTER */
+        val &= 0x3f;
+        i2c->filt = val;
+        break;
+    }
+
+    DPRINTK(1, "I2CCR = %02x I2SCR = %02x\n", i2c->ctrl, i2c->sts);
+}
+
+static const MemoryRegionOps i2c_ops = {
+    .read = mpc8540_i2c_read,
+    .write = mpc8540_i2c_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static
+void mpc8540_i2c_reset(DeviceState *dev)
+{
+    I2CState *i2c = MPC8540_I2C(dev);
+
+    i2c->sts = 0x81; /* transfer complete and ack received */
+}
+
+static int mpc8540_i2c_inst_init(SysBusDevice *dev)
+{
+    I2CState *i2c = MPC8540_I2C(dev);
+
+    i2c->bus = i2c_init_bus(&dev->parent_obj, "bus");
+
+    memory_region_init_io(&i2c->mmio, &dev->parent_obj.parent_obj,
Quoted 8 lines

hw/i2c/mpc8540_i2c.c line 263

Quoted 6 lines
hw/i2c/mpc8540_i2c.c new file +6 -0
+                          &i2c_ops, i2c, TYPE_MPC8540_I2C, 0x18);
+
+    sysbus_init_mmio(dev, &i2c->mmio);
+    sysbus_init_irq(dev, &i2c->irq);
+    return 0;
+}

This should be a realize routine.

Ah.. that's a good point, actually. On that basis, I'm pulling the draft patch from ppc-for-2.12

Quoted 25 lines
Quoted 24 lines
hw/i2c/mpc8540_i2c.c new file +23 -0
+static void mpc8540_i2c_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = &mpc8540_i2c_inst_init;
+    dc->reset = &mpc8540_i2c_reset;
+}
+
+static const TypeInfo mpc8540_i2c_type = {
+    .name = TYPE_MPC8540_I2C,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(I2CState),
+    .class_size = sizeof(SysBusDeviceClass),
+    .class_init = mpc8540_i2c_class_init,
+};
+
+static void mpc8540_i2c_register(void)
+{
+    type_register_static(&mpc8540_i2c_type);
+}
+
+type_init(mpc8540_i2c_register)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#17 lore raw

PATCH 7/12 qtest: add e500_i2c_create()

In reply to Michael Davidsaver

Add interface for testing i2c devices
with PPC e500.

---
 tests/Makefile.include  |  1 +
 tests/libqos/i2c-e500.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/libqos/i2c.h      |  3 +++
 3 files changed, 70 insertions(+)
 create mode 100644 tests/libqos/i2c-e500.c
tests/Makefile.include +1 -0
index c002352134..ad1c219423 100644
@@ -721,6 +721,7 @@ libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o
 libqos-pc-obj-y += tests/libqos/ahci.o
 libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
 libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
+libqos-e500-obj-y = $(libqos-obj-y) tests/libqos/i2c-e500.o
 libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
 libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
 
tests/libqos/i2c-e500.c new file +66 -0

66 lines not on this page · raw message

tests/libqos/i2c.h +3 -0
index 6e648f922a..40c59a7997 100644
@@ -29,4 +29,7 @@ I2CAdapter *omap_i2c_create(uint64_t addr);
 /* libi2c-imx.c */
 I2CAdapter *imx_i2c_create(uint64_t addr);
 
+/* i2c-e500.c */
+I2CAdapter *e500_i2c_create(uint64_t ccsr_base);
+
 #endif
Signature · 1 line
2.11.0
#18 lore raw

PATCH 8/12 e500: add mpc8540 i2c controller to ccsr

In reply to Michael Davidsaver
---
 hw/ppc/e500.c | 8 ++++++++
 1 file changed, 8 insertions(+)
hw/ppc/e500.c +8 -0
index 6f77844303..bef7d313d4 100644
@@ -861,6 +861,14 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     qdev_init_nofail(dev);
     ccsr_addr_space = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 
+    dev = qdev_create(NULL, "mpc8540-i2c");
+    object_property_add_child(qdev_get_machine(), "i2c[*]",
+                              OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    memory_region_add_subregion(ccsr_addr_space, 0x3000,
+                                sysbus_mmio_get_region(s, 0));
+
     mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs);
 
     /* Serial */
Signature · 1 line
2.11.0
#19 lore raw

Re: [Qemu-devel] [PATCH 08/12] e500: add mpc8540 i2c controller to ccsr

Quoted 1 line · Michael Davidsaver

You're adding what seems to be a fairly specific device to the general e500 init - this again suggests that it should be split, putting creation of devices under control of individual machines.

Quoted 23 lines
---
 hw/ppc/e500.c | 8 ++++++++
 1 file changed, 8 insertions(+)
hw/ppc/e500.c +8 -0
index 6f77844303..bef7d313d4 100644
@@ -861,6 +861,14 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     qdev_init_nofail(dev);
     ccsr_addr_space = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 
+    dev = qdev_create(NULL, "mpc8540-i2c");
+    object_property_add_child(qdev_get_machine(), "i2c[*]",
+                              OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    memory_region_add_subregion(ccsr_addr_space, 0x3000,
+                                sysbus_mmio_get_region(s, 0));
+
     mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs);
 
     /* Serial */
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#20 lore raw

Re: [Qemu-devel] [PATCH 08/12] e500: add mpc8540 i2c controller to ccsr

Quoted 6 lines · David Gibson
Quoted 1 line · Michael Davidsaver

You're adding what seems to be a fairly specific device to the general e500 init - this again suggests that it should be split, putting creation of devices under control of individual machines.

I'll address the ppce500_init() part of this question separately.

In addition to the MPC8540, I find that documentation for the MPC8544 (modeled) and P2020 (un-modeled) show the same i2c controller registers.
So I think it's reasonable for the generic ppce500 machine to have it
as well.

For what it's worth, the Linux driver for this unit (drivers/i2c/busses/i2c-mpc.c) lists compatibility with a number of other freescale SoCs with only some differences in clock selection (not modeled). The description for the module is:

Quoted 29 lines

MODULE_DESCRIPTION("I2C-Bus adapter for MPC107 bridge and "

                   "MPC824x/83xx/85xx/86xx/512x/52xx processors");
Quoted 23 lines
---
 hw/ppc/e500.c | 8 ++++++++
 1 file changed, 8 insertions(+)
hw/ppc/e500.c +8 -0
index 6f77844303..bef7d313d4 100644
@@ -861,6 +861,14 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     qdev_init_nofail(dev);
     ccsr_addr_space = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 
+    dev = qdev_create(NULL, "mpc8540-i2c");
+    object_property_add_child(qdev_get_machine(), "i2c[*]",
+                              OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    memory_region_add_subregion(ccsr_addr_space, 0x3000,
+                                sysbus_mmio_get_region(s, 0));
+
     mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs);
 
     /* Serial */
#21 lore raw

PATCH 9/12 nvram: add AT24Cx i2c eeprom

In reply to Michael Davidsaver
---
 hw/nvram/Makefile.objs  |   1 +
 hw/nvram/eeprom_at24c.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 206 insertions(+)
 create mode 100644 hw/nvram/eeprom_at24c.c
hw/nvram/Makefile.objs +1 -0
index c018f6b2ff..0f4ee71dcb 100644
@@ -1,5 +1,6 @@
 common-obj-$(CONFIG_DS1225Y) += ds1225y.o
 common-obj-y += eeprom93xx.o
+common-obj-y += eeprom_at24c.o
 common-obj-y += fw_cfg.o
 common-obj-y += chrp_nvram.o
 common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
hw/nvram/eeprom_at24c.c new file +205 -0

205 lines not on this page · raw message

Signature · 1 line
2.11.0
Applied
#22 lore raw

Re: [Qemu-devel] [PATCH 09/12] nvram: add AT24Cx i2c eeprom

Quoted 1 line · Michael Davidsaver

Applied to ppc-for-2.12.

Quoted 228 lines
---
 hw/nvram/Makefile.objs  |   1 +
 hw/nvram/eeprom_at24c.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 206 insertions(+)
 create mode 100644 hw/nvram/eeprom_at24c.c
hw/nvram/Makefile.objs +1 -0
index c018f6b2ff..0f4ee71dcb 100644
@@ -1,5 +1,6 @@
 common-obj-$(CONFIG_DS1225Y) += ds1225y.o
 common-obj-y += eeprom93xx.o
+common-obj-y += eeprom_at24c.o
 common-obj-y += fw_cfg.o
 common-obj-y += chrp_nvram.o
 common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
hw/nvram/eeprom_at24c.c new file +205 -0
new file mode 100644
index 0000000000..efa3621ac6
@@ -0,0 +1,205 @@
+/*
+ * *AT24C* series I2C EEPROM
+ *
+ * Copyright (c) 2015 Michael Davidsaver
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the LICENSE file in the top-level directory.
+ */
+
+#include <string.h>
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "sysemu/block-backend.h"
+
+/* #define DEBUG_AT24C */
+
+#ifdef DEBUG_AT24C
+#define DPRINTK(FMT, ...) printf(TYPE_AT24C_EE " : " FMT, ## __VA_ARGS__)
+#else
+#define DPRINTK(FMT, ...) do {} while (0)
+#endif
+
+#define ERR(FMT, ...) fprintf(stderr, TYPE_AT24C_EE " : " FMT, \
+                            ## __VA_ARGS__)
+
+#define TYPE_AT24C_EE "at24c-eeprom"
+#define AT24C_EE(obj) OBJECT_CHECK(EEPROMState, (obj), TYPE_AT24C_EE)
+
+typedef struct EEPROMState {
+    I2CSlave parent_obj;
+
+    /* address counter */
+    uint16_t cur;
+    /* total size in bytes */
+    uint32_t rsize;
+    bool writable;
+    /* cells changed since last START? */
+    bool changed;
+    /* during WRITE, # of address bytes transfered */
+    uint8_t haveaddr;
+
+    uint8_t *mem;
+
+    BlockBackend *blk;
+} EEPROMState;
+
+static
+int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
+{
+    EEPROMState *ee = container_of(s, EEPROMState, parent_obj);
+
+    switch (event) {
+    case I2C_START_SEND:
+    case I2C_START_RECV:
+    case I2C_FINISH:
+        ee->haveaddr = 0;
+        DPRINTK("clear\n");
+        if (ee->blk && ee->changed) {
+            int len = blk_pwrite(ee->blk, 0, ee->mem, ee->rsize, 0);
+            if (len != ee->rsize) {
+                ERR(TYPE_AT24C_EE
+                        " : failed to write backing file\n");
+            }
+            DPRINTK("Wrote to backing file\n");
+        }
+        ee->changed = false;
+        break;
+    case I2C_NACK:
+        break;
+    }
+    return 0;
+}
+
+static
+int at24c_eeprom_recv(I2CSlave *s)
+{
+    EEPROMState *ee = AT24C_EE(s);
+    int ret;
+
+    ret = ee->mem[ee->cur];
+
+    ee->cur = (ee->cur + 1u) % ee->rsize;
+    DPRINTK("Recv %02x %c\n", ret, ret);
+
+    return ret;
+}
+
+static
+int at24c_eeprom_send(I2CSlave *s, uint8_t data)
+{
+    EEPROMState *ee = AT24C_EE(s);
+
+    if (ee->haveaddr < 2) {
+        ee->cur <<= 8;
+        ee->cur |= data;
+        ee->haveaddr++;
+        if (ee->haveaddr == 2) {
+            ee->cur %= ee->rsize;
+            DPRINTK("Set pointer %04x\n", ee->cur);
+        }
+
+    } else {
+        if (ee->writable) {
+            DPRINTK("Send %02x\n", data);
+            ee->mem[ee->cur] = data;
+            ee->changed = true;
+        } else {
+            DPRINTK("Send error %02x read-only\n", data);
+        }
+        ee->cur = (ee->cur + 1u) % ee->rsize;
+
+    }
+
+    return 0;
+}
+
+static
+int at24c_eeprom_init(I2CSlave *i2c)
+{
+    EEPROMState *ee = AT24C_EE(i2c);
+
+    ee->mem = g_malloc0(ee->rsize);
+
+    if (ee->blk) {
+        int64_t len = blk_getlength(ee->blk);
+
+        if (len != ee->rsize) {
+            ERR(TYPE_AT24C_EE " : Backing file size %lu != %u\n",
+                    (unsigned long)len, (unsigned)ee->rsize);
+            exit(1);
+        }
+
+        if (blk_set_perm(ee->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE,
+                         BLK_PERM_ALL, &error_fatal) < 0)
+        {
+            ERR(TYPE_AT24C_EE
+                    " : Backing file incorrect permission\n");
+            exit(1);
+        }
+    }
+    return 0;
+}
+
+static
+void at24c_eeprom_reset(DeviceState *state)
+{
+    EEPROMState *ee = AT24C_EE(state);
+
+    ee->changed = false;
+    ee->cur = 0;
+    ee->haveaddr = 0;
+
+    memset(ee->mem, 0, ee->rsize);
+
+    if (ee->blk) {
+        int len = blk_pread(ee->blk, 0, ee->mem, ee->rsize);
+
+        if (len != ee->rsize) {
+            ERR(TYPE_AT24C_EE
+                    " : Failed initial sync with backing file\n");
+        }
+        DPRINTK("Reset read backing file\n");
+    }
+}
+
+static Property at24c_eeprom_props[] = {
+    DEFINE_PROP_UINT32("rom-size", EEPROMState, rsize, 0),
+    DEFINE_PROP_BOOL("writable", EEPROMState, writable, true),
+    DEFINE_PROP_DRIVE("drive", EEPROMState, blk),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static
+void at24c_eeprom_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = &at24c_eeprom_init;
+    k->event = &at24c_eeprom_event;
+    k->recv = &at24c_eeprom_recv;
+    k->send = &at24c_eeprom_send;
+
+    dc->props = at24c_eeprom_props;
+    dc->reset = at24c_eeprom_reset;
+}
+
+static
+const TypeInfo at24c_eeprom_type = {
+    .name = TYPE_AT24C_EE,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(EEPROMState),
+    .class_size = sizeof(I2CSlaveClass),
+    .class_init = at24c_eeprom_class_init,
+};
+
+static void at24c_eeprom_register(void)
+{
+    type_register_static(&at24c_eeprom_type);
+}
+
+type_init(at24c_eeprom_register)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#23 lore raw

PATCH 10/12 timer: add ds1375 RTC

In reply to Michael Davidsaver

only basic functionality implemented (read time and sram). no set time or alarms.

---
 default-configs/ppc-softmmu.mak |   1 +
 hw/timer/Makefile.objs          |   1 +
 hw/timer/ds1375-i2c.c           | 293 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 295 insertions(+)
 create mode 100644 hw/timer/ds1375-i2c.c
default-configs/ppc-softmmu.mak +1 -0
index bb225c6e46..04bfa79154 100644
@@ -52,3 +52,4 @@ CONFIG_SERIAL_ISA=y
 CONFIG_MC146818RTC=y
 CONFIG_ISA_TESTDEV=y
 CONFIG_RS6000_MC=y
+CONFIG_DS1375=y
hw/timer/Makefile.objs +1 -0
index 8c19eac3b6..6521d47367 100644
@@ -4,6 +4,7 @@ common-obj-$(CONFIG_ARM_V7M) += armv7m_systick.o
 common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o
 common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
 common-obj-$(CONFIG_DS1338) += ds1338.o
+common-obj-$(CONFIG_DS1375) += ds1375-i2c.o
 common-obj-$(CONFIG_HPET) += hpet.o
 common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
 common-obj-$(CONFIG_M48T59) += m48t59.o
hw/timer/ds1375-i2c.c new file +293 -0

293 lines not on this page · raw message

Signature · 1 line
2.11.0
#24 lore raw

Re: [Qemu-devel] [PATCH 10/12] timer: add ds1375 RTC

Quoted 4 lines · Michael Davidsaver

only basic functionality implemented (read time and sram). no set time or alarms.

I know there about a zillion different Dallas/Maxim sram/rtc chips, many of which have a lot of similarities. Is it possible to share any code with the existing hw/timer/ds1338.c?

Quoted 327 lines
---
 default-configs/ppc-softmmu.mak |   1 +
 hw/timer/Makefile.objs          |   1 +
 hw/timer/ds1375-i2c.c           | 293 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 295 insertions(+)
 create mode 100644 hw/timer/ds1375-i2c.c
default-configs/ppc-softmmu.mak +1 -0
index bb225c6e46..04bfa79154 100644
@@ -52,3 +52,4 @@ CONFIG_SERIAL_ISA=y
 CONFIG_MC146818RTC=y
 CONFIG_ISA_TESTDEV=y
 CONFIG_RS6000_MC=y
+CONFIG_DS1375=y
hw/timer/Makefile.objs +1 -0
index 8c19eac3b6..6521d47367 100644
@@ -4,6 +4,7 @@ common-obj-$(CONFIG_ARM_V7M) += armv7m_systick.o
 common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o
 common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
 common-obj-$(CONFIG_DS1338) += ds1338.o
+common-obj-$(CONFIG_DS1375) += ds1375-i2c.o
 common-obj-$(CONFIG_HPET) += hpet.o
 common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
 common-obj-$(CONFIG_M48T59) += m48t59.o
hw/timer/ds1375-i2c.c new file +293 -0
new file mode 100644
index 0000000000..dba9cc05c4
@@ -0,0 +1,293 @@
+/*
+ * Dallas/Maxim ds1375 I2C RTC w/ SRAM
+ *
+ * Copyright (c) 2017 Michael Davidsaver
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the LICENSE file in the top-level directory.
+ *
+ * Only basic functionality is modeled (time and user SRAM).
+ * Alarms not modeled.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "qemu/bcd.h"
+#include "hw/hw.h"
+#include "hw/registerfields.h"
+#include "hw/i2c/i2c.h"
+
+#define DEBUG_DS1375
+
+#ifdef DEBUG_DS1375
+#define DPRINTK(FMT, ...) printf(TYPE_DS1375 " : " FMT, ## __VA_ARGS__)
+#else
+#define DPRINTK(FMT, ...) do {} while (0)
+#endif
+
+#define LOG(MSK, FMT, ...) qemu_log_mask(MSK, TYPE_DS1375 " : " FMT, \
+                            ## __VA_ARGS__)
+
+#define TYPE_DS1375 "ds1375"
+#define DS1375(obj) OBJECT_CHECK(DS1375State, (obj), TYPE_DS1375)
+
+#define DS1375_REGSIZE 0x20
+
+#define R_SEC   (0x0)
+#define R_MIN   (0x1)
+#define R_HOUR  (0x2)
+#define R_WDAY  (0x3)
+#define R_DATE  (0x4)
+#define R_MONTH (0x5)
+#define R_YEAR  (0x6)
+#define R_A1SEC   (0x7)
+#define R_A1MIN   (0x8)
+#define R_A1HOUR  (0x9)
+#define R_A1DAY   (0xa)
+#define R_A2SEC   (0xb)
+#define R_A2MIN   (0xc)
+#define R_A2HOUR  (0xd)
+#define R_CTRL  (0xe)
+#define R_STS   (0xf)
+
+FIELD(HOUR, SET12, 6, 1)
+FIELD(HOUR, HOUR24, 0, 6)
+FIELD(HOUR, AMPM, 5, 1)
+FIELD(HOUR, HOUR12, 0, 5)
+
+FIELD(MONTH, MONTH, 0, 5)
+FIELD(MONTH, CENTURY, 7, 1)
+
+FIELD(CTRL, ECLK, 7, 1)
+FIELD(CTRL, CLKSEL, 5, 2)
+FIELD(CTRL, RS, 3, 2)
+FIELD(CTRL, INTCN, 2, 1)
+FIELD(CTRL, A2IE, 1, 1)
+FIELD(CTRL, A1IE, 0, 1)
+
+typedef struct DS1375State {
+    I2CSlave parent_obj;
+
+    /* register address counter */
+    uint8_t addr;
+    /* when writing, whether the address has been sent */
+    bool addrd;
+
+    int time_offset;
+
+    uint8_t regs[DS1375_REGSIZE];
+} DS1375State;
+
+/* update current time register if clock enabled */
+static
+void ds1375_latch(DS1375State *ds)
+{
+    struct tm now;
+
+    if (!ARRAY_FIELD_EX32(ds->regs, CTRL, ECLK)) {
+        return;
+    }
+
+    qemu_get_timedate(&now, ds->time_offset);
+
+    DPRINTK("Current Time %3u/%2u/%u %2u:%2u:%2u (wday %u)\n",
+            now.tm_year, now.tm_mon, now.tm_mday,
+            now.tm_hour, now.tm_min, now.tm_sec,
+            now.tm_wday);
+
+    /* ensure unused bits are zero */
+    memset(ds->regs, 0, R_YEAR + 1);
+
+    ds->regs[R_SEC] = to_bcd(now.tm_sec);
+    ds->regs[R_MIN] = to_bcd(now.tm_min);
+
+    if (ARRAY_FIELD_EX32(ds->regs, HOUR, SET12) == 0) {
+        /* 24 hour */
+        ARRAY_FIELD_DP32(ds->regs, HOUR, HOUR24, to_bcd(now.tm_hour));
+    } else {
+        /* 12 hour am/pm */
+        ARRAY_FIELD_DP32(ds->regs, HOUR, AMPM, now.tm_hour >= 12);
+        ARRAY_FIELD_DP32(ds->regs, HOUR, HOUR12, to_bcd(now.tm_hour % 12u));
+    }
+
+    ds->regs[R_WDAY] = now.tm_wday; /* day of the week */
+    ds->regs[R_DATE] = to_bcd(now.tm_mday);
+
+    ARRAY_FIELD_DP32(ds->regs, MONTH, MONTH, to_bcd(now.tm_mon + 1));
+    ARRAY_FIELD_DP32(ds->regs, MONTH, CENTURY, now.tm_year > 99);
+
+    ds->regs[R_YEAR] = to_bcd(now.tm_year % 100u);
+
+    DPRINTK("Latched time\n");
+}
+
+static
+void ds1375_update(DS1375State *ds)
+{
+    struct tm now;
+
+    now.tm_sec = from_bcd(ds->regs[R_SEC]);
+    now.tm_min = from_bcd(ds->regs[R_MIN]);
+
+    if (ARRAY_FIELD_EX32(ds->regs, HOUR, SET12)) {
+        now.tm_hour = from_bcd(ARRAY_FIELD_EX32(ds->regs, HOUR, HOUR12));
+        if (ARRAY_FIELD_EX32(ds->regs, HOUR, AMPM)) {
+            now.tm_hour += 12;
+        }
+
+    } else {
+        now.tm_hour = from_bcd(ARRAY_FIELD_EX32(ds->regs, HOUR, HOUR24));
+    }
+
+    now.tm_wday = from_bcd(ds->regs[R_WDAY]);
+    now.tm_mday = from_bcd(ds->regs[R_DATE]);
+    now.tm_mon = from_bcd(ARRAY_FIELD_EX32(ds->regs, MONTH, MONTH)) - 1;
+
+    now.tm_year = from_bcd(ds->regs[R_YEAR]) % 100u;
+    if (ARRAY_FIELD_EX32(ds->regs, MONTH, CENTURY)) {
+        now.tm_year += 100;
+    }
+
+    DPRINTK("New Time %3u/%2u/%u %2u:%2u:%2u (wday %u)\n",
+            now.tm_year, now.tm_mon, now.tm_mday,
+            now.tm_hour, now.tm_min, now.tm_sec,
+            now.tm_wday);
+
+    ds->time_offset = qemu_timedate_diff(&now);
+    DPRINTK("Update offset = %d\n", ds->time_offset);
+}
+
+static
+int ds1375_event(I2CSlave *s, enum i2c_event event)
+{
+    DS1375State *ds = container_of(s, DS1375State, parent_obj);
+
+    switch (event) {
+    case I2C_START_SEND:
+        ds->addrd = false;
+    case I2C_START_RECV:
+        ds1375_latch(ds);
+    case I2C_FINISH:
+        DPRINTK("Event %d\n", (int)event);
+    case I2C_NACK:
+        break;
+    }
+    return 0;
+}
+
+static
+int ds1375_recv(I2CSlave *s)
+{
+    DS1375State *ds = container_of(s, DS1375State, parent_obj);
+    int ret = 0;
+
+    switch (ds->addr) {
+    case R_SEC ... R_YEAR:
+    case R_CTRL:
+    case R_STS:
+    case 0x10 ... 0x1f:
+        ret = ds->regs[ds->addr];
+        break;
+    default:
+        LOG(LOG_UNIMP, "Read from unimplemented (%02x) %02x\n", ds->addr, ret);
+    }
+
+    DPRINTK("Recv (%02x) %02x\n", ds->addr, ret);
+
+    ds->addr++;
+    ds->addr &= 0x1f;
+    if (ds->addr == 0) {
+        ds1375_latch(ds);
+    }
+
+    return ret;
+}
+
+static
+int ds1375_send(I2CSlave *s, uint8_t data)
+{
+    DS1375State *ds = container_of(s, DS1375State, parent_obj);
+
+    if (!ds->addrd) {
+        data &= 0x1f;
+        ds->addr = data;
+        DPRINTK("Set address pointer %02x\n", data);
+        ds->addrd = true;
+        return 0;
+
+    } else {
+        DPRINTK("Send (%02x) %02x\n", ds->addr, data);
+        switch (ds->addr) {
+        case R_SEC ... R_YEAR:
+            ds->regs[ds->addr] = data;
+            ds1375_update(ds);
+            break;
+        case R_CTRL:
+            if (data & 0x7) {
+                LOG(LOG_UNIMP, "Alarm interrupt/output not modeled\n");
+            }
+            ds->regs[ds->addr] = data;
+            break;
+        case 0x10 ... 0x1f:
+            ds->regs[ds->addr] = data;
+            break;
+        default:
+            LOG(LOG_UNIMP, "Write to unimplemented (%02x) %02x\n",
+                ds->addr, data);
+        }
+
+        ds->addr++;
+        ds->addr &= 0x1f;
+        if (ds->addr == 0) {
+            ds1375_latch(ds);
+        }
+
+        return 0;
+    }
+}
+
+static
+void ds1375_reset(DeviceState *device)
+{
+    DS1375State *ds = DS1375(device);
+
+    memset(ds->regs, 0, sizeof(ds->regs));
+    /* TODO: not clear SRAM? */
+
+    /* Default to 12-hour mode */
+    ARRAY_FIELD_DP32(ds->regs, CTRL, ECLK, 1);
+
+    ds->addr = 0;
+
+    /* do not re-zero time offset */
+}
+
+static
+void ds1375_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->event = &ds1375_event;
+    k->recv = &ds1375_recv;
+    k->send = &ds1375_send;
+
+    dc->reset = &ds1375_reset;
+}
+
+static
+const TypeInfo ds1375_type = {
+    .name = TYPE_DS1375,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(DS1375State),
+    .class_size = sizeof(I2CSlaveClass),
+    .class_init = ds1375_class_init,
+};
+
+static void ds1375_register(void)
+{
+    type_register_static(&ds1375_type);
+}
+
+type_init(ds1375_register)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#25 lore raw

PATCH 11/12 ppc: add mvme3100 machine

In reply to Michael Davidsaver
---
 hw/ppc/Makefile.objs   |   1 +
 hw/ppc/mvme3100.c      | 688 +++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/mvme3100_cpld.c | 192 ++++++++++++++
 3 files changed, 881 insertions(+)
 create mode 100644 hw/ppc/mvme3100.c
 create mode 100644 hw/ppc/mvme3100_cpld.c
hw/ppc/Makefile.objs +1 -0
index c1a63d0c39..c1118aaa42 100644
@@ -26,5 +26,6 @@ obj-$(CONFIG_MAC) += mac_newworld.o
 obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o
 obj-$(CONFIG_E500) += ppce500_spin.o
 obj-$(CONFIG_E500) += e500_ccsr.o
+obj-$(CONFIG_E500) += mvme3100.o mvme3100_cpld.o
 # PowerPC 440 Xilinx ML507 reference board.
 obj-$(CONFIG_XILINX) += virtex_ml507.o
hw/ppc/mvme3100.c new file +688 -0

688 lines not on this page · raw message

hw/ppc/mvme3100_cpld.c new file +192 -0

192 lines not on this page · raw message

Signature · 1 line
2.11.0
#26 lore raw

PATCH 12/12 tests: add mvme3100-test

In reply to Michael Davidsaver

Exercise some features of the mvme3100 CPLD logic
and read from the eeprom w/ VPD.

---
 tests/Makefile.include |  3 ++
 tests/mvme3100-test.c  | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)
 create mode 100644 tests/mvme3100-test.c
tests/Makefile.include +3 -0
index ad1c219423..7ea041a885 100644
@@ -372,6 +372,8 @@ check-qtest-s390x-y += tests/virtio-balloon-test$(EXESUF)
 check-qtest-s390x-y += tests/virtio-console-test$(EXESUF)
 check-qtest-s390x-y += tests/virtio-serial-test$(EXESUF)
 
+check-qtest-ppc-$(CONFIG_E500) += tests/mvme3100-test$(EXESUF)
+
 check-qtest-generic-y += tests/qom-test$(EXESUF)
 check-qtest-generic-y += tests/test-hmp$(EXESUF)
 
@@ -781,6 +783,7 @@ tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o
 tests/ac97-test$(EXESUF): tests/ac97-test.o
 tests/es1370-test$(EXESUF): tests/es1370-test.o
 tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
+tests/mvme3100-test$(EXESUF): tests/mvme3100-test.o $(libqos-e500-obj-y)
 tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
 tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y)
 tests/usb-hcd-uhci-test$(EXESUF): tests/usb-hcd-uhci-test.o $(libqos-usb-obj-y)
tests/mvme3100-test.c new file +79 -0

79 lines not on this page · raw message

Signature · 1 line
2.11.0

A message that is not in the archive (sent off-list or not yet received): [email protected]

Applied
#28 lore raw

Re: [Qemu-devel] [PATCH 02/12] e500: consolidate mpc8540 guts with e500-ccsr

Quoted 6 lines · Michael Davidsaver

Preparation for adding more MPCxxxx control
registers.

Use e500 SVR to enable part specific registers. Only the mpc8544 reset register at present.

Quoted 7 lines

Expose CCSR as SysBusDevice region to eliminate e500-ccsr.h.

Track CCSR base address within device, and map on reset, in preparation for CCSRBAR.

Applied to ppc-for-2.12.

Quoted 466 lines
---
 hw/pci-host/ppce500.c |  10 ++--
 hw/ppc/Makefile.objs  |   3 +-
 hw/ppc/e500-ccsr.h    |  17 ------
 hw/ppc/e500.c         |  37 +-----------
 hw/ppc/e500_ccsr.c    | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/ppc/mpc8544_guts.c | 143 -----------------------------------------------
 6 files changed, 162 insertions(+), 200 deletions(-)
 delete mode 100644 hw/ppc/e500-ccsr.h
 create mode 100644 hw/ppc/e500_ccsr.c
 delete mode 100644 hw/ppc/mpc8544_guts.c
hw/pci-host/ppce500.c +5 -5
index 39cd24464d..f2d108bc8a 100644
@@ -16,7 +16,6 @@
 
 #include "qemu/osdep.h"
 #include "hw/hw.h"
-#include "hw/ppc/e500-ccsr.h"
 #include "hw/pci/pci.h"
 #include "hw/pci/pci_host.h"
 #include "qemu/bswap.h"
@@ -420,16 +419,17 @@ static const VMStateDescription vmstate_ppce500_pci = {
 static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp)
 {
     PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d);
-    PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(),
-                                  "/e500-ccsr"));
+    SysBusDevice *ccsr = SYS_BUS_DEVICE(container_get(qdev_get_machine(),
+                                                      "/e500-ccsr"));
+    MemoryRegion *ccsr_mr = sysbus_mmio_get_region(ccsr, 0);
 
     pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
     d->config[PCI_HEADER_TYPE] =
         (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
         PCI_HEADER_TYPE_BRIDGE;
 
-    memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", &ccsr->ccsr_space,
-                             0, int128_get64(ccsr->ccsr_space.size));
+    memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", ccsr_mr,
+                             0, memory_region_size(ccsr_mr));
     pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0);
 }
 
hw/ppc/Makefile.objs +2 -1
index 7efc686748..c1a63d0c39 100644
@@ -24,6 +24,7 @@ obj-$(CONFIG_MAC) += mac_oldworld.o
 obj-$(CONFIG_MAC) += mac_newworld.o
 # e500
 obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o
-obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o
+obj-$(CONFIG_E500) += ppce500_spin.o
+obj-$(CONFIG_E500) += e500_ccsr.o
 # PowerPC 440 Xilinx ML507 reference board.
 obj-$(CONFIG_XILINX) += virtex_ml507.o
hw/ppc/e500-ccsr.h deleted +0 -17
deleted file mode 100644
index 12a2ba4b97..0000000000
@@ -1,17 +0,0 @@
-#ifndef E500_CCSR_H
-#define E500_CCSR_H
-
-#include "hw/sysbus.h"
-
-typedef struct PPCE500CCSRState {
-    /*< private >*/
-    SysBusDevice parent;
-    /*< public >*/
-
-    MemoryRegion ccsr_space;
-} PPCE500CCSRState;
-
-#define TYPE_CCSR "e500-ccsr"
-#define CCSR(obj) OBJECT_CHECK(PPCE500CCSRState, (obj), TYPE_CCSR)
-
-#endif /* E500_CCSR_H */
hw/ppc/e500.c +3 -34
index 9e7e1b29c4..474a46a985 100644
@@ -15,10 +15,10 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/log.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
 #include "e500.h"
-#include "e500-ccsr.h"
 #include "net/net.h"
 #include "qemu/config-file.h"
 #include "hw/hw.h"
@@ -795,7 +795,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     CPUPPCState *firstenv = NULL;
     MemoryRegion *ccsr_addr_space;
     SysBusDevice *s;
-    PPCE500CCSRState *ccsr;
 
     irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *));
     irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB);
@@ -854,11 +853,9 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     dev = qdev_create(NULL, "e500-ccsr");
     object_property_add_child(qdev_get_machine(), "e500-ccsr",
                               OBJECT(dev), NULL);
+    qdev_prop_set_uint32(dev, "base", params->ccsrbar_base);
     qdev_init_nofail(dev);
-    ccsr = CCSR(dev);
-    ccsr_addr_space = &ccsr->ccsr_space;
-    memory_region_add_subregion(address_space_mem, params->ccsrbar_base,
-                                ccsr_addr_space);
+    ccsr_addr_space = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 
     mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs);
 
@@ -875,13 +872,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
                        serial_hds[1], DEVICE_BIG_ENDIAN);
     }
 
-    /* General Utility device */
-    dev = qdev_create(NULL, "mpc8544-guts");
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    memory_region_add_subregion(ccsr_addr_space, MPC8544_UTIL_OFFSET,
-                                sysbus_mmio_get_region(s, 0));
-
     /* PCI */
     dev = qdev_create(NULL, "e500-pcihost");
     qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot);
@@ -1039,24 +1029,3 @@ void ppce500_init(MachineState *machine, PPCE500Params *params)
     boot_info->dt_base = dt_base;
     boot_info->dt_size = dt_size;
 }
-
-static void e500_ccsr_initfn(Object *obj)
-{
-    PPCE500CCSRState *ccsr = CCSR(obj);
-    memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr",
-                       MPC8544_CCSRBAR_SIZE);
-}
-
-static const TypeInfo e500_ccsr_info = {
-    .name          = TYPE_CCSR,
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PPCE500CCSRState),
-    .instance_init = e500_ccsr_initfn,
-};
-
-static void e500_register_types(void)
-{
-    type_register_static(&e500_ccsr_info);
-}
-
-type_init(e500_register_types)
hw/ppc/e500_ccsr.c new file +152 -0
new file mode 100644
index 0000000000..1b586c3f42
@@ -0,0 +1,152 @@
+/*
+ * MPC8540 and MPC8544 specific control registers.
+ * Not really part of e500 spec, but common to many
+ * Freescale parts w/ e500 cores.
+ *
+ * Copyright (c) 2017 Michael Davidsaver
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Authors: Alexander Graf, <[email protected]>
+ *          Michael Davidsaver <[email protected]>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the LICENSE file in the top-level directory.
+ *
+ * This model was developed according to:
+ *
+ * MPC8544E PowerQUICC III Integrated Host Processor Family Reference Manual
+ * Rev. 1
+ *
+ * MPC8540 PowerQUICC III Integrated Host Processor Reference Manual, Rev. 1
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+
+/* E500_ denotes registers common to all */
+
+#define E500_PVR         (0xE00A0)
+#define E500_SVR         (0xE00A4)
+
+#define MPC8544_RSTCR       (0xE00B0)
+#define MPC8544_RSTCR_RESET      (0x02)
+
+typedef struct {
+    /*< private >*/
+    SysBusDevice parent_obj;
+    /*< public >*/
+
+    MemoryRegion iomem;
+
+    uint32_t defbase;
+} CCSRState;
+
+#define TYPE_E500_CCSR "e500-ccsr"
+#define E500_CCSR(obj) OBJECT_CHECK(CCSRState, (obj), TYPE_E500_CCSR)
+
+static uint64_t e500_ccsr_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    CPUPPCState *env = &cpu->env;
+
+    switch (addr) {
+    case E500_PVR:
+        return env->spr[SPR_PVR];
+    case E500_SVR:
+        return env->spr[SPR_E500_SVR];
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR | LOG_UNIMP,
+                  "can't read undefined ccsr regster %x\n",
+                  (unsigned)addr);
+    return 0;
+}
+
+static void e500_ccsr_write(void *opaque, hwaddr addr,
+                               uint64_t value, unsigned size)
+{
+    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
+    CPUPPCState *env = &cpu->env;
+    uint32_t svr = env->spr[SPR_E500_SVR] >> 16;
+
+    switch (svr) {
+    case 0: /* generic.  assumed to be mpc8544ds or e500plat board */
+    case 0x8034: /* mpc8544 */
+    case 0x803C: /* mpc8544E */
+        switch (addr) {
+        case MPC8544_RSTCR:
+            if (value & MPC8544_RSTCR_RESET) {
+                qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+            }
+            return;
+        }
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR | LOG_UNIMP,
+                  "can't write undefined ccsr regster %x <- %08x\n",
+                  (unsigned)addr, (unsigned)value);
+}
+
+static const MemoryRegionOps e500_ccsr_ops = {
+    .read = e500_ccsr_read,
+    .write = e500_ccsr_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static void e500_ccsr_reset(DeviceState *dev)
+{
+    CCSRState *ccsr = E500_CCSR(dev);
+
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ccsr->defbase);
+}
+
+static void e500_ccsr_initfn(Object *obj)
+{
+    CCSRState *ccsr = E500_CCSR(obj);
+
+    memory_region_init_io(&ccsr->iomem, obj, &e500_ccsr_ops,
+                          ccsr, "e500-ccsr", 1024 * 1024);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &ccsr->iomem);
+
+}
+
+static Property e500_ccsr_props[] = {
+    DEFINE_PROP_UINT32("base", CCSRState, defbase, 0xff700000),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static
+void e500_ccsr_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = e500_ccsr_props;
+    dc->reset = e500_ccsr_reset;
+}
+
+static const TypeInfo e500_ccsr_info = {
+    .name          = TYPE_E500_CCSR,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(CCSRState),
+    .instance_init = e500_ccsr_initfn,
+    .class_size    = sizeof(SysBusDeviceClass),
+    .class_init    = e500_ccsr_class_initfn
+};
+
+static void e500_ccsr_register_types(void)
+{
+    type_register_static(&e500_ccsr_info);
+}
+
+type_init(e500_ccsr_register_types)
hw/ppc/mpc8544_guts.c deleted +0 -143
deleted file mode 100644
index ce1254b5d4..0000000000
@@ -1,143 +0,0 @@
-/*
- * QEMU PowerPC MPC8544 global util pseudo-device
- *
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All rights reserved.
- *
- * Author: Alexander Graf, <[email protected]>
- *
- * This 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.
- *
- * *****************************************************************
- *
- * The documentation for this device is noted in the MPC8544 documentation,
- * file name "MPC8544ERM.pdf". You can easily find it on the web.
- *
- */
-
-#include "qemu/osdep.h"
-#include "qemu-common.h"
-#include "cpu.h"
-#include "hw/hw.h"
-#include "sysemu/sysemu.h"
-#include "hw/sysbus.h"
-
-#define MPC8544_GUTS_MMIO_SIZE        0x1000
-#define MPC8544_GUTS_RSTCR_RESET      0x02
-
-#define MPC8544_GUTS_ADDR_PORPLLSR    0x00
-#define MPC8544_GUTS_ADDR_PORBMSR     0x04
-#define MPC8544_GUTS_ADDR_PORIMPSCR   0x08
-#define MPC8544_GUTS_ADDR_PORDEVSR    0x0C
-#define MPC8544_GUTS_ADDR_PORDBGMSR   0x10
-#define MPC8544_GUTS_ADDR_PORDEVSR2   0x14
-#define MPC8544_GUTS_ADDR_GPPORCR     0x20
-#define MPC8544_GUTS_ADDR_GPIOCR      0x30
-#define MPC8544_GUTS_ADDR_GPOUTDR     0x40
-#define MPC8544_GUTS_ADDR_GPINDR      0x50
-#define MPC8544_GUTS_ADDR_PMUXCR      0x60
-#define MPC8544_GUTS_ADDR_DEVDISR     0x70
-#define MPC8544_GUTS_ADDR_POWMGTCSR   0x80
-#define MPC8544_GUTS_ADDR_MCPSUMR     0x90
-#define MPC8544_GUTS_ADDR_RSTRSCR     0x94
-#define MPC8544_GUTS_ADDR_PVR         0xA0
-#define MPC8544_GUTS_ADDR_SVR         0xA4
-#define MPC8544_GUTS_ADDR_RSTCR       0xB0
-#define MPC8544_GUTS_ADDR_IOVSELSR    0xC0
-#define MPC8544_GUTS_ADDR_DDRCSR      0xB20
-#define MPC8544_GUTS_ADDR_DDRCDR      0xB24
-#define MPC8544_GUTS_ADDR_DDRCLKDR    0xB28
-#define MPC8544_GUTS_ADDR_CLKOCR      0xE00
-#define MPC8544_GUTS_ADDR_SRDS1CR1    0xF04
-#define MPC8544_GUTS_ADDR_SRDS2CR1    0xF10
-#define MPC8544_GUTS_ADDR_SRDS2CR3    0xF18
-
-#define TYPE_MPC8544_GUTS "mpc8544-guts"
-#define MPC8544_GUTS(obj) OBJECT_CHECK(GutsState, (obj), TYPE_MPC8544_GUTS)
-
-struct GutsState {
-    /*< private >*/
-    SysBusDevice parent_obj;
-    /*< public >*/
-
-    MemoryRegion iomem;
-};
-
-typedef struct GutsState GutsState;
-
-static uint64_t mpc8544_guts_read(void *opaque, hwaddr addr,
-                                  unsigned size)
-{
-    uint32_t value = 0;
-    PowerPCCPU *cpu = POWERPC_CPU(current_cpu);
-    CPUPPCState *env = &cpu->env;
-
-    addr &= MPC8544_GUTS_MMIO_SIZE - 1;
-    switch (addr) {
-    case MPC8544_GUTS_ADDR_PVR:
-        value = env->spr[SPR_PVR];
-        break;
-    case MPC8544_GUTS_ADDR_SVR:
-        value = env->spr[SPR_E500_SVR];
-        break;
-    default:
-        fprintf(stderr, "guts: Unknown register read: %x\n", (int)addr);
-        break;
-    }
-
-    return value;
-}
-
-static void mpc8544_guts_write(void *opaque, hwaddr addr,
-                               uint64_t value, unsigned size)
-{
-    addr &= MPC8544_GUTS_MMIO_SIZE - 1;
-
-    switch (addr) {
-    case MPC8544_GUTS_ADDR_RSTCR:
-        if (value & MPC8544_GUTS_RSTCR_RESET) {
-            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
-        }
-        break;
-    default:
-        fprintf(stderr, "guts: Unknown register write: %x = %x\n",
-                (int)addr, (unsigned)value);
-        break;
-    }
-}
-
-static const MemoryRegionOps mpc8544_guts_ops = {
-    .read = mpc8544_guts_read,
-    .write = mpc8544_guts_write,
-    .endianness = DEVICE_BIG_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-};
-
-static void mpc8544_guts_initfn(Object *obj)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(obj);
-    GutsState *s = MPC8544_GUTS(obj);
-
-    memory_region_init_io(&s->iomem, OBJECT(s), &mpc8544_guts_ops, s,
-                          "mpc8544.guts", MPC8544_GUTS_MMIO_SIZE);
-    sysbus_init_mmio(d, &s->iomem);
-}
-
-static const TypeInfo mpc8544_guts_info = {
-    .name          = TYPE_MPC8544_GUTS,
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(GutsState),
-    .instance_init = mpc8544_guts_initfn,
-};
-
-static void mpc8544_guts_register_types(void)
-{
-    type_register_static(&mpc8544_guts_info);
-}
-
-type_init(mpc8544_guts_register_types)
Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#29 lore raw

Re: [Qemu-devel] [PATCH 02/12] e500: consolidate mpc8540 guts with e500-ccsr

Quoted 10 lines · David Gibson
Quoted 9 lines · Michael Davidsaver

Preparation for adding more MPCxxxx control
registers.

Use e500 SVR to enable part specific registers.
Only the mpc8544 reset register at present.

Expose CCSR as SysBusDevice region to eliminate
e500-ccsr.h.

Quoted 6 lines
Quoted 4 lines

Track CCSR base address within device, and map on reset, in preparation for CCSRBAR.

Applied to ppc-for-2.12.

Sorry. I've now pulled this from ppc-for-2.12, because it caused a breakage of make check (specifically the boot-serial-test with ppc64).

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#30 lore raw

Re: [Qemu-devel] [PATCH 02/12] e500: consolidate mpc8540 guts with e500-ccsr

Quoted 14 lines · David Gibson
Quoted 13 lines · David Gibson
Quoted 12 lines · Michael Davidsaver

Preparation for adding more MPCxxxx control
registers.

Use e500 SVR to enable part specific registers.
Only the mpc8544 reset register at present.

Expose CCSR as SysBusDevice region to eliminate
e500-ccsr.h.

Track CCSR base address within device, and map on reset, in preparation for CCSRBAR.

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

Applied to ppc-for-2.12.

Sorry. I've now pulled this from ppc-for-2.12, because it caused a breakage of make check (specifically the boot-serial-test with ppc64).

Oops, I'll begin running this test myself. So far I've only been running tests for i386, amd64, arm, and ppc.

#31 lore raw

Re: [Qemu-devel] [PATCH 02/12] e500: consolidate mpc8540 guts with e500-ccsr

Quoted 17 lines · Michael Davidsaver
Quoted 16 lines · David Gibson
Quoted 15 lines · David Gibson
Quoted 13 lines · Michael Davidsaver

Preparation for adding more MPCxxxx control
registers.

Use e500 SVR to enable part specific registers.
Only the mpc8544 reset register at present.

Expose CCSR as SysBusDevice region to eliminate
e500-ccsr.h.

Track CCSR base address within device, and map on reset, in preparation for CCSRBAR.

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Applied to ppc-for-2.12.

Sorry. I've now pulled this from ppc-for-2.12, because it caused a breakage of make check (specifically the boot-serial-test with ppc64).

Oops, I'll begin running this test myself. So far I've only been running tests for i386, amd64, arm, and ppc.

I really recommend running a build and make check smoke test for all targets ("./configure && make && make check SPEED=slow") before posting patches for submission. It takes a while, but not stupidly long.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#32 lore raw
In reply to Michael Davidsaver
Quoted 28 lines · Michael Davidsaver

This series adds simulation of MVME3100 powerpc SBCs, originally from Motorola, and now sold by Artesyn[1]. There are two variants differing in CPU speed and memory size.

I've been working on this sporadically for the past 2 year. Recently I've finished all the features which I have in mind. If this series is accepted there is a continuation which adds VME bus. I've found it useful in software compatibility testing. I wonder if there is any interest at large?

There are two main parts of this series. 1-5 are changing code common with the "ppce500" and "mpc8544ds" boards, with the remainder being additions.

The changes are to how the CCSR region is handled in order to support the CCSRBAR register which allows the whole region to be relocated. Also added are a couple of memory and clock configuration registers which RTEMS guests read.

#3 is actually a minor issue I found recently with the mpc8544 PCI host bridge, which I'm uncertain how to address. The host bridge device 0:0 identifies itself as a bridge, but doesn't properly implement the bridge config registers. This confuses Linux, which then does a full re-enumeration (successfully).

The rest are additions of an I2C controller, an I2C eeprom, an I2C RTC, and new board code.

Quoted 7 lines

My testing has been almost exclusively with an RTEMS guest[2]. Though I have recently done a little with Linux.

RTEMS guests (and Linux too for now) require a stub bootloader[3] to put the system in the same state as the real bootloader. RTEMS has an unfortunately strong dependence on bootloader provided configuration (eg. it doesn't re-enumerate the PCI bus).

I've applied several patches from this series to ppc-for-2.12, others I've commented on. If you could address the comments and rebase what's left on ppc-for-2.12, that would be great.

Signature · 4 lines
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson
#33 lore raw

On 11/21/2017 10:12 PM, David Gibson wrote:
...

Quoted 3 lines

I've applied several patches from this series to ppc-for-2.12, others I've commented on. If you could address the comments and rebase what's left on ppc-for-2.12, that would be great.

Will do.