target-arm queue:
* xlnx-zdma: Fix endianness handling of descriptor loading * nrf51: Fix last GPIO CNF address * gicv3: Use gicr_typer in arm_gicv3_icc_reset * msf2: Add EMAC block to SmartFusion2 SoC * New clock modelling framework * hw/arm: versal: Setup the ADMA with 128bit bus-width * Cadence: gem: fix wraparound in 64bit descriptors * cadence_gem: clear RX control descriptor * target/arm: Vectorize integer comparison vs zero * hw/arm/virt: dt: add kaslr-seed property * hw/arm: xlnx-zcu102: Disable unsupported FDT firmware nodes -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl6q5CoZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3pWPD/9zjcV3TlOUWg/2aRQOYWB1 I/h2AGTI09Y/nGMmwvHEyQKyAg6mL8KfJwCUDHr1pE3DeTt4Z7dA+3rhk1uy+gKA Ot/7e4IVSMiNh28xkBiSPviBXjYtgmVjvSlgKn4fty6g+30wdGV8ymNz1wXO8II0 5cuGlaz0VQ4N+W4qz9kuaJNEAsMSnmrJ9fUzZDllRsNy4li3aSR4sQ9CymsJ23+3 9CdStk/ibA7tExDX5qkj4lKozENEAU/jethA91CQCMLnK/7aGfHbLqVyWu6xDuQ7 oTdyXr7nrGIUjod+Cx7mLyUQKXVfsiw0x4kmjvOnaVZHh5oIgDj83vWXQ28nC6P4 wVYCRWpg68GPuaEru8VeocdoATMa1ONjrv5/gFGOxlma4AjD07WQ53hTp2pL0HT2 +uYPwm2iSYgYKX7QV/rbNzWHK1nYq6/3LDeVQc6nr/3jVewpZngnf2pMxChRUUoT qtdLwJL/om9hqV4lsU7cxHKSNnkocfDhjkwRy6wg0L/iXDftt1sKbZO+G78vvsow S+NqjpAo4m+P7ExS8DGiSsgvQIQIHvcjjpeym4fWmBxPaXep6oUIewzBuExcYWK8 XogFZEnW6PNyr/CKLh7GYH9C0F6FI36+yPUZFxvdBpz4w5QBADYKyyG0/53P0uKa ez3ixFfplzcx8RIiy+nIsQ== =9plU -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200430-1' into staging target-arm queue: * xlnx-zdma: Fix endianness handling of descriptor loading * nrf51: Fix last GPIO CNF address * gicv3: Use gicr_typer in arm_gicv3_icc_reset * msf2: Add EMAC block to SmartFusion2 SoC * New clock modelling framework * hw/arm: versal: Setup the ADMA with 128bit bus-width * Cadence: gem: fix wraparound in 64bit descriptors * cadence_gem: clear RX control descriptor * target/arm: Vectorize integer comparison vs zero * hw/arm/virt: dt: add kaslr-seed property * hw/arm: xlnx-zcu102: Disable unsupported FDT firmware nodes # gpg: Signature made Thu 30 Apr 2020 15:43:54 BST # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20200430-1: (30 commits) hw/arm: xlnx-zcu102: Disable unsupported FDT firmware nodes hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102 device_tree: Constify compat in qemu_fdt_node_path() device_tree: Allow name wildcards in qemu_fdt_node_path() target/arm/cpu: Update coding style to make checkpatch.pl happy target/arm: Make cpu_register() available for other files target/arm: Restrict the Address Translate write operation to TCG accel hw/arm/virt: dt: add kaslr-seed property hw/arm/virt: dt: move creation of /secure-chosen to create_fdt() target/arm: Vectorize integer comparison vs zero net: cadence_gem: clear RX control descriptor Cadence: gem: fix wraparound in 64bit descriptors hw/arm: versal: Setup the ADMA with 128bit bus-width qdev-monitor: print the device's clock with info qtree hw/arm/xilinx_zynq: connect uart clocks to slcr hw/char/cadence_uart: add clock support hw/misc/zynq_slcr: add clock generation for uarts docs/clocks: add device's clock documentation qdev-clock: introduce an init array to ease the device construction qdev: add clock input&output support to devices. ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
126eeee6c7
@ -921,6 +921,8 @@ F: include/hw/arm/msf2-soc.h
|
||||
F: include/hw/misc/msf2-sysreg.h
|
||||
F: include/hw/timer/mss-timer.h
|
||||
F: include/hw/ssi/mss-spi.h
|
||||
F: hw/net/msf2-emac.c
|
||||
F: include/hw/net/msf2-emac.h
|
||||
|
||||
Emcraft M2S-FG484
|
||||
M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
||||
|
@ -291,7 +291,7 @@ char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp)
|
||||
return path_array;
|
||||
}
|
||||
|
||||
char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
|
||||
char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
|
||||
Error **errp)
|
||||
{
|
||||
int offset, len, ret;
|
||||
@ -308,7 +308,7 @@ char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
|
||||
offset = len;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(iter_name, name)) {
|
||||
if (!name || !strcmp(iter_name, name)) {
|
||||
char *path;
|
||||
|
||||
path = g_malloc(path_len);
|
||||
|
391
docs/devel/clocks.rst
Normal file
391
docs/devel/clocks.rst
Normal file
@ -0,0 +1,391 @@
|
||||
Modelling a clock tree in QEMU
|
||||
==============================
|
||||
|
||||
What are clocks?
|
||||
----------------
|
||||
|
||||
Clocks are QOM objects developed for the purpose of modelling the
|
||||
distribution of clocks in QEMU.
|
||||
|
||||
They allow us to model the clock distribution of a platform and detect
|
||||
configuration errors in the clock tree such as badly configured PLL, clock
|
||||
source selection or disabled clock.
|
||||
|
||||
The object is *Clock* and its QOM name is ``clock`` (in C code, the macro
|
||||
``TYPE_CLOCK``).
|
||||
|
||||
Clocks are typically used with devices where they are used to model inputs
|
||||
and outputs. They are created in a similar way to GPIOs. Inputs and outputs
|
||||
of different devices can be connected together.
|
||||
|
||||
In these cases a Clock object is a child of a Device object, but this
|
||||
is not a requirement. Clocks can be independent of devices. For
|
||||
example it is possible to create a clock outside of any device to
|
||||
model the main clock source of a machine.
|
||||
|
||||
Here is an example of clocks::
|
||||
|
||||
+---------+ +----------------------+ +--------------+
|
||||
| Clock 1 | | Device B | | Device C |
|
||||
| | | +-------+ +-------+ | | +-------+ |
|
||||
| |>>-+-->>|Clock 2| |Clock 3|>>--->>|Clock 6| |
|
||||
+---------+ | | | (in) | | (out) | | | | (in) | |
|
||||
| | +-------+ +-------+ | | +-------+ |
|
||||
| | +-------+ | +--------------+
|
||||
| | |Clock 4|>>
|
||||
| | | (out) | | +--------------+
|
||||
| | +-------+ | | Device D |
|
||||
| | +-------+ | | +-------+ |
|
||||
| | |Clock 5|>>--->>|Clock 7| |
|
||||
| | | (out) | | | | (in) | |
|
||||
| | +-------+ | | +-------+ |
|
||||
| +----------------------+ | |
|
||||
| | +-------+ |
|
||||
+----------------------------->>|Clock 8| |
|
||||
| | (in) | |
|
||||
| +-------+ |
|
||||
+--------------+
|
||||
|
||||
Clocks are defined in the ``include/hw/clock.h`` header and device
|
||||
related functions are defined in the ``include/hw/qdev-clock.h``
|
||||
header.
|
||||
|
||||
The clock state
|
||||
---------------
|
||||
|
||||
The state of a clock is its period; it is stored as an integer
|
||||
representing it in units of 2 :sup:`-32` ns. The special value of 0 is used to
|
||||
represent the clock being inactive or gated. The clocks do not model
|
||||
the signal itself (pin toggling) or other properties such as the duty
|
||||
cycle.
|
||||
|
||||
All clocks contain this state: outputs as well as inputs. This allows
|
||||
the current period of a clock to be fetched at any time. When a clock
|
||||
is updated, the value is immediately propagated to all connected
|
||||
clocks in the tree.
|
||||
|
||||
To ease interaction with clocks, helpers with a unit suffix are defined for
|
||||
every clock state setter or getter. The suffixes are:
|
||||
|
||||
- ``_ns`` for handling periods in nanoseconds
|
||||
- ``_hz`` for handling frequencies in hertz
|
||||
|
||||
The 0 period value is converted to 0 in hertz and vice versa. 0 always means
|
||||
that the clock is disabled.
|
||||
|
||||
Adding a new clock
|
||||
------------------
|
||||
|
||||
Adding clocks to a device must be done during the init method of the Device
|
||||
instance.
|
||||
|
||||
To add an input clock to a device, the function ``qdev_init_clock_in()``
|
||||
must be used. It takes the name, a callback and an opaque parameter
|
||||
for the callback (this will be explained in a following section).
|
||||
Output is simpler; only the name is required. Typically::
|
||||
|
||||
qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
|
||||
qdev_init_clock_out(DEVICE(dev), "clk_out");
|
||||
|
||||
Both functions return the created Clock pointer, which should be saved in the
|
||||
device's state structure for further use.
|
||||
|
||||
These objects will be automatically deleted by the QOM reference mechanism.
|
||||
|
||||
Note that it is possible to create a static array describing clock inputs and
|
||||
outputs. The function ``qdev_init_clocks()`` must be called with the array as
|
||||
parameter to initialize the clocks: it has the same behaviour as calling the
|
||||
``qdev_init_clock_in/out()`` for each clock in the array. To ease the array
|
||||
construction, some macros are defined in ``include/hw/qdev-clock.h``.
|
||||
As an example, the following creates 2 clocks to a device: one input and one
|
||||
output.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* device structure containing pointers to the clock objects */
|
||||
typedef struct MyDeviceState {
|
||||
DeviceState parent_obj;
|
||||
Clock *clk_in;
|
||||
Clock *clk_out;
|
||||
} MyDeviceState;
|
||||
|
||||
/*
|
||||
* callback for the input clock (see "Callback on input clock
|
||||
* change" section below for more information).
|
||||
*/
|
||||
static void clk_in_callback(void *opaque);
|
||||
|
||||
/*
|
||||
* static array describing clocks:
|
||||
* + a clock input named "clk_in", whose pointer is stored in
|
||||
* the clk_in field of a MyDeviceState structure with callback
|
||||
* clk_in_callback.
|
||||
* + a clock output named "clk_out" whose pointer is stored in
|
||||
* the clk_out field of a MyDeviceState structure.
|
||||
*/
|
||||
static const ClockPortInitArray mydev_clocks = {
|
||||
QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
|
||||
QDEV_CLOCK_OUT(MyDeviceState, clk_out),
|
||||
QDEV_CLOCK_END
|
||||
};
|
||||
|
||||
/* device initialization function */
|
||||
static void mydev_init(Object *obj)
|
||||
{
|
||||
/* cast to MyDeviceState */
|
||||
MyDeviceState *mydev = MYDEVICE(obj);
|
||||
/* create and fill the pointer fields in the MyDeviceState */
|
||||
qdev_init_clocks(mydev, mydev_clocks);
|
||||
[...]
|
||||
}
|
||||
|
||||
An alternative way to create a clock is to simply call
|
||||
``object_new(TYPE_CLOCK)``. In that case the clock will neither be an
|
||||
input nor an output of a device. After the whole QOM hierarchy of the
|
||||
clock has been set ``clock_setup_canonical_path()`` should be called.
|
||||
|
||||
At creation, the period of the clock is 0: the clock is disabled. You can
|
||||
change it using ``clock_set_ns()`` or ``clock_set_hz()``.
|
||||
|
||||
Note that if you are creating a clock with a fixed period which will never
|
||||
change (for example the main clock source of a board), then you'll have
|
||||
nothing else to do. This value will be propagated to other clocks when
|
||||
connecting the clocks together and devices will fetch the right value during
|
||||
the first reset.
|
||||
|
||||
Retrieving clocks from a device
|
||||
-------------------------------
|
||||
|
||||
``qdev_get_clock_in()`` and ``dev_get_clock_out()`` are available to
|
||||
get the clock inputs or outputs of a device. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
Clock *clk = qdev_get_clock_in(DEVICE(mydev), "clk_in");
|
||||
|
||||
or:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
Clock *clk = qdev_get_clock_out(DEVICE(mydev), "clk_out");
|
||||
|
||||
Connecting two clocks together
|
||||
------------------------------
|
||||
|
||||
To connect two clocks together, use the ``clock_set_source()`` function.
|
||||
Given two clocks ``clk1``, and ``clk2``, ``clock_set_source(clk2, clk1);``
|
||||
configures ``clk2`` to follow the ``clk1`` period changes. Every time ``clk1``
|
||||
is updated, ``clk2`` will be updated too.
|
||||
|
||||
When connecting clock between devices, prefer using the
|
||||
``qdev_connect_clock_in()`` function to set the source of an input
|
||||
device clock. For example, to connect the input clock ``clk2`` of
|
||||
``devB`` to the output clock ``clk1`` of ``devA``, do:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
qdev_connect_clock_in(devB, "clk2", qdev_get_clock_out(devA, "clk1"))
|
||||
|
||||
We used ``qdev_get_clock_out()`` above, but any clock can drive an
|
||||
input clock, even another input clock. The following diagram shows
|
||||
some examples of connections. Note also that a clock can drive several
|
||||
other clocks.
|
||||
|
||||
::
|
||||
|
||||
+------------+ +--------------------------------------------------+
|
||||
| Device A | | Device B |
|
||||
| | | +---------------------+ |
|
||||
| | | | Device C | |
|
||||
| +-------+ | | +-------+ | +-------+ +-------+ | +-------+ |
|
||||
| |Clock 1|>>-->>|Clock 2|>>+-->>|Clock 3| |Clock 5|>>>>|Clock 6|>>
|
||||
| | (out) | | | | (in) | | | | (in) | | (out) | | | (out) | |
|
||||
| +-------+ | | +-------+ | | +-------+ +-------+ | +-------+ |
|
||||
+------------+ | | +---------------------+ |
|
||||
| | |
|
||||
| | +--------------+ |
|
||||
| | | Device D | |
|
||||
| | | +-------+ | |
|
||||
| +-->>|Clock 4| | |
|
||||
| | | (in) | | |
|
||||
| | +-------+ | |
|
||||
| +--------------+ |
|
||||
+--------------------------------------------------+
|
||||
|
||||
In the above example, when *Clock 1* is updated by *Device A*, three
|
||||
clocks get the new clock period value: *Clock 2*, *Clock 3* and *Clock 4*.
|
||||
|
||||
It is not possible to disconnect a clock or to change the clock connection
|
||||
after it is connected.
|
||||
|
||||
Unconnected input clocks
|
||||
------------------------
|
||||
|
||||
A newly created input clock is disabled (period of 0). This means the
|
||||
clock will be considered as disabled until the period is updated. If
|
||||
the clock remains unconnected it will always keep its initial value
|
||||
of 0. If this is not the desired behaviour, ``clock_set()``,
|
||||
``clock_set_ns()`` or ``clock_set_hz()`` should be called on the Clock
|
||||
object during device instance init. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
|
||||
dev);
|
||||
/* set initial value to 10ns / 100MHz */
|
||||
clock_set_ns(clk, 10);
|
||||
|
||||
Fetching clock frequency/period
|
||||
-------------------------------
|
||||
|
||||
To get the current state of a clock, use the functions ``clock_get()``,
|
||||
``clock_get_ns()`` or ``clock_get_hz()``.
|
||||
|
||||
It is also possible to register a callback on clock frequency changes.
|
||||
Here is an example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void clock_callback(void *opaque) {
|
||||
MyDeviceState *s = (MyDeviceState *) opaque;
|
||||
/*
|
||||
* 'opaque' is the argument passed to qdev_init_clock_in();
|
||||
* usually this will be the device state pointer.
|
||||
*/
|
||||
|
||||
/* do something with the new period */
|
||||
fprintf(stdout, "device new period is %" PRIu64 "ns\n",
|
||||
clock_get_ns(dev->my_clk_input));
|
||||
}
|
||||
|
||||
Changing a clock period
|
||||
-----------------------
|
||||
|
||||
A device can change its outputs using the ``clock_update()``,
|
||||
``clock_update_ns()`` or ``clock_update_hz()`` function. It will trigger
|
||||
updates on every connected input.
|
||||
|
||||
For example, let's say that we have an output clock *clkout* and we
|
||||
have a pointer to it in the device state because we did the following
|
||||
in init phase:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
dev->clkout = qdev_init_clock_out(DEVICE(dev), "clkout");
|
||||
|
||||
Then at any time (apart from the cases listed below), it is possible to
|
||||
change the clock value by doing:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
clock_update_hz(dev->clkout, 1000 * 1000 * 1000); /* 1GHz */
|
||||
|
||||
Because updating a clock may trigger any side effects through
|
||||
connected clocks and their callbacks, this operation must be done
|
||||
while holding the qemu io lock.
|
||||
|
||||
For the same reason, one can update clocks only when it is allowed to have
|
||||
side effects on other objects. In consequence, it is forbidden:
|
||||
|
||||
* during migration,
|
||||
* and in the enter phase of reset.
|
||||
|
||||
Note that calling ``clock_update[_ns|_hz]()`` is equivalent to calling
|
||||
``clock_set[_ns|_hz]()`` (with the same arguments) then
|
||||
``clock_propagate()`` on the clock. Thus, setting the clock value can
|
||||
be separated from triggering the side-effects. This is often required
|
||||
to factorize code to handle reset and migration in devices.
|
||||
|
||||
Aliasing clocks
|
||||
---------------
|
||||
|
||||
Sometimes, one needs to forward, or inherit, a clock from another
|
||||
device. Typically, when doing device composition, a device might
|
||||
expose a sub-device's clock without interfering with it. The function
|
||||
``qdev_alias_clock()`` can be used to achieve this behaviour. Note
|
||||
that it is possible to expose the clock under a different name.
|
||||
``qdev_alias_clock()`` works for both input and output clocks.
|
||||
|
||||
For example, if device B is a child of device A,
|
||||
``device_a_instance_init()`` may do something like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void device_a_instance_init(Object *obj)
|
||||
{
|
||||
AState *A = DEVICE_A(obj);
|
||||
BState *B;
|
||||
/* create object B as child of A */
|
||||
[...]
|
||||
qdev_alias_clock(B, "clk", A, "b_clk");
|
||||
/*
|
||||
* Now A has a clock "b_clk" which is an alias to
|
||||
* the clock "clk" of its child B.
|
||||
*/
|
||||
}
|
||||
|
||||
This function does not return any clock object. The new clock has the
|
||||
same direction (input or output) as the original one. This function
|
||||
only adds a link to the existing clock. In the above example, object B
|
||||
remains the only object allowed to use the clock and device A must not
|
||||
try to change the clock period or set a callback to the clock. This
|
||||
diagram describes the example with an input clock::
|
||||
|
||||
+--------------------------+
|
||||
| Device A |
|
||||
| +--------------+ |
|
||||
| | Device B | |
|
||||
| | +-------+ | |
|
||||
>>"b_clk">>>| "clk" | | |
|
||||
| (in) | | (in) | | |
|
||||
| | +-------+ | |
|
||||
| +--------------+ |
|
||||
+--------------------------+
|
||||
|
||||
Migration
|
||||
---------
|
||||
|
||||
Clock state is not migrated automatically. Every device must handle its
|
||||
clock migration. Alias clocks must not be migrated.
|
||||
|
||||
To ensure clock states are restored correctly during migration, there
|
||||
are two solutions.
|
||||
|
||||
Clock states can be migrated by adding an entry into the device
|
||||
vmstate description. You should use the ``VMSTATE_CLOCK`` macro for this.
|
||||
This is typically used to migrate an input clock state. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
MyDeviceState {
|
||||
DeviceState parent_obj;
|
||||
[...] /* some fields */
|
||||
Clock *clk;
|
||||
};
|
||||
|
||||
VMStateDescription my_device_vmstate = {
|
||||
.name = "my_device",
|
||||
.fields = (VMStateField[]) {
|
||||
[...], /* other migrated fields */
|
||||
VMSTATE_CLOCK(clk, MyDeviceState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
The second solution is to restore the clock state using information already
|
||||
at our disposal. This can be used to restore output clock states using the
|
||||
device state. The functions ``clock_set[_ns|_hz]()`` can be used during the
|
||||
``post_load()`` migration callback.
|
||||
|
||||
When adding clock support to an existing device, if you care about
|
||||
migration compatibility you will need to be careful, as simply adding
|
||||
a ``VMSTATE_CLOCK()`` line will break compatibility. Instead, you can
|
||||
put the ``VMSTATE_CLOCK()`` line into a vmstate subsection with a
|
||||
suitable ``needed`` function, and use ``clock_set()`` in a
|
||||
``pre_load()`` function to set the default value that will be used if
|
||||
the source virtual machine in the migration does not send the clock
|
||||
state.
|
||||
|
||||
Care should be taken not to use ``clock_update[_ns|_hz]()`` or
|
||||
``clock_propagate()`` during the whole migration procedure because it
|
||||
will trigger side effects to other devices in an unknown state.
|
@ -27,3 +27,4 @@ Contents:
|
||||
bitops
|
||||
reset
|
||||
s390-dasd-ipl
|
||||
clocks
|
||||
|
@ -222,7 +222,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
|
||||
state->devs[i].arch_id = id_list->cpus[i].arch_id;
|
||||
}
|
||||
memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state,
|
||||
"acpi-mem-hotplug", ACPI_CPU_HOTPLUG_REG_LEN);
|
||||
"acpi-cpu-hotplug", ACPI_CPU_HOTPLUG_REG_LEN);
|
||||
memory_region_add_subregion(as, base_addr, &state->ctrl_reg);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SmartFusion2 SoC emulation.
|
||||
*
|
||||
* Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
||||
* Copyright (c) 2017-2020 Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -35,11 +35,14 @@
|
||||
|
||||
#define MSF2_TIMER_BASE 0x40004000
|
||||
#define MSF2_SYSREG_BASE 0x40038000
|
||||
#define MSF2_EMAC_BASE 0x40041000
|
||||
|
||||
#define ENVM_BASE_ADDRESS 0x60000000
|
||||
|
||||
#define SRAM_BASE_ADDRESS 0x20000000
|
||||
|
||||
#define MSF2_EMAC_IRQ 12
|
||||
|
||||
#define MSF2_ENVM_MAX_SIZE (512 * KiB)
|
||||
|
||||
/*
|
||||
@ -81,6 +84,13 @@ static void m2sxxx_soc_initfn(Object *obj)
|
||||
sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]),
|
||||
TYPE_MSS_SPI);
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
|
||||
TYPE_MSS_EMAC);
|
||||
if (nd_table[0].used) {
|
||||
qemu_check_nic_model(&nd_table[0], TYPE_MSS_EMAC);
|
||||
qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
@ -192,6 +202,19 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
g_free(bus_name);
|
||||
}
|
||||
|
||||
dev = DEVICE(&s->emac);
|
||||
object_property_set_link(OBJECT(&s->emac), OBJECT(get_system_memory()),
|
||||
"ahb-bus", &error_abort);
|
||||
object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
|
||||
if (err != NULL) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_mmio_map(busdev, 0, MSF2_EMAC_BASE);
|
||||
sysbus_connect_irq(busdev, 0,
|
||||
qdev_get_gpio_in(armv7m, MSF2_EMAC_IRQ));
|
||||
|
||||
/* Below devices are not modelled yet. */
|
||||
create_unimplemented_device("i2c_0", 0x40002000, 0x1000);
|
||||
create_unimplemented_device("dma", 0x40003000, 0x1000);
|
||||
@ -202,7 +225,6 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
|
||||
create_unimplemented_device("can", 0x40015000, 0x1000);
|
||||
create_unimplemented_device("rtc", 0x40017000, 0x1000);
|
||||
create_unimplemented_device("apb_config", 0x40020000, 0x10000);
|
||||
create_unimplemented_device("emac", 0x40041000, 0x1000);
|
||||
create_unimplemented_device("usb", 0x40043000, 0x1000);
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,7 @@
|
||||
#include "hw/acpi/generic_event_device.h"
|
||||
#include "hw/virtio/virtio-iommu.h"
|
||||
#include "hw/char/pl011.h"
|
||||
#include "qemu/guest-random.h"
|
||||
|
||||
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
|
||||
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
|
||||
@ -213,6 +214,18 @@ static bool cpu_type_valid(const char *cpu)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void create_kaslr_seed(VirtMachineState *vms, const char *node)
|
||||
{
|
||||
Error *err = NULL;
|
||||
uint64_t seed;
|
||||
|
||||
if (qemu_guest_getrandom(&seed, sizeof(seed), &err)) {
|
||||
error_free(err);
|
||||
return;
|
||||
}
|
||||
qemu_fdt_setprop_u64(vms->fdt, node, "kaslr-seed", seed);
|
||||
}
|
||||
|
||||
static void create_fdt(VirtMachineState *vms)
|
||||
{
|
||||
MachineState *ms = MACHINE(vms);
|
||||
@ -233,6 +246,12 @@ static void create_fdt(VirtMachineState *vms)
|
||||
|
||||
/* /chosen must exist for load_dtb to fill in necessary properties later */
|
||||
qemu_fdt_add_subnode(fdt, "/chosen");
|
||||
create_kaslr_seed(vms, "/chosen");
|
||||
|
||||
if (vms->secure) {
|
||||
qemu_fdt_add_subnode(fdt, "/secure-chosen");
|
||||
create_kaslr_seed(vms, "/secure-chosen");
|
||||
}
|
||||
|
||||
/* Clock node, for the benefit of the UART. The kernel device tree
|
||||
* binding documentation claims the PL011 node clock properties are
|
||||
@ -761,7 +780,6 @@ static void create_uart(const VirtMachineState *vms, int uart,
|
||||
qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled");
|
||||
qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay");
|
||||
|
||||
qemu_fdt_add_subnode(vms->fdt, "/secure-chosen");
|
||||
qemu_fdt_setprop_string(vms->fdt, "/secure-chosen", "stdout-path",
|
||||
nodename);
|
||||
}
|
||||
|
@ -35,6 +35,15 @@
|
||||
#include "hw/char/cadence_uart.h"
|
||||
#include "hw/net/cadence_gem.h"
|
||||
#include "hw/cpu/a9mpcore.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "sysemu/reset.h"
|
||||
|
||||
#define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9")
|
||||
#define ZYNQ_MACHINE(obj) \
|
||||
OBJECT_CHECK(ZynqMachineState, (obj), TYPE_ZYNQ_MACHINE)
|
||||
|
||||
/* board base frequency: 33.333333 MHz */
|
||||
#define PS_CLK_FREQUENCY (100 * 1000 * 1000 / 3)
|
||||
|
||||
#define NUM_SPI_FLASHES 4
|
||||
#define NUM_QSPI_FLASHES 2
|
||||
@ -75,6 +84,11 @@ static const int dma_irqs[8] = {
|
||||
0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \
|
||||
0xe5801000 + (addr)
|
||||
|
||||
typedef struct ZynqMachineState {
|
||||
MachineState parent;
|
||||
Clock *ps_clk;
|
||||
} ZynqMachineState;
|
||||
|
||||
static void zynq_write_board_setup(ARMCPU *cpu,
|
||||
const struct arm_boot_info *info)
|
||||
{
|
||||
@ -159,10 +173,11 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
|
||||
|
||||
static void zynq_init(MachineState *machine)
|
||||
{
|
||||
ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine);
|
||||
ARMCPU *cpu;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
|
||||
DeviceState *dev;
|
||||
DeviceState *dev, *slcr;
|
||||
SysBusDevice *busdev;
|
||||
qemu_irq pic[64];
|
||||
int n;
|
||||
@ -206,9 +221,18 @@ static void zynq_init(MachineState *machine)
|
||||
1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa,
|
||||
0);
|
||||
|
||||
dev = qdev_create(NULL, "xilinx,zynq_slcr");
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8000000);
|
||||
/* Create slcr, keep a pointer to connect clocks */
|
||||
slcr = qdev_create(NULL, "xilinx,zynq_slcr");
|
||||
qdev_init_nofail(slcr);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(slcr), 0, 0xF8000000);
|
||||
|
||||
/* Create the main clock source, and feed slcr with it */
|
||||
zynq_machine->ps_clk = CLOCK(object_new(TYPE_CLOCK));
|
||||
object_property_add_child(OBJECT(zynq_machine), "ps_clk",
|
||||
OBJECT(zynq_machine->ps_clk), &error_abort);
|
||||
object_unref(OBJECT(zynq_machine->ps_clk));
|
||||
clock_set_hz(zynq_machine->ps_clk, PS_CLK_FREQUENCY);
|
||||
qdev_connect_clock_in(slcr, "ps_clk", zynq_machine->ps_clk);
|
||||
|
||||
dev = qdev_create(NULL, TYPE_A9MPCORE_PRIV);
|
||||
qdev_prop_set_uint32(dev, "num-cpu", 1);
|
||||
@ -229,8 +253,12 @@ static void zynq_init(MachineState *machine)
|
||||
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]);
|
||||
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]);
|
||||
|
||||
cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0));
|
||||
cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1));
|
||||
dev = cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0));
|
||||
qdev_connect_clock_in(dev, "refclk",
|
||||
qdev_get_clock_out(slcr, "uart0_ref_clk"));
|
||||
dev = cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1));
|
||||
qdev_connect_clock_in(dev, "refclk",
|
||||
qdev_get_clock_out(slcr, "uart1_ref_clk"));
|
||||
|
||||
sysbus_create_varargs("cadence_ttc", 0xF8001000,
|
||||
pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
|
||||
@ -308,8 +336,9 @@ static void zynq_init(MachineState *machine)
|
||||
arm_load_kernel(ARM_CPU(first_cpu), machine, &zynq_binfo);
|
||||
}
|
||||
|
||||
static void zynq_machine_init(MachineClass *mc)
|
||||
static void zynq_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
|
||||
mc->init = zynq_init;
|
||||
mc->max_cpus = 1;
|
||||
@ -319,4 +348,16 @@ static void zynq_machine_init(MachineClass *mc)
|
||||
mc->default_ram_id = "zynq.ext_ram";
|
||||
}
|
||||
|
||||
DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init)
|
||||
static const TypeInfo zynq_machine_type = {
|
||||
.name = TYPE_ZYNQ_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.class_init = zynq_machine_class_init,
|
||||
.instance_size = sizeof(ZynqMachineState),
|
||||
};
|
||||
|
||||
static void zynq_machine_register_types(void)
|
||||
{
|
||||
type_register_static(&zynq_machine_type);
|
||||
}
|
||||
|
||||
type_init(zynq_machine_register_types)
|
||||
|
@ -205,6 +205,8 @@ static void versal_create_admas(Versal *s, qemu_irq *pic)
|
||||
|
||||
dev = qdev_create(NULL, "xlnx.zdma");
|
||||
s->lpd.iou.adma[i] = SYS_BUS_DEVICE(dev);
|
||||
object_property_set_int(OBJECT(s->lpd.iou.adma[i]), 128, "bus-width",
|
||||
&error_abort);
|
||||
object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal);
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/log.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
|
||||
typedef struct XlnxZCU102 {
|
||||
MachineState parent_obj;
|
||||
@ -31,13 +32,14 @@ typedef struct XlnxZCU102 {
|
||||
|
||||
bool secure;
|
||||
bool virt;
|
||||
|
||||
struct arm_boot_info binfo;
|
||||
} XlnxZCU102;
|
||||
|
||||
#define TYPE_ZCU102_MACHINE MACHINE_TYPE_NAME("xlnx-zcu102")
|
||||
#define ZCU102_MACHINE(obj) \
|
||||
OBJECT_CHECK(XlnxZCU102, (obj), TYPE_ZCU102_MACHINE)
|
||||
|
||||
static struct arm_boot_info xlnx_zcu102_binfo;
|
||||
|
||||
static bool zcu102_get_secure(Object *obj, Error **errp)
|
||||
{
|
||||
@ -67,6 +69,34 @@ static void zcu102_set_virt(Object *obj, bool value, Error **errp)
|
||||
s->virt = value;
|
||||
}
|
||||
|
||||
static void zcu102_modify_dtb(const struct arm_boot_info *binfo, void *fdt)
|
||||
{
|
||||
XlnxZCU102 *s = container_of(binfo, XlnxZCU102, binfo);
|
||||
bool method_is_hvc;
|
||||
char **node_path;
|
||||
const char *r;
|
||||
int prop_len;
|
||||
int i;
|
||||
|
||||
/* If EL3 is enabled, we keep all firmware nodes active. */
|
||||
if (!s->secure) {
|
||||
node_path = qemu_fdt_node_path(fdt, NULL, "xlnx,zynqmp-firmware",
|
||||
&error_fatal);
|
||||
|
||||
for (i = 0; node_path && node_path[i]; i++) {
|
||||
r = qemu_fdt_getprop(fdt, node_path[i], "method", &prop_len, NULL);
|
||||
method_is_hvc = r && !strcmp("hvc", r);
|
||||
|
||||
/* Allow HVC based firmware if EL2 is enabled. */
|
||||
if (method_is_hvc && s->virt) {
|
||||
continue;
|
||||
}
|
||||
qemu_fdt_setprop_string(fdt, node_path[i], "status", "disabled");
|
||||
}
|
||||
g_strfreev(node_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void xlnx_zcu102_init(MachineState *machine)
|
||||
{
|
||||
XlnxZCU102 *s = ZCU102_MACHINE(machine);
|
||||
@ -166,9 +196,10 @@ static void xlnx_zcu102_init(MachineState *machine)
|
||||
|
||||
/* TODO create and connect IDE devices for ide_drive_get() */
|
||||
|
||||
xlnx_zcu102_binfo.ram_size = ram_size;
|
||||
xlnx_zcu102_binfo.loader_start = 0;
|
||||
arm_load_kernel(s->soc.boot_cpu_ptr, machine, &xlnx_zcu102_binfo);
|
||||
s->binfo.ram_size = ram_size;
|
||||
s->binfo.loader_start = 0;
|
||||
s->binfo.modify_dtb = zcu102_modify_dtb;
|
||||
arm_load_kernel(s->soc.boot_cpu_ptr, machine, &s->binfo);
|
||||
}
|
||||
|
||||
static void xlnx_zcu102_machine_instance_init(Object *obj)
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "qemu/module.h"
|
||||
#include "hw/char/cadence_uart.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "trace.h"
|
||||
|
||||
#ifdef CADENCE_UART_ERR_DEBUG
|
||||
#define DB_PRINT(...) do { \
|
||||
@ -97,7 +99,7 @@
|
||||
#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
|
||||
#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
|
||||
|
||||
#define UART_INPUT_CLK 50000000
|
||||
#define UART_DEFAULT_REF_CLK (50 * 1000 * 1000)
|
||||
|
||||
#define R_CR (0x00/4)
|
||||
#define R_MR (0x04/4)
|
||||
@ -171,12 +173,15 @@ static void uart_send_breaks(CadenceUARTState *s)
|
||||
static void uart_parameters_setup(CadenceUARTState *s)
|
||||
{
|
||||
QEMUSerialSetParams ssp;
|
||||
unsigned int baud_rate, packet_size;
|
||||
unsigned int baud_rate, packet_size, input_clk;
|
||||
input_clk = clock_get_hz(s->refclk);
|
||||
|
||||
baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
|
||||
UART_INPUT_CLK / 8 : UART_INPUT_CLK;
|
||||
baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? input_clk / 8 : input_clk;
|
||||
baud_rate /= (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
|
||||
trace_cadence_uart_baudrate(baud_rate);
|
||||
|
||||
ssp.speed = baud_rate;
|
||||
|
||||
ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
|
||||
packet_size = 1;
|
||||
|
||||
switch (s->r[R_MR] & UART_MR_PAR) {
|
||||
@ -215,6 +220,13 @@ static void uart_parameters_setup(CadenceUARTState *s)
|
||||
}
|
||||
|
||||
packet_size += ssp.data_bits + ssp.stop_bits;
|
||||
if (ssp.speed == 0) {
|
||||
/*
|
||||
* Avoid division-by-zero below.
|
||||
* TODO: find something better
|
||||
*/
|
||||
ssp.speed = 1;
|
||||
}
|
||||
s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size;
|
||||
qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
|
||||
}
|
||||
@ -340,6 +352,11 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size)
|
||||
CadenceUARTState *s = opaque;
|
||||
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
|
||||
|
||||
/* ignore characters when unclocked or in reset */
|
||||
if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
|
||||
uart_write_rx_fifo(opaque, buf, size);
|
||||
}
|
||||
@ -353,6 +370,11 @@ static void uart_event(void *opaque, QEMUChrEvent event)
|
||||
CadenceUARTState *s = opaque;
|
||||
uint8_t buf = '\0';
|
||||
|
||||
/* ignore characters when unclocked or in reset */
|
||||
if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event == CHR_EVENT_BREAK) {
|
||||
uart_write_rx_fifo(opaque, &buf, 1);
|
||||
}
|
||||
@ -462,9 +484,9 @@ static const MemoryRegionOps uart_ops = {
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void cadence_uart_reset(DeviceState *dev)
|
||||
static void cadence_uart_reset_init(Object *obj, ResetType type)
|
||||
{
|
||||
CadenceUARTState *s = CADENCE_UART(dev);
|
||||
CadenceUARTState *s = CADENCE_UART(obj);
|
||||
|
||||
s->r[R_CR] = 0x00000128;
|
||||
s->r[R_IMR] = 0;
|
||||
@ -473,6 +495,11 @@ static void cadence_uart_reset(DeviceState *dev)
|
||||
s->r[R_BRGR] = 0x0000028B;
|
||||
s->r[R_BDIV] = 0x0000000F;
|
||||
s->r[R_TTRIG] = 0x00000020;
|
||||
}
|
||||
|
||||
static void cadence_uart_reset_hold(Object *obj)
|
||||
{
|
||||
CadenceUARTState *s = CADENCE_UART(obj);
|
||||
|
||||
uart_rx_reset(s);
|
||||
uart_tx_reset(s);
|
||||
@ -491,6 +518,14 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
|
||||
uart_event, NULL, s, NULL, true);
|
||||
}
|
||||
|
||||
static void cadence_uart_refclk_update(void *opaque)
|
||||
{
|
||||
CadenceUARTState *s = opaque;
|
||||
|
||||
/* recompute uart's speed on clock change */
|
||||
uart_parameters_setup(s);
|
||||
}
|
||||
|
||||
static void cadence_uart_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
@ -500,9 +535,23 @@ static void cadence_uart_init(Object *obj)
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
|
||||
cadence_uart_refclk_update, s);
|
||||
/* initialize the frequency in case the clock remains unconnected */
|
||||
clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
|
||||
|
||||
s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10;
|
||||
}
|
||||
|
||||
static int cadence_uart_pre_load(void *opaque)
|
||||
{
|
||||
CadenceUARTState *s = opaque;
|
||||
|
||||
/* the frequency will be overriden if the refclk field is present */
|
||||
clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cadence_uart_post_load(void *opaque, int version_id)
|
||||
{
|
||||
CadenceUARTState *s = opaque;
|
||||
@ -521,8 +570,9 @@ static int cadence_uart_post_load(void *opaque, int version_id)
|
||||
|
||||
static const VMStateDescription vmstate_cadence_uart = {
|
||||
.name = "cadence_uart",
|
||||
.version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 2,
|
||||
.pre_load = cadence_uart_pre_load,
|
||||
.post_load = cadence_uart_post_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX),
|
||||
@ -534,8 +584,9 @@ static const VMStateDescription vmstate_cadence_uart = {
|
||||
VMSTATE_UINT32(tx_count, CadenceUARTState),
|
||||
VMSTATE_UINT32(rx_wpos, CadenceUARTState),
|
||||
VMSTATE_TIMER_PTR(fifo_trigger_handle, CadenceUARTState),
|
||||
VMSTATE_CLOCK_V(refclk, CadenceUARTState, 3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static Property cadence_uart_properties[] = {
|
||||
@ -546,10 +597,12 @@ static Property cadence_uart_properties[] = {
|
||||
static void cadence_uart_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
|
||||
dc->realize = cadence_uart_realize;
|
||||
dc->vmsd = &vmstate_cadence_uart;
|
||||
dc->reset = cadence_uart_reset;
|
||||
rc->phases.enter = cadence_uart_reset_init;
|
||||
rc->phases.hold = cadence_uart_reset_hold;
|
||||
device_class_set_props(dc, cadence_uart_properties);
|
||||
}
|
||||
|
||||
|
@ -97,3 +97,6 @@ exynos_uart_wo_read(uint32_t channel, const char *name, uint32_t reg) "UART%d: T
|
||||
exynos_uart_rxsize(uint32_t channel, uint32_t size) "UART%d: Rx FIFO size: %d"
|
||||
exynos_uart_channel_error(uint32_t channel) "Wrong UART channel number: %d"
|
||||
exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) "UART%d: Rx timeout stat=0x%x intsp=0x%x"
|
||||
|
||||
# hw/char/cadence_uart.c
|
||||
cadence_uart_baudrate(unsigned baudrate) "baudrate %u"
|
||||
|
@ -7,6 +7,7 @@ common-obj-y += hotplug.o
|
||||
common-obj-y += vmstate-if.o
|
||||
# irq.o needed for qdev GPIO handling:
|
||||
common-obj-y += irq.o
|
||||
common-obj-y += clock.o qdev-clock.o
|
||||
|
||||
common-obj-$(CONFIG_SOFTMMU) += reset.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o
|
||||
@ -20,6 +21,7 @@ common-obj-$(CONFIG_SOFTMMU) += null-machine.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += loader.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += machine-hmp-cmds.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += numa.o
|
||||
common-obj-$(CONFIG_SOFTMMU) += clock-vmstate.o
|
||||
obj-$(CONFIG_SOFTMMU) += machine-qmp-cmds.o
|
||||
|
||||
common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
|
||||
|
25
hw/core/clock-vmstate.c
Normal file
25
hw/core/clock-vmstate.c
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Clock migration structure
|
||||
*
|
||||
* Copyright GreenSocs 2019-2020
|
||||
*
|
||||
* Authors:
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
const VMStateDescription vmstate_clock = {
|
||||
.name = "clock",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT64(period, Clock),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
130
hw/core/clock.c
Normal file
130
hw/core/clock.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Hardware Clocks
|
||||
*
|
||||
* Copyright GreenSocs 2016-2020
|
||||
*
|
||||
* Authors:
|
||||
* Frederic Konrad
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/clock.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define CLOCK_PATH(_clk) (_clk->canonical_path)
|
||||
|
||||
void clock_setup_canonical_path(Clock *clk)
|
||||
{
|
||||
g_free(clk->canonical_path);
|
||||
clk->canonical_path = object_get_canonical_path(OBJECT(clk));
|
||||
}
|
||||
|
||||
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
|
||||
{
|
||||
clk->callback = cb;
|
||||
clk->callback_opaque = opaque;
|
||||
}
|
||||
|
||||
void clock_clear_callback(Clock *clk)
|
||||
{
|
||||
clock_set_callback(clk, NULL, NULL);
|
||||
}
|
||||
|
||||
void clock_set(Clock *clk, uint64_t period)
|
||||
{
|
||||
trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period),
|
||||
CLOCK_PERIOD_TO_NS(period));
|
||||
clk->period = period;
|
||||
}
|
||||
|
||||
static void clock_propagate_period(Clock *clk, bool call_callbacks)
|
||||
{
|
||||
Clock *child;
|
||||
|
||||
QLIST_FOREACH(child, &clk->children, sibling) {
|
||||
if (child->period != clk->period) {
|
||||
child->period = clk->period;
|
||||
trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
|
||||
CLOCK_PERIOD_TO_NS(clk->period),
|
||||
call_callbacks);
|
||||
if (call_callbacks && child->callback) {
|
||||
child->callback(child->callback_opaque);
|
||||
}
|
||||
clock_propagate_period(child, call_callbacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clock_propagate(Clock *clk)
|
||||
{
|
||||
assert(clk->source == NULL);
|
||||
trace_clock_propagate(CLOCK_PATH(clk));
|
||||
clock_propagate_period(clk, true);
|
||||
}
|
||||
|
||||
void clock_set_source(Clock *clk, Clock *src)
|
||||
{
|
||||
/* changing clock source is not supported */
|
||||
assert(!clk->source);
|
||||
|
||||
trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src));
|
||||
|
||||
clk->period = src->period;
|
||||
QLIST_INSERT_HEAD(&src->children, clk, sibling);
|
||||
clk->source = src;
|
||||
clock_propagate_period(clk, false);
|
||||
}
|
||||
|
||||
static void clock_disconnect(Clock *clk)
|
||||
{
|
||||
if (clk->source == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace_clock_disconnect(CLOCK_PATH(clk));
|
||||
|
||||
clk->source = NULL;
|
||||
QLIST_REMOVE(clk, sibling);
|
||||
}
|
||||
|
||||
static void clock_initfn(Object *obj)
|
||||
{
|
||||
Clock *clk = CLOCK(obj);
|
||||
|
||||
QLIST_INIT(&clk->children);
|
||||
}
|
||||
|
||||
static void clock_finalizefn(Object *obj)
|
||||
{
|
||||
Clock *clk = CLOCK(obj);
|
||||
Clock *child, *next;
|
||||
|
||||
/* clear our list of children */
|
||||
QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) {
|
||||
clock_disconnect(child);
|
||||
}
|
||||
|
||||
/* remove us from source's children list */
|
||||
clock_disconnect(clk);
|
||||
|
||||
g_free(clk->canonical_path);
|
||||
}
|
||||
|
||||
static const TypeInfo clock_info = {
|
||||
.name = TYPE_CLOCK,
|
||||
.parent = TYPE_OBJECT,
|
||||
.instance_size = sizeof(Clock),
|
||||
.instance_init = clock_initfn,
|
||||
.instance_finalize = clock_finalizefn,
|
||||
};
|
||||
|
||||
static void clock_register_types(void)
|
||||
{
|
||||
type_register_static(&clock_info);
|
||||
}
|
||||
|
||||
type_init(clock_register_types)
|
185
hw/core/qdev-clock.c
Normal file
185
hw/core/qdev-clock.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Device's clock input and output
|
||||
*
|
||||
* Copyright GreenSocs 2016-2020
|
||||
*
|
||||
* Authors:
|
||||
* Frederic Konrad
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
/*
|
||||
* qdev_init_clocklist:
|
||||
* Add a new clock in a device
|
||||
*/
|
||||
static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
|
||||
bool output, Clock *clk)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
/*
|
||||
* Clock must be added before realize() so that we can compute the
|
||||
* clock's canonical path during device_realize().
|
||||
*/
|
||||
assert(!dev->realized);
|
||||
|
||||
/*
|
||||
* The ncl structure is freed by qdev_finalize_clocklist() which will
|
||||
* be called during @dev's device_finalize().
|
||||
*/
|
||||
ncl = g_new0(NamedClockList, 1);
|
||||
ncl->name = g_strdup(name);
|
||||
ncl->output = output;
|
||||
ncl->alias = (clk != NULL);
|
||||
|
||||
/*
|
||||
* Trying to create a clock whose name clashes with some other
|
||||
* clock or property is a bug in the caller and we will abort().
|
||||
*/
|
||||
if (clk == NULL) {
|
||||
clk = CLOCK(object_new(TYPE_CLOCK));
|
||||
object_property_add_child(OBJECT(dev), name, OBJECT(clk), &error_abort);
|
||||
if (output) {
|
||||
/*
|
||||
* Remove object_new()'s initial reference.
|
||||
* Note that for inputs, the reference created by object_new()
|
||||
* will be deleted in qdev_finalize_clocklist().
|
||||
*/
|
||||
object_unref(OBJECT(clk));
|
||||
}
|
||||
} else {
|
||||
object_property_add_link(OBJECT(dev), name,
|
||||
object_get_typename(OBJECT(clk)),
|
||||
(Object **) &ncl->clock,
|
||||
NULL, OBJ_PROP_LINK_STRONG, &error_abort);
|
||||
}
|
||||
|
||||
ncl->clock = clk;
|
||||
|
||||
QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
|
||||
return ncl;
|
||||
}
|
||||
|
||||
void qdev_finalize_clocklist(DeviceState *dev)
|
||||
{
|
||||
/* called by @dev's device_finalize() */
|
||||
NamedClockList *ncl, *ncl_next;
|
||||
|
||||
QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
|
||||
QLIST_REMOVE(ncl, node);
|
||||
if (!ncl->output && !ncl->alias) {
|
||||
/*
|
||||
* We kept a reference on the input clock to ensure it lives up to
|
||||
* this point so we can safely remove the callback.
|
||||
* It avoids having a callback to a deleted object if ncl->clock
|
||||
* is still referenced somewhere else (eg: by a clock output).
|
||||
*/
|
||||
clock_clear_callback(ncl->clock);
|
||||
object_unref(OBJECT(ncl->clock));
|
||||
}
|
||||
g_free(ncl->name);
|
||||
g_free(ncl);
|
||||
}
|
||||
}
|
||||
|
||||
Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
assert(name);
|
||||
|
||||
ncl = qdev_init_clocklist(dev, name, true, NULL);
|
||||
|
||||
return ncl->clock;
|
||||
}
|
||||
|
||||
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
|
||||
ClockCallback *callback, void *opaque)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
assert(name);
|
||||
|
||||
ncl = qdev_init_clocklist(dev, name, false, NULL);
|
||||
|
||||
if (callback) {
|
||||
clock_set_callback(ncl->clock, callback, opaque);
|
||||
}
|
||||
return ncl->clock;
|
||||
}
|
||||
|
||||
void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
|
||||
{
|
||||
const struct ClockPortInitElem *elem;
|
||||
|
||||
for (elem = &clocks[0]; elem->name != NULL; elem++) {
|
||||
Clock **clkp;
|
||||
/* offset cannot be inside the DeviceState part */
|
||||
assert(elem->offset > sizeof(DeviceState));
|
||||
clkp = (Clock **)(((void *) dev) + elem->offset);
|
||||
if (elem->is_output) {
|
||||
*clkp = qdev_init_clock_out(dev, elem->name);
|
||||
} else {
|
||||
*clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
QLIST_FOREACH(ncl, &dev->clocks, node) {
|
||||
if (strcmp(name, ncl->name) == 0) {
|
||||
return ncl;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
assert(name);
|
||||
|
||||
ncl = qdev_get_clocklist(dev, name);
|
||||
assert(!ncl->output);
|
||||
|
||||
return ncl->clock;
|
||||
}
|
||||
|
||||
Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
assert(name);
|
||||
|
||||
ncl = qdev_get_clocklist(dev, name);
|
||||
assert(ncl->output);
|
||||
|
||||
return ncl->clock;
|
||||
}
|
||||
|
||||
Clock *qdev_alias_clock(DeviceState *dev, const char *name,
|
||||
DeviceState *alias_dev, const char *alias_name)
|
||||
{
|
||||
NamedClockList *ncl;
|
||||
|
||||
assert(name && alias_name);
|
||||
|
||||
ncl = qdev_get_clocklist(dev, name);
|
||||
|
||||
qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock);
|
||||
|
||||
return ncl->clock;
|
||||
}
|
@ -37,6 +37,7 @@
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
@ -855,6 +856,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(dev);
|
||||
HotplugHandler *hotplug_ctrl;
|
||||
BusState *bus;
|
||||
NamedClockList *ncl;
|
||||
Error *local_err = NULL;
|
||||
bool unattached_parent = false;
|
||||
static int unattached_count;
|
||||
@ -902,6 +904,13 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||
*/
|
||||
g_free(dev->canonical_path);
|
||||
dev->canonical_path = object_get_canonical_path(OBJECT(dev));
|
||||
QLIST_FOREACH(ncl, &dev->clocks, node) {
|
||||
if (ncl->alias) {
|
||||
continue;
|
||||
} else {
|
||||
clock_setup_canonical_path(ncl->clock);
|
||||
}
|
||||
}
|
||||
|
||||
if (qdev_get_vmsd(dev)) {
|
||||
if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
|
||||
@ -1025,6 +1034,7 @@ static void device_initfn(Object *obj)
|
||||
dev->allow_unplug_during_migration = false;
|
||||
|
||||
QLIST_INIT(&dev->gpios);
|
||||
QLIST_INIT(&dev->clocks);
|
||||
}
|
||||
|
||||
static void device_post_init(Object *obj)
|
||||
@ -1054,6 +1064,8 @@ static void device_finalize(Object *obj)
|
||||
*/
|
||||
}
|
||||
|
||||
qdev_finalize_clocklist(dev);
|
||||
|
||||
/* Only send event if the device had been completely realized */
|
||||
if (dev->pending_deleted_event) {
|
||||
g_assert(dev->canonical_path);
|
||||
|
@ -27,3 +27,10 @@ resettable_phase_exit_begin(void *obj, const char *objtype, unsigned count, int
|
||||
resettable_phase_exit_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d"
|
||||
resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d"
|
||||
resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
|
||||
# clock.c
|
||||
clock_set_source(const char *clk, const char *src) "'%s', src='%s'"
|
||||
clock_disconnect(const char *clk) "'%s'"
|
||||
clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', ns=%"PRIu64"->%"PRIu64
|
||||
clock_propagate(const char *clk) "'%s'"
|
||||
clock_update(const char *clk, const char *src, uint64_t val, int cb) "'%s', src='%s', ns=%"PRIu64", cb=%d"
|
||||
|
@ -299,19 +299,30 @@ static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr)
|
||||
s->regs[basereg + 1] = addr >> 32;
|
||||
}
|
||||
|
||||
static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
|
||||
static void zdma_load_descriptor_reg(XlnxZDMA *s, unsigned int reg,
|
||||
XlnxZDMADescr *descr)
|
||||
{
|
||||
descr->addr = zdma_get_regaddr64(s, reg);
|
||||
descr->size = s->regs[reg + 2];
|
||||
descr->attr = s->regs[reg + 3];
|
||||
}
|
||||
|
||||
static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr,
|
||||
XlnxZDMADescr *descr)
|
||||
{
|
||||
/* ZDMA descriptors must be aligned to their own size. */
|
||||
if (addr % sizeof(XlnxZDMADescr)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"zdma: unaligned descriptor at %" PRIx64,
|
||||
addr);
|
||||
memset(buf, 0x0, sizeof(XlnxZDMADescr));
|
||||
memset(descr, 0x0, sizeof(XlnxZDMADescr));
|
||||
s->error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
address_space_read(s->dma_as, addr, s->attr, buf, sizeof(XlnxZDMADescr));
|
||||
descr->addr = address_space_ldq_le(s->dma_as, addr, s->attr, NULL);
|
||||
descr->size = address_space_ldl_le(s->dma_as, addr + 8, s->attr, NULL);
|
||||
descr->attr = address_space_ldl_le(s->dma_as, addr + 12, s->attr, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -321,8 +332,7 @@ static void zdma_load_src_descriptor(XlnxZDMA *s)
|
||||
unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
|
||||
|
||||
if (ptype == PT_REG) {
|
||||
memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
|
||||
sizeof(s->dsc_src));
|
||||
zdma_load_descriptor_reg(s, R_ZDMA_CH_SRC_DSCR_WORD0, &s->dsc_src);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -344,7 +354,7 @@ static void zdma_update_descr_addr(XlnxZDMA *s, bool type,
|
||||
} else {
|
||||
addr = zdma_get_regaddr64(s, basereg);
|
||||
addr += sizeof(s->dsc_dst);
|
||||
address_space_read(s->dma_as, addr, s->attr, (void *) &next, 8);
|
||||
next = address_space_ldq_le(s->dma_as, addr, s->attr, NULL);
|
||||
}
|
||||
|
||||
zdma_put_regaddr64(s, basereg, next);
|
||||
@ -357,8 +367,7 @@ static void zdma_load_dst_descriptor(XlnxZDMA *s)
|
||||
bool dst_type;
|
||||
|
||||
if (ptype == PT_REG) {
|
||||
memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
|
||||
sizeof(s->dsc_dst));
|
||||
zdma_load_descriptor_reg(s, R_ZDMA_CH_DST_DSCR_WORD0, &s->dsc_dst);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -658,13 +658,11 @@ static void kvm_arm_gicv3_get(GICv3State *s)
|
||||
|
||||
static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
ARMCPU *cpu;
|
||||
GICv3State *s;
|
||||
GICv3CPUState *c;
|
||||
|
||||
c = (GICv3CPUState *)env->gicv3state;
|
||||
s = c->gic;
|
||||
cpu = ARM_CPU(c->cpu);
|
||||
|
||||
c->icc_pmr_el1 = 0;
|
||||
c->icc_bpr[GICV3_G0] = GIC_MIN_BPR;
|
||||
@ -681,7 +679,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
|
||||
/* Initialize to actual HW supported configuration */
|
||||
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
|
||||
KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity),
|
||||
KVM_VGIC_ATTR(ICC_CTLR_EL1, c->gicr_typer),
|
||||
&c->icc_ctlr_el1[GICV3_NS], false, &error_abort);
|
||||
|
||||
c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS];
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-clock.h"
|
||||
|
||||
#ifndef ZYNQ_SLCR_ERR_DEBUG
|
||||
#define ZYNQ_SLCR_ERR_DEBUG 0
|
||||
@ -45,6 +46,12 @@ REG32(LOCKSTA, 0x00c)
|
||||
REG32(ARM_PLL_CTRL, 0x100)
|
||||
REG32(DDR_PLL_CTRL, 0x104)
|
||||
REG32(IO_PLL_CTRL, 0x108)
|
||||
/* fields for [ARM|DDR|IO]_PLL_CTRL registers */
|
||||
FIELD(xxx_PLL_CTRL, PLL_RESET, 0, 1)
|
||||
FIELD(xxx_PLL_CTRL, PLL_PWRDWN, 1, 1)
|
||||
FIELD(xxx_PLL_CTRL, PLL_BYPASS_QUAL, 3, 1)
|
||||
FIELD(xxx_PLL_CTRL, PLL_BYPASS_FORCE, 4, 1)
|
||||
FIELD(xxx_PLL_CTRL, PLL_FPDIV, 12, 7)
|
||||
REG32(PLL_STATUS, 0x10c)
|
||||
REG32(ARM_PLL_CFG, 0x110)
|
||||
REG32(DDR_PLL_CFG, 0x114)
|
||||
@ -64,6 +71,10 @@ REG32(SMC_CLK_CTRL, 0x148)
|
||||
REG32(LQSPI_CLK_CTRL, 0x14c)
|
||||
REG32(SDIO_CLK_CTRL, 0x150)
|
||||
REG32(UART_CLK_CTRL, 0x154)
|
||||
FIELD(UART_CLK_CTRL, CLKACT0, 0, 1)
|
||||
FIELD(UART_CLK_CTRL, CLKACT1, 1, 1)
|
||||
FIELD(UART_CLK_CTRL, SRCSEL, 4, 2)
|
||||
FIELD(UART_CLK_CTRL, DIVISOR, 8, 6)
|
||||
REG32(SPI_CLK_CTRL, 0x158)
|
||||
REG32(CAN_CLK_CTRL, 0x15c)
|
||||
REG32(CAN_MIOCLK_CTRL, 0x160)
|
||||
@ -179,11 +190,127 @@ typedef struct ZynqSLCRState {
|
||||
MemoryRegion iomem;
|
||||
|
||||
uint32_t regs[ZYNQ_SLCR_NUM_REGS];
|
||||
|
||||
Clock *ps_clk;
|
||||
Clock *uart0_ref_clk;
|
||||
Clock *uart1_ref_clk;
|
||||
} ZynqSLCRState;
|
||||
|
||||
static void zynq_slcr_reset(DeviceState *d)
|
||||
/*
|
||||
* return the output frequency of ARM/DDR/IO pll
|
||||
* using input frequency and PLL_CTRL register
|
||||
*/
|
||||
static uint64_t zynq_slcr_compute_pll(uint64_t input, uint32_t ctrl_reg)
|
||||
{
|
||||
ZynqSLCRState *s = ZYNQ_SLCR(d);
|
||||
uint32_t mult = ((ctrl_reg & R_xxx_PLL_CTRL_PLL_FPDIV_MASK) >>
|
||||
R_xxx_PLL_CTRL_PLL_FPDIV_SHIFT);
|
||||
|
||||
/* first, check if pll is bypassed */
|
||||
if (ctrl_reg & R_xxx_PLL_CTRL_PLL_BYPASS_FORCE_MASK) {
|
||||
return input;
|
||||
}
|
||||
|
||||
/* is pll disabled ? */
|
||||
if (ctrl_reg & (R_xxx_PLL_CTRL_PLL_RESET_MASK |
|
||||
R_xxx_PLL_CTRL_PLL_PWRDWN_MASK)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* frequency multiplier -> period division */
|
||||
return input / mult;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the output period of a clock given:
|
||||
* + the periods in an array corresponding to input mux selector
|
||||
* + the register xxx_CLK_CTRL value
|
||||
* + enable bit index in ctrl register
|
||||
*
|
||||
* This function makes the assumption that the ctrl_reg value is organized as
|
||||
* follows:
|
||||
* + bits[13:8] clock frequency divisor
|
||||
* + bits[5:4] clock mux selector (index in array)
|
||||
* + bits[index] clock enable
|
||||
*/
|
||||
static uint64_t zynq_slcr_compute_clock(const uint64_t periods[],
|
||||
uint32_t ctrl_reg,
|
||||
unsigned index)
|
||||
{
|
||||
uint32_t srcsel = extract32(ctrl_reg, 4, 2); /* bits [5:4] */
|
||||
uint32_t divisor = extract32(ctrl_reg, 8, 6); /* bits [13:8] */
|
||||
|
||||
/* first, check if clock is disabled */
|
||||
if (((ctrl_reg >> index) & 1u) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* according to the Zynq technical ref. manual UG585 v1.12.2 in
|
||||
* Clocks chapter, section 25.10.1 page 705:
|
||||
* "The 6-bit divider provides a divide range of 1 to 63"
|
||||
* We follow here what is implemented in linux kernel and consider
|
||||
* the 0 value as a bypass (no division).
|
||||
*/
|
||||
/* frequency divisor -> period multiplication */
|
||||
return periods[srcsel] * (divisor ? divisor : 1u);
|
||||
}
|
||||
|
||||
/*
|
||||
* macro helper around zynq_slcr_compute_clock to avoid repeating
|
||||
* the register name.
|
||||
*/
|
||||
#define ZYNQ_COMPUTE_CLK(state, plls, reg, enable_field) \
|
||||
zynq_slcr_compute_clock((plls), (state)->regs[reg], \
|
||||
reg ## _ ## enable_field ## _SHIFT)
|
||||
|
||||
/**
|
||||
* Compute and set the ouputs clocks periods.
|
||||
* But do not propagate them further. Connected clocks
|
||||
* will not receive any updates (See zynq_slcr_compute_clocks())
|
||||
*/
|
||||
static void zynq_slcr_compute_clocks(ZynqSLCRState *s)
|
||||
{
|
||||
uint64_t ps_clk = clock_get(s->ps_clk);
|
||||
|
||||
/* consider outputs clocks are disabled while in reset */
|
||||
if (device_is_in_reset(DEVICE(s))) {
|
||||
ps_clk = 0;
|
||||
}
|
||||
|
||||
uint64_t io_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_IO_PLL_CTRL]);
|
||||
uint64_t arm_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_ARM_PLL_CTRL]);
|
||||
uint64_t ddr_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_DDR_PLL_CTRL]);
|
||||
|
||||
uint64_t uart_mux[4] = {io_pll, io_pll, arm_pll, ddr_pll};
|
||||
|
||||
/* compute uartX reference clocks */
|
||||
clock_set(s->uart0_ref_clk,
|
||||
ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT0));
|
||||
clock_set(s->uart1_ref_clk,
|
||||
ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate the outputs clocks.
|
||||
* zynq_slcr_compute_clocks() should have been called before
|
||||
* to configure them.
|
||||
*/
|
||||
static void zynq_slcr_propagate_clocks(ZynqSLCRState *s)
|
||||
{
|
||||
clock_propagate(s->uart0_ref_clk);
|
||||
clock_propagate(s->uart1_ref_clk);
|
||||
}
|
||||
|
||||
static void zynq_slcr_ps_clk_callback(void *opaque)
|
||||
{
|
||||
ZynqSLCRState *s = (ZynqSLCRState *) opaque;
|
||||
zynq_slcr_compute_clocks(s);
|
||||
zynq_slcr_propagate_clocks(s);
|
||||
}
|
||||
|
||||
static void zynq_slcr_reset_init(Object *obj, ResetType type)
|
||||
{
|
||||
ZynqSLCRState *s = ZYNQ_SLCR(obj);
|
||||
int i;
|
||||
|
||||
DB_PRINT("RESET\n");
|
||||
@ -277,6 +404,23 @@ static void zynq_slcr_reset(DeviceState *d)
|
||||
s->regs[R_DDRIOB + 12] = 0x00000021;
|
||||
}
|
||||
|
||||
static void zynq_slcr_reset_hold(Object *obj)
|
||||
{
|
||||
ZynqSLCRState *s = ZYNQ_SLCR(obj);
|
||||
|
||||
/* will disable all output clocks */
|
||||
zynq_slcr_compute_clocks(s);
|
||||
zynq_slcr_propagate_clocks(s);
|
||||
}
|
||||
|
||||
static void zynq_slcr_reset_exit(Object *obj)
|
||||
{
|
||||
ZynqSLCRState *s = ZYNQ_SLCR(obj);
|
||||
|
||||
/* will compute output clocks according to ps_clk and registers */
|
||||
zynq_slcr_compute_clocks(s);
|
||||
zynq_slcr_propagate_clocks(s);
|
||||
}
|
||||
|
||||
static bool zynq_slcr_check_offset(hwaddr offset, bool rnw)
|
||||
{
|
||||
@ -409,6 +553,13 @@ static void zynq_slcr_write(void *opaque, hwaddr offset,
|
||||
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
|
||||
}
|
||||
break;
|
||||
case R_IO_PLL_CTRL:
|
||||
case R_ARM_PLL_CTRL:
|
||||
case R_DDR_PLL_CTRL:
|
||||
case R_UART_CLK_CTRL:
|
||||
zynq_slcr_compute_clocks(s);
|
||||
zynq_slcr_propagate_clocks(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -418,6 +569,13 @@ static const MemoryRegionOps slcr_ops = {
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const ClockPortInitArray zynq_slcr_clocks = {
|
||||
QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback),
|
||||
QDEV_CLOCK_OUT(ZynqSLCRState, uart0_ref_clk),
|
||||
QDEV_CLOCK_OUT(ZynqSLCRState, uart1_ref_clk),
|
||||
QDEV_CLOCK_END
|
||||
};
|
||||
|
||||
static void zynq_slcr_init(Object *obj)
|
||||
{
|
||||
ZynqSLCRState *s = ZYNQ_SLCR(obj);
|
||||
@ -425,14 +583,17 @@ static void zynq_slcr_init(Object *obj)
|
||||
memory_region_init_io(&s->iomem, obj, &slcr_ops, s, "slcr",
|
||||
ZYNQ_SLCR_MMIO_SIZE);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
|
||||
|
||||
qdev_init_clocks(DEVICE(obj), zynq_slcr_clocks);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_zynq_slcr = {
|
||||
.name = "zynq_slcr",
|
||||
.version_id = 2,
|
||||
.version_id = 3,
|
||||
.minimum_version_id = 2,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32_ARRAY(regs, ZynqSLCRState, ZYNQ_SLCR_NUM_REGS),
|
||||
VMSTATE_CLOCK_V(ps_clk, ZynqSLCRState, 3),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
@ -440,9 +601,12 @@ static const VMStateDescription vmstate_zynq_slcr = {
|
||||
static void zynq_slcr_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_zynq_slcr;
|
||||
dc->reset = zynq_slcr_reset;
|
||||
rc->phases.enter = zynq_slcr_reset_init;
|
||||
rc->phases.hold = zynq_slcr_reset_hold;
|
||||
rc->phases.exit = zynq_slcr_reset_exit;
|
||||
}
|
||||
|
||||
static const TypeInfo zynq_slcr_info = {
|
||||
|
@ -55,3 +55,4 @@ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
|
||||
obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
|
||||
|
||||
common-obj-$(CONFIG_CAN_BUS) += can/
|
||||
common-obj-$(CONFIG_MSF2) += msf2-emac.o
|
||||
|
@ -411,6 +411,11 @@ static inline void rx_desc_set_sof(uint32_t *desc)
|
||||
desc[1] |= DESC_1_RX_SOF;
|
||||
}
|
||||
|
||||
static inline void rx_desc_clear_control(uint32_t *desc)
|
||||
{
|
||||
desc[1] = 0;
|
||||
}
|
||||
|
||||
static inline void rx_desc_set_eof(uint32_t *desc)
|
||||
{
|
||||
desc[1] |= DESC_1_RX_EOF;
|
||||
@ -999,6 +1004,8 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||
rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
|
||||
bytes_to_copy -= MIN(bytes_to_copy, rxbufsize);
|
||||
|
||||
rx_desc_clear_control(s->rx_desc[q]);
|
||||
|
||||
/* Update the descriptor. */
|
||||
if (first_desc) {
|
||||
rx_desc_set_sof(s->rx_desc[q]);
|
||||
@ -1238,7 +1245,14 @@ static void gem_transmit(CadenceGEMState *s)
|
||||
/* read next descriptor */
|
||||
if (tx_desc_get_wrap(desc)) {
|
||||
tx_desc_set_last(desc);
|
||||
packet_desc_addr = s->regs[GEM_TXQBASE];
|
||||
|
||||
if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) {
|
||||
packet_desc_addr = s->regs[GEM_TBQPH];
|
||||
packet_desc_addr <<= 32;
|
||||
} else {
|
||||
packet_desc_addr = 0;
|
||||
}
|
||||
packet_desc_addr |= s->regs[GEM_TXQBASE];
|
||||
} else {
|
||||
packet_desc_addr += 4 * gem_get_desc_len(s, false);
|
||||
}
|
||||
|
589
hw/net/msf2-emac.c
Normal file
589
hw/net/msf2-emac.c
Normal file
@ -0,0 +1,589 @@
|
||||
/*
|
||||
* QEMU model of the Smartfusion2 Ethernet MAC.
|
||||
*
|
||||
* Copyright (c) 2020 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* Refer to section Ethernet MAC in the document:
|
||||
* UG0331: SmartFusion2 Microcontroller Subsystem User Guide
|
||||
* Datasheet URL:
|
||||
* https://www.microsemi.com/document-portal/cat_view/56661-internal-documents/
|
||||
* 56758-soc?lang=en&limit=20&limitstart=220
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/net/msf2-emac.h"
|
||||
#include "hw/net/mii.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
REG32(CFG1, 0x0)
|
||||
FIELD(CFG1, RESET, 31, 1)
|
||||
FIELD(CFG1, RX_EN, 2, 1)
|
||||
FIELD(CFG1, TX_EN, 0, 1)
|
||||
FIELD(CFG1, LB_EN, 8, 1)
|
||||
REG32(CFG2, 0x4)
|
||||
REG32(IFG, 0x8)
|
||||
REG32(HALF_DUPLEX, 0xc)
|
||||
REG32(MAX_FRAME_LENGTH, 0x10)
|
||||
REG32(MII_CMD, 0x24)
|
||||
FIELD(MII_CMD, READ, 0, 1)
|
||||
REG32(MII_ADDR, 0x28)
|
||||
FIELD(MII_ADDR, REGADDR, 0, 5)
|
||||
FIELD(MII_ADDR, PHYADDR, 8, 5)
|
||||
REG32(MII_CTL, 0x2c)
|
||||
REG32(MII_STS, 0x30)
|
||||
REG32(STA1, 0x40)
|
||||
REG32(STA2, 0x44)
|
||||
REG32(FIFO_CFG0, 0x48)
|
||||
REG32(FIFO_CFG4, 0x58)
|
||||
FIELD(FIFO_CFG4, BCAST, 9, 1)
|
||||
FIELD(FIFO_CFG4, MCAST, 8, 1)
|
||||
REG32(FIFO_CFG5, 0x5C)
|
||||
FIELD(FIFO_CFG5, BCAST, 9, 1)
|
||||
FIELD(FIFO_CFG5, MCAST, 8, 1)
|
||||
REG32(DMA_TX_CTL, 0x180)
|
||||
FIELD(DMA_TX_CTL, EN, 0, 1)
|
||||
REG32(DMA_TX_DESC, 0x184)
|
||||
REG32(DMA_TX_STATUS, 0x188)
|
||||
FIELD(DMA_TX_STATUS, PKTCNT, 16, 8)
|
||||
FIELD(DMA_TX_STATUS, UNDERRUN, 1, 1)
|
||||
FIELD(DMA_TX_STATUS, PKT_SENT, 0, 1)
|
||||
REG32(DMA_RX_CTL, 0x18c)
|
||||
FIELD(DMA_RX_CTL, EN, 0, 1)
|
||||
REG32(DMA_RX_DESC, 0x190)
|
||||
REG32(DMA_RX_STATUS, 0x194)
|
||||
FIELD(DMA_RX_STATUS, PKTCNT, 16, 8)
|
||||
FIELD(DMA_RX_STATUS, OVERFLOW, 2, 1)
|
||||
FIELD(DMA_RX_STATUS, PKT_RCVD, 0, 1)
|
||||
REG32(DMA_IRQ_MASK, 0x198)
|
||||
REG32(DMA_IRQ, 0x19c)
|
||||
|
||||
#define EMPTY_MASK (1 << 31)
|
||||
#define PKT_SIZE 0x7FF
|
||||
#define PHYADDR 0x1
|
||||
#define MAX_PKT_SIZE 2048
|
||||
|
||||
typedef struct {
|
||||
uint32_t pktaddr;
|
||||
uint32_t pktsize;
|
||||
uint32_t next;
|
||||
} EmacDesc;
|
||||
|
||||
static uint32_t emac_get_isr(MSF2EmacState *s)
|
||||
{
|
||||
uint32_t ier = s->regs[R_DMA_IRQ_MASK];
|
||||
uint32_t tx = s->regs[R_DMA_TX_STATUS] & 0xF;
|
||||
uint32_t rx = s->regs[R_DMA_RX_STATUS] & 0xF;
|
||||
uint32_t isr = (rx << 4) | tx;
|
||||
|
||||
s->regs[R_DMA_IRQ] = ier & isr;
|
||||
return s->regs[R_DMA_IRQ];
|
||||
}
|
||||
|
||||
static void emac_update_irq(MSF2EmacState *s)
|
||||
{
|
||||
bool intr = emac_get_isr(s);
|
||||
|
||||
qemu_set_irq(s->irq, intr);
|
||||
}
|
||||
|
||||
static void emac_load_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc)
|
||||
{
|
||||
address_space_read(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, d, sizeof *d);
|
||||
/* Convert from LE into host endianness. */
|
||||
d->pktaddr = le32_to_cpu(d->pktaddr);
|
||||
d->pktsize = le32_to_cpu(d->pktsize);
|
||||
d->next = le32_to_cpu(d->next);
|
||||
}
|
||||
|
||||
static void emac_store_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc)
|
||||
{
|
||||
/* Convert from host endianness into LE. */
|
||||
d->pktaddr = cpu_to_le32(d->pktaddr);
|
||||
d->pktsize = cpu_to_le32(d->pktsize);
|
||||
d->next = cpu_to_le32(d->next);
|
||||
|
||||
address_space_write(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, d, sizeof *d);
|
||||
}
|
||||
|
||||
static void msf2_dma_tx(MSF2EmacState *s)
|
||||
{
|
||||
NetClientState *nc = qemu_get_queue(s->nic);
|
||||
hwaddr desc = s->regs[R_DMA_TX_DESC];
|
||||
uint8_t buf[MAX_PKT_SIZE];
|
||||
EmacDesc d;
|
||||
int size;
|
||||
uint8_t pktcnt;
|
||||
uint32_t status;
|
||||
|
||||
if (!(s->regs[R_CFG1] & R_CFG1_TX_EN_MASK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
emac_load_desc(s, &d, desc);
|
||||
if (d.pktsize & EMPTY_MASK) {
|
||||
break;
|
||||
}
|
||||
size = d.pktsize & PKT_SIZE;
|
||||
address_space_read(&s->dma_as, d.pktaddr, MEMTXATTRS_UNSPECIFIED,
|
||||
buf, size);
|
||||
/*
|
||||
* This is very basic way to send packets. Ideally there should be
|
||||
* a FIFO and packets should be sent out from FIFO only when
|
||||
* R_CFG1 bit 0 is set.
|
||||
*/
|
||||
if (s->regs[R_CFG1] & R_CFG1_LB_EN_MASK) {
|
||||
nc->info->receive(nc, buf, size);
|
||||
} else {
|
||||
qemu_send_packet(nc, buf, size);
|
||||
}
|
||||
d.pktsize |= EMPTY_MASK;
|
||||
emac_store_desc(s, &d, desc);
|
||||
/* update sent packets count */
|
||||
status = s->regs[R_DMA_TX_STATUS];
|
||||
pktcnt = FIELD_EX32(status, DMA_TX_STATUS, PKTCNT);
|
||||
pktcnt++;
|
||||
s->regs[R_DMA_TX_STATUS] = FIELD_DP32(status, DMA_TX_STATUS,
|
||||
PKTCNT, pktcnt);
|
||||
s->regs[R_DMA_TX_STATUS] |= R_DMA_TX_STATUS_PKT_SENT_MASK;
|
||||
desc = d.next;
|
||||
}
|
||||
s->regs[R_DMA_TX_STATUS] |= R_DMA_TX_STATUS_UNDERRUN_MASK;
|
||||
s->regs[R_DMA_TX_CTL] &= ~R_DMA_TX_CTL_EN_MASK;
|
||||
}
|
||||
|
||||
static void msf2_phy_update_link(MSF2EmacState *s)
|
||||
{
|
||||
/* Autonegotiation status mirrors link status. */
|
||||
if (qemu_get_queue(s->nic)->link_down) {
|
||||
s->phy_regs[MII_BMSR] &= ~(MII_BMSR_AN_COMP |
|
||||
MII_BMSR_LINK_ST);
|
||||
} else {
|
||||
s->phy_regs[MII_BMSR] |= (MII_BMSR_AN_COMP |
|
||||
MII_BMSR_LINK_ST);
|
||||
}
|
||||
}
|
||||
|
||||
static void msf2_phy_reset(MSF2EmacState *s)
|
||||
{
|
||||
memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
|
||||
s->phy_regs[MII_BMCR] = 0x1140;
|
||||
s->phy_regs[MII_BMSR] = 0x7968;
|
||||
s->phy_regs[MII_PHYID1] = 0x0022;
|
||||
s->phy_regs[MII_PHYID2] = 0x1550;
|
||||
s->phy_regs[MII_ANAR] = 0x01E1;
|
||||
s->phy_regs[MII_ANLPAR] = 0xCDE1;
|
||||
|
||||
msf2_phy_update_link(s);
|
||||
}
|
||||
|
||||
static void write_to_phy(MSF2EmacState *s)
|
||||
{
|
||||
uint8_t reg_addr = s->regs[R_MII_ADDR] & R_MII_ADDR_REGADDR_MASK;
|
||||
uint8_t phy_addr = (s->regs[R_MII_ADDR] >> R_MII_ADDR_PHYADDR_SHIFT) &
|
||||
R_MII_ADDR_REGADDR_MASK;
|
||||
uint16_t data = s->regs[R_MII_CTL] & 0xFFFF;
|
||||
|
||||
if (phy_addr != PHYADDR) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (reg_addr) {
|
||||
case MII_BMCR:
|
||||
if (data & MII_BMCR_RESET) {
|
||||
/* Phy reset */
|
||||
msf2_phy_reset(s);
|
||||
data &= ~MII_BMCR_RESET;
|
||||
}
|
||||
if (data & MII_BMCR_AUTOEN) {
|
||||
/* Complete autonegotiation immediately */
|
||||
data &= ~MII_BMCR_AUTOEN;
|
||||
s->phy_regs[MII_BMSR] |= MII_BMSR_AN_COMP;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
s->phy_regs[reg_addr] = data;
|
||||
}
|
||||
|
||||
static uint16_t read_from_phy(MSF2EmacState *s)
|
||||
{
|
||||
uint8_t reg_addr = s->regs[R_MII_ADDR] & R_MII_ADDR_REGADDR_MASK;
|
||||
uint8_t phy_addr = (s->regs[R_MII_ADDR] >> R_MII_ADDR_PHYADDR_SHIFT) &
|
||||
R_MII_ADDR_REGADDR_MASK;
|
||||
|
||||
if (phy_addr == PHYADDR) {
|
||||
return s->phy_regs[reg_addr];
|
||||
} else {
|
||||
return 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void msf2_emac_do_reset(MSF2EmacState *s)
|
||||
{
|
||||
memset(&s->regs[0], 0, sizeof(s->regs));
|
||||
s->regs[R_CFG1] = 0x80000000;
|
||||
s->regs[R_CFG2] = 0x00007000;
|
||||
s->regs[R_IFG] = 0x40605060;
|
||||
s->regs[R_HALF_DUPLEX] = 0x00A1F037;
|
||||
s->regs[R_MAX_FRAME_LENGTH] = 0x00000600;
|
||||
s->regs[R_FIFO_CFG5] = 0X3FFFF;
|
||||
|
||||
msf2_phy_reset(s);
|
||||
}
|
||||
|
||||
static uint64_t emac_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
MSF2EmacState *s = opaque;
|
||||
uint32_t r = 0;
|
||||
|
||||
addr >>= 2;
|
||||
|
||||
switch (addr) {
|
||||
case R_DMA_IRQ:
|
||||
r = emac_get_isr(s);
|
||||
break;
|
||||
default:
|
||||
if (addr >= ARRAY_SIZE(s->regs)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__,
|
||||
addr * 4);
|
||||
return r;
|
||||
}
|
||||
r = s->regs[addr];
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static void emac_write(void *opaque, hwaddr addr, uint64_t val64,
|
||||
unsigned int size)
|
||||
{
|
||||
MSF2EmacState *s = opaque;
|
||||
uint32_t value = val64;
|
||||
uint32_t enreqbits;
|
||||
uint8_t pktcnt;
|
||||
|
||||
addr >>= 2;
|
||||
switch (addr) {
|
||||
case R_DMA_TX_CTL:
|
||||
s->regs[addr] = value;
|
||||
if (value & R_DMA_TX_CTL_EN_MASK) {
|
||||
msf2_dma_tx(s);
|
||||
}
|
||||
break;
|
||||
case R_DMA_RX_CTL:
|
||||
s->regs[addr] = value;
|
||||
if (value & R_DMA_RX_CTL_EN_MASK) {
|
||||
s->rx_desc = s->regs[R_DMA_RX_DESC];
|
||||
qemu_flush_queued_packets(qemu_get_queue(s->nic));
|
||||
}
|
||||
break;
|
||||
case R_CFG1:
|
||||
s->regs[addr] = value;
|
||||
if (value & R_CFG1_RESET_MASK) {
|
||||
msf2_emac_do_reset(s);
|
||||
}
|
||||
break;
|
||||
case R_FIFO_CFG0:
|
||||
/*
|
||||
* For our implementation, turning on modules is instantaneous,
|
||||
* so the states requested via the *ENREQ bits appear in the
|
||||
* *ENRPLY bits immediately. Also the reset bits to reset PE-MCXMAC
|
||||
* module are not emulated here since it deals with start of frames,
|
||||
* inter-packet gap and control frames.
|
||||
*/
|
||||
enreqbits = extract32(value, 8, 5);
|
||||
s->regs[addr] = deposit32(value, 16, 5, enreqbits);
|
||||
break;
|
||||
case R_DMA_TX_DESC:
|
||||
if (value & 0x3) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Tx Descriptor address should be"
|
||||
" 32 bit aligned\n");
|
||||
}
|
||||
/* Ignore [1:0] bits */
|
||||
s->regs[addr] = value & ~3;
|
||||
break;
|
||||
case R_DMA_RX_DESC:
|
||||
if (value & 0x3) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "Rx Descriptor address should be"
|
||||
" 32 bit aligned\n");
|
||||
}
|
||||
/* Ignore [1:0] bits */
|
||||
s->regs[addr] = value & ~3;
|
||||
break;
|
||||
case R_DMA_TX_STATUS:
|
||||
if (value & R_DMA_TX_STATUS_UNDERRUN_MASK) {
|
||||
s->regs[addr] &= ~R_DMA_TX_STATUS_UNDERRUN_MASK;
|
||||
}
|
||||
if (value & R_DMA_TX_STATUS_PKT_SENT_MASK) {
|
||||
pktcnt = FIELD_EX32(s->regs[addr], DMA_TX_STATUS, PKTCNT);
|
||||
pktcnt--;
|
||||
s->regs[addr] = FIELD_DP32(s->regs[addr], DMA_TX_STATUS,
|
||||
PKTCNT, pktcnt);
|
||||
if (pktcnt == 0) {
|
||||
s->regs[addr] &= ~R_DMA_TX_STATUS_PKT_SENT_MASK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case R_DMA_RX_STATUS:
|
||||
if (value & R_DMA_RX_STATUS_OVERFLOW_MASK) {
|
||||
s->regs[addr] &= ~R_DMA_RX_STATUS_OVERFLOW_MASK;
|
||||
}
|
||||
if (value & R_DMA_RX_STATUS_PKT_RCVD_MASK) {
|
||||
pktcnt = FIELD_EX32(s->regs[addr], DMA_RX_STATUS, PKTCNT);
|
||||
pktcnt--;
|
||||
s->regs[addr] = FIELD_DP32(s->regs[addr], DMA_RX_STATUS,
|
||||
PKTCNT, pktcnt);
|
||||
if (pktcnt == 0) {
|
||||
s->regs[addr] &= ~R_DMA_RX_STATUS_PKT_RCVD_MASK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case R_DMA_IRQ:
|
||||
break;
|
||||
case R_MII_CMD:
|
||||
if (value & R_MII_CMD_READ_MASK) {
|
||||
s->regs[R_MII_STS] = read_from_phy(s);
|
||||
}
|
||||
break;
|
||||
case R_MII_CTL:
|
||||
s->regs[addr] = value;
|
||||
write_to_phy(s);
|
||||
break;
|
||||
case R_STA1:
|
||||
s->regs[addr] = value;
|
||||
/*
|
||||
* R_STA1 [31:24] : octet 1 of mac address
|
||||
* R_STA1 [23:16] : octet 2 of mac address
|
||||
* R_STA1 [15:8] : octet 3 of mac address
|
||||
* R_STA1 [7:0] : octet 4 of mac address
|
||||
*/
|
||||
stl_be_p(s->mac_addr, value);
|
||||
break;
|
||||
case R_STA2:
|
||||
s->regs[addr] = value;
|
||||
/*
|
||||
* R_STA2 [31:24] : octet 5 of mac address
|
||||
* R_STA2 [23:16] : octet 6 of mac address
|
||||
*/
|
||||
stw_be_p(s->mac_addr + 4, value >> 16);
|
||||
break;
|
||||
default:
|
||||
if (addr >= ARRAY_SIZE(s->regs)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__,
|
||||
addr * 4);
|
||||
return;
|
||||
}
|
||||
s->regs[addr] = value;
|
||||
break;
|
||||
}
|
||||
emac_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps emac_ops = {
|
||||
.read = emac_read,
|
||||
.write = emac_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static bool emac_can_rx(NetClientState *nc)
|
||||
{
|
||||
MSF2EmacState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
return (s->regs[R_CFG1] & R_CFG1_RX_EN_MASK) &&
|
||||
(s->regs[R_DMA_RX_CTL] & R_DMA_RX_CTL_EN_MASK);
|
||||
}
|
||||
|
||||
static bool addr_filter_ok(MSF2EmacState *s, const uint8_t *buf)
|
||||
{
|
||||
/* The broadcast MAC address: FF:FF:FF:FF:FF:FF */
|
||||
const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF };
|
||||
bool bcast_en = true;
|
||||
bool mcast_en = true;
|
||||
|
||||
if (s->regs[R_FIFO_CFG5] & R_FIFO_CFG5_BCAST_MASK) {
|
||||
bcast_en = true; /* Broadcast dont care for drop circuitry */
|
||||
} else if (s->regs[R_FIFO_CFG4] & R_FIFO_CFG4_BCAST_MASK) {
|
||||
bcast_en = false;
|
||||
}
|
||||
|
||||
if (s->regs[R_FIFO_CFG5] & R_FIFO_CFG5_MCAST_MASK) {
|
||||
mcast_en = true; /* Multicast dont care for drop circuitry */
|
||||
} else if (s->regs[R_FIFO_CFG4] & R_FIFO_CFG4_MCAST_MASK) {
|
||||
mcast_en = false;
|
||||
}
|
||||
|
||||
if (!memcmp(buf, broadcast_addr, sizeof(broadcast_addr))) {
|
||||
return bcast_en;
|
||||
}
|
||||
|
||||
if (buf[0] & 1) {
|
||||
return mcast_en;
|
||||
}
|
||||
|
||||
return !memcmp(buf, s->mac_addr, sizeof(s->mac_addr));
|
||||
}
|
||||
|
||||
static ssize_t emac_rx(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||
{
|
||||
MSF2EmacState *s = qemu_get_nic_opaque(nc);
|
||||
EmacDesc d;
|
||||
uint8_t pktcnt;
|
||||
uint32_t status;
|
||||
|
||||
if (size > (s->regs[R_MAX_FRAME_LENGTH] & 0xFFFF)) {
|
||||
return size;
|
||||
}
|
||||
if (!addr_filter_ok(s, buf)) {
|
||||
return size;
|
||||
}
|
||||
|
||||
emac_load_desc(s, &d, s->rx_desc);
|
||||
|
||||
if (d.pktsize & EMPTY_MASK) {
|
||||
address_space_write(&s->dma_as, d.pktaddr, MEMTXATTRS_UNSPECIFIED,
|
||||
buf, size & PKT_SIZE);
|
||||
d.pktsize = size & PKT_SIZE;
|
||||
emac_store_desc(s, &d, s->rx_desc);
|
||||
/* update received packets count */
|
||||
status = s->regs[R_DMA_RX_STATUS];
|
||||
pktcnt = FIELD_EX32(status, DMA_RX_STATUS, PKTCNT);
|
||||
pktcnt++;
|
||||
s->regs[R_DMA_RX_STATUS] = FIELD_DP32(status, DMA_RX_STATUS,
|
||||
PKTCNT, pktcnt);
|
||||
s->regs[R_DMA_RX_STATUS] |= R_DMA_RX_STATUS_PKT_RCVD_MASK;
|
||||
s->rx_desc = d.next;
|
||||
} else {
|
||||
s->regs[R_DMA_RX_CTL] &= ~R_DMA_RX_CTL_EN_MASK;
|
||||
s->regs[R_DMA_RX_STATUS] |= R_DMA_RX_STATUS_OVERFLOW_MASK;
|
||||
}
|
||||
emac_update_irq(s);
|
||||
return size;
|
||||
}
|
||||
|
||||
static void msf2_emac_reset(DeviceState *dev)
|
||||
{
|
||||
MSF2EmacState *s = MSS_EMAC(dev);
|
||||
|
||||
msf2_emac_do_reset(s);
|
||||
}
|
||||
|
||||
static void emac_set_link(NetClientState *nc)
|
||||
{
|
||||
MSF2EmacState *s = qemu_get_nic_opaque(nc);
|
||||
|
||||
msf2_phy_update_link(s);
|
||||
}
|
||||
|
||||
static NetClientInfo net_msf2_emac_info = {
|
||||
.type = NET_CLIENT_DRIVER_NIC,
|
||||
.size = sizeof(NICState),
|
||||
.can_receive = emac_can_rx,
|
||||
.receive = emac_rx,
|
||||
.link_status_changed = emac_set_link,
|
||||
};
|
||||
|
||||
static void msf2_emac_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
MSF2EmacState *s = MSS_EMAC(dev);
|
||||
|
||||
if (!s->dma_mr) {
|
||||
error_setg(errp, "MSS_EMAC 'ahb-bus' link not set");
|
||||
return;
|
||||
}
|
||||
|
||||
address_space_init(&s->dma_as, s->dma_mr, "emac-ahb");
|
||||
|
||||
qemu_macaddr_default_if_unset(&s->conf.macaddr);
|
||||
s->nic = qemu_new_nic(&net_msf2_emac_info, &s->conf,
|
||||
object_get_typename(OBJECT(dev)), dev->id, s);
|
||||
qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
|
||||
}
|
||||
|
||||
static void msf2_emac_init(Object *obj)
|
||||
{
|
||||
MSF2EmacState *s = MSS_EMAC(obj);
|
||||
|
||||
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
|
||||
|
||||
memory_region_init_io(&s->mmio, obj, &emac_ops, s,
|
||||
"msf2-emac", R_MAX * 4);
|
||||
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
|
||||
}
|
||||
|
||||
static Property msf2_emac_properties[] = {
|
||||
DEFINE_PROP_LINK("ahb-bus", MSF2EmacState, dma_mr,
|
||||
TYPE_MEMORY_REGION, MemoryRegion *),
|
||||
DEFINE_NIC_PROPERTIES(MSF2EmacState, conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_msf2_emac = {
|
||||
.name = TYPE_MSS_EMAC,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8_ARRAY(mac_addr, MSF2EmacState, ETH_ALEN),
|
||||
VMSTATE_UINT32(rx_desc, MSF2EmacState),
|
||||
VMSTATE_UINT16_ARRAY(phy_regs, MSF2EmacState, PHY_MAX_REGS),
|
||||
VMSTATE_UINT32_ARRAY(regs, MSF2EmacState, R_MAX),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void msf2_emac_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = msf2_emac_realize;
|
||||
dc->reset = msf2_emac_reset;
|
||||
dc->vmsd = &vmstate_msf2_emac;
|
||||
device_class_set_props(dc, msf2_emac_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo msf2_emac_info = {
|
||||
.name = TYPE_MSS_EMAC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(MSF2EmacState),
|
||||
.instance_init = msf2_emac_init,
|
||||
.class_init = msf2_emac_class_init,
|
||||
};
|
||||
|
||||
static void msf2_emac_register_types(void)
|
||||
{
|
||||
type_register_static(&msf2_emac_info);
|
||||
}
|
||||
|
||||
type_init(msf2_emac_register_types)
|
@ -29,6 +29,7 @@
|
||||
#include "hw/timer/mss-timer.h"
|
||||
#include "hw/misc/msf2-sysreg.h"
|
||||
#include "hw/ssi/mss-spi.h"
|
||||
#include "hw/net/msf2-emac.h"
|
||||
|
||||
#define TYPE_MSF2_SOC "msf2-soc"
|
||||
#define MSF2_SOC(obj) OBJECT_CHECK(MSF2State, (obj), TYPE_MSF2_SOC)
|
||||
@ -62,6 +63,7 @@ typedef struct MSF2State {
|
||||
MSF2SysregState sysreg;
|
||||
MSSTimerState timer;
|
||||
MSSSpiState spi[MSF2_NUM_SPIS];
|
||||
MSF2EmacState emac;
|
||||
} MSF2State;
|
||||
|
||||
#endif
|
||||
|
@ -49,6 +49,7 @@ typedef struct {
|
||||
CharBackend chr;
|
||||
qemu_irq irq;
|
||||
QEMUTimer *fifo_trigger_handle;
|
||||
Clock *refclk;
|
||||
} CadenceUARTState;
|
||||
|
||||
static inline DeviceState *cadence_uart_create(hwaddr addr,
|
||||
|
225
include/hw/clock.h
Normal file
225
include/hw/clock.h
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Hardware Clocks
|
||||
*
|
||||
* Copyright GreenSocs 2016-2020
|
||||
*
|
||||
* Authors:
|
||||
* Frederic Konrad
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef QEMU_HW_CLOCK_H
|
||||
#define QEMU_HW_CLOCK_H
|
||||
|
||||
#include "qom/object.h"
|
||||
#include "qemu/queue.h"
|
||||
|
||||
#define TYPE_CLOCK "clock"
|
||||
#define CLOCK(obj) OBJECT_CHECK(Clock, (obj), TYPE_CLOCK)
|
||||
|
||||
typedef void ClockCallback(void *opaque);
|
||||
|
||||
/*
|
||||
* clock store a value representing the clock's period in 2^-32ns unit.
|
||||
* It can represent:
|
||||
* + periods from 2^-32ns up to 4seconds
|
||||
* + frequency from ~0.25Hz 2e10Ghz
|
||||
* Resolution of frequency representation decreases with frequency:
|
||||
* + at 100MHz, resolution is ~2mHz
|
||||
* + at 1Ghz, resolution is ~0.2Hz
|
||||
* + at 10Ghz, resolution is ~20Hz
|
||||
*/
|
||||
#define CLOCK_PERIOD_1SEC (1000000000llu << 32)
|
||||
|
||||
/*
|
||||
* macro helpers to convert to hertz / nanosecond
|
||||
*/
|
||||
#define CLOCK_PERIOD_FROM_NS(ns) ((ns) * (CLOCK_PERIOD_1SEC / 1000000000llu))
|
||||
#define CLOCK_PERIOD_TO_NS(per) ((per) / (CLOCK_PERIOD_1SEC / 1000000000llu))
|
||||
#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u)
|
||||
#define CLOCK_PERIOD_TO_HZ(per) (((per) != 0) ? CLOCK_PERIOD_1SEC / (per) : 0u)
|
||||
|
||||
/**
|
||||
* Clock:
|
||||
* @parent_obj: parent class
|
||||
* @period: unsigned integer representing the period of the clock
|
||||
* @canonical_path: clock path string cache (used for trace purpose)
|
||||
* @callback: called when clock changes
|
||||
* @callback_opaque: argument for @callback
|
||||
* @source: source (or parent in clock tree) of the clock
|
||||
* @children: list of clocks connected to this one (it is their source)
|
||||
* @sibling: structure used to form a clock list
|
||||
*/
|
||||
|
||||
typedef struct Clock Clock;
|
||||
|
||||
struct Clock {
|
||||
/*< private >*/
|
||||
Object parent_obj;
|
||||
|
||||
/* all fields are private and should not be modified directly */
|
||||
|
||||
/* fields */
|
||||
uint64_t period;
|
||||
char *canonical_path;
|
||||
ClockCallback *callback;
|
||||
void *callback_opaque;
|
||||
|
||||
/* Clocks are organized in a clock tree */
|
||||
Clock *source;
|
||||
QLIST_HEAD(, Clock) children;
|
||||
QLIST_ENTRY(Clock) sibling;
|
||||
};
|
||||
|
||||
/*
|
||||
* vmstate description entry to be added in device vmsd.
|
||||
*/
|
||||
extern const VMStateDescription vmstate_clock;
|
||||
#define VMSTATE_CLOCK(field, state) \
|
||||
VMSTATE_CLOCK_V(field, state, 0)
|
||||
#define VMSTATE_CLOCK_V(field, state, version) \
|
||||
VMSTATE_STRUCT_POINTER_V(field, state, version, vmstate_clock, Clock)
|
||||
|
||||
/**
|
||||
* clock_setup_canonical_path:
|
||||
* @clk: clock
|
||||
*
|
||||
* compute the canonical path of the clock (used by log messages)
|
||||
*/
|
||||
void clock_setup_canonical_path(Clock *clk);
|
||||
|
||||
/**
|
||||
* clock_set_callback:
|
||||
* @clk: the clock to register the callback into
|
||||
* @cb: the callback function
|
||||
* @opaque: the argument to the callback
|
||||
*
|
||||
* Register a callback called on every clock update.
|
||||
*/
|
||||
void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque);
|
||||
|
||||
/**
|
||||
* clock_clear_callback:
|
||||
* @clk: the clock to delete the callback from
|
||||
*
|
||||
* Unregister the callback registered with clock_set_callback.
|
||||
*/
|
||||
void clock_clear_callback(Clock *clk);
|
||||
|
||||
/**
|
||||
* clock_set_source:
|
||||
* @clk: the clock.
|
||||
* @src: the source clock
|
||||
*
|
||||
* Setup @src as the clock source of @clk. The current @src period
|
||||
* value is also copied to @clk and its subtree but no callback is
|
||||
* called.
|
||||
* Further @src update will be propagated to @clk and its subtree.
|
||||
*/
|
||||
void clock_set_source(Clock *clk, Clock *src);
|
||||
|
||||
/**
|
||||
* clock_set:
|
||||
* @clk: the clock to initialize.
|
||||
* @value: the clock's value, 0 means unclocked
|
||||
*
|
||||
* Set the local cached period value of @clk to @value.
|
||||
*/
|
||||
void clock_set(Clock *clk, uint64_t value);
|
||||
|
||||
static inline void clock_set_hz(Clock *clk, unsigned hz)
|
||||
{
|
||||
clock_set(clk, CLOCK_PERIOD_FROM_HZ(hz));
|
||||
}
|
||||
|
||||
static inline void clock_set_ns(Clock *clk, unsigned ns)
|
||||
{
|
||||
clock_set(clk, CLOCK_PERIOD_FROM_NS(ns));
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_propagate:
|
||||
* @clk: the clock
|
||||
*
|
||||
* Propagate the clock period that has been previously configured using
|
||||
* @clock_set(). This will update recursively all connected clocks.
|
||||
* It is an error to call this function on a clock which has a source.
|
||||
* Note: this function must not be called during device inititialization
|
||||
* or migration.
|
||||
*/
|
||||
void clock_propagate(Clock *clk);
|
||||
|
||||
/**
|
||||
* clock_update:
|
||||
* @clk: the clock to update.
|
||||
* @value: the new clock's value, 0 means unclocked
|
||||
*
|
||||
* Update the @clk to the new @value. All connected clocks will be informed
|
||||
* of this update. This is equivalent to call @clock_set() then
|
||||
* @clock_propagate().
|
||||
*/
|
||||
static inline void clock_update(Clock *clk, uint64_t value)
|
||||
{
|
||||
clock_set(clk, value);
|
||||
clock_propagate(clk);
|
||||
}
|
||||
|
||||
static inline void clock_update_hz(Clock *clk, unsigned hz)
|
||||
{
|
||||
clock_update(clk, CLOCK_PERIOD_FROM_HZ(hz));
|
||||
}
|
||||
|
||||
static inline void clock_update_ns(Clock *clk, unsigned ns)
|
||||
{
|
||||
clock_update(clk, CLOCK_PERIOD_FROM_NS(ns));
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_get:
|
||||
* @clk: the clk to fetch the clock
|
||||
*
|
||||
* @return: the current period.
|
||||
*/
|
||||
static inline uint64_t clock_get(const Clock *clk)
|
||||
{
|
||||
return clk->period;
|
||||
}
|
||||
|
||||
static inline unsigned clock_get_hz(Clock *clk)
|
||||
{
|
||||
return CLOCK_PERIOD_TO_HZ(clock_get(clk));
|
||||
}
|
||||
|
||||
static inline unsigned clock_get_ns(Clock *clk)
|
||||
{
|
||||
return CLOCK_PERIOD_TO_NS(clock_get(clk));
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_is_enabled:
|
||||
* @clk: a clock
|
||||
*
|
||||
* @return: true if the clock is running.
|
||||
*/
|
||||
static inline bool clock_is_enabled(const Clock *clk)
|
||||
{
|
||||
return clock_get(clk) != 0;
|
||||
}
|
||||
|
||||
static inline void clock_init(Clock *clk, uint64_t value)
|
||||
{
|
||||
clock_set(clk, value);
|
||||
}
|
||||
static inline void clock_init_hz(Clock *clk, uint64_t value)
|
||||
{
|
||||
clock_set_hz(clk, value);
|
||||
}
|
||||
static inline void clock_init_ns(Clock *clk, uint64_t value)
|
||||
{
|
||||
clock_set_ns(clk, value);
|
||||
}
|
||||
|
||||
#endif /* QEMU_HW_CLOCK_H */
|
@ -42,7 +42,7 @@
|
||||
#define NRF51_GPIO_REG_DIRSET 0x518
|
||||
#define NRF51_GPIO_REG_DIRCLR 0x51C
|
||||
#define NRF51_GPIO_REG_CNF_START 0x700
|
||||
#define NRF51_GPIO_REG_CNF_END 0x77F
|
||||
#define NRF51_GPIO_REG_CNF_END 0x77C
|
||||
|
||||
#define NRF51_GPIO_PULLDOWN 1
|
||||
#define NRF51_GPIO_PULLUP 3
|
||||
|
53
include/hw/net/msf2-emac.h
Normal file
53
include/hw/net/msf2-emac.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* QEMU model of the Smartfusion2 Ethernet MAC.
|
||||
*
|
||||
* Copyright (c) 2020 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "exec/memory.h"
|
||||
#include "net/net.h"
|
||||
#include "net/eth.h"
|
||||
|
||||
#define TYPE_MSS_EMAC "msf2-emac"
|
||||
#define MSS_EMAC(obj) \
|
||||
OBJECT_CHECK(MSF2EmacState, (obj), TYPE_MSS_EMAC)
|
||||
|
||||
#define R_MAX (0x1a0 / 4)
|
||||
#define PHY_MAX_REGS 32
|
||||
|
||||
typedef struct MSF2EmacState {
|
||||
SysBusDevice parent;
|
||||
|
||||
MemoryRegion mmio;
|
||||
MemoryRegion *dma_mr;
|
||||
AddressSpace dma_as;
|
||||
|
||||
qemu_irq irq;
|
||||
NICState *nic;
|
||||
NICConf conf;
|
||||
|
||||
uint8_t mac_addr[ETH_ALEN];
|
||||
uint32_t rx_desc;
|
||||
uint16_t phy_regs[PHY_MAX_REGS];
|
||||
|
||||
uint32_t regs[R_MAX];
|
||||
} MSF2EmacState;
|
159
include/hw/qdev-clock.h
Normal file
159
include/hw/qdev-clock.h
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Device's clock input and output
|
||||
*
|
||||
* Copyright GreenSocs 2016-2020
|
||||
*
|
||||
* Authors:
|
||||
* Frederic Konrad
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef QDEV_CLOCK_H
|
||||
#define QDEV_CLOCK_H
|
||||
|
||||
#include "hw/clock.h"
|
||||
|
||||
/**
|
||||
* qdev_init_clock_in:
|
||||
* @dev: the device to add an input clock to
|
||||
* @name: the name of the clock (can't be NULL).
|
||||
* @callback: optional callback to be called on update or NULL.
|
||||
* @opaque: argument for the callback
|
||||
* @returns: a pointer to the newly added clock
|
||||
*
|
||||
* Add an input clock to device @dev as a clock named @name.
|
||||
* This adds a child<> property.
|
||||
* The callback will be called with @opaque as opaque parameter.
|
||||
*/
|
||||
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
|
||||
ClockCallback *callback, void *opaque);
|
||||
|
||||
/**
|
||||
* qdev_init_clock_out:
|
||||
* @dev: the device to add an output clock to
|
||||
* @name: the name of the clock (can't be NULL).
|
||||
* @returns: a pointer to the newly added clock
|
||||
*
|
||||
* Add an output clock to device @dev as a clock named @name.
|
||||
* This adds a child<> property.
|
||||
*/
|
||||
Clock *qdev_init_clock_out(DeviceState *dev, const char *name);
|
||||
|
||||
/**
|
||||
* qdev_get_clock_in:
|
||||
* @dev: the device which has the clock
|
||||
* @name: the name of the clock (can't be NULL).
|
||||
* @returns: a pointer to the clock
|
||||
*
|
||||
* Get the input clock @name from @dev or NULL if does not exist.
|
||||
*/
|
||||
Clock *qdev_get_clock_in(DeviceState *dev, const char *name);
|
||||
|
||||
/**
|
||||
* qdev_get_clock_out:
|
||||
* @dev: the device which has the clock
|
||||
* @name: the name of the clock (can't be NULL).
|
||||
* @returns: a pointer to the clock
|
||||
*
|
||||
* Get the output clock @name from @dev or NULL if does not exist.
|
||||
*/
|
||||
Clock *qdev_get_clock_out(DeviceState *dev, const char *name);
|
||||
|
||||
/**
|
||||
* qdev_connect_clock_in:
|
||||
* @dev: a device
|
||||
* @name: the name of an input clock in @dev
|
||||
* @source: the source clock (an output clock of another device for example)
|
||||
*
|
||||
* Set the source clock of input clock @name of device @dev to @source.
|
||||
* @source period update will be propagated to @name clock.
|
||||
*/
|
||||
static inline void qdev_connect_clock_in(DeviceState *dev, const char *name,
|
||||
Clock *source)
|
||||
{
|
||||
clock_set_source(qdev_get_clock_in(dev, name), source);
|
||||
}
|
||||
|
||||
/**
|
||||
* qdev_alias_clock:
|
||||
* @dev: the device which has the clock
|
||||
* @name: the name of the clock in @dev (can't be NULL)
|
||||
* @alias_dev: the device to add the clock
|
||||
* @alias_name: the name of the clock in @container
|
||||
* @returns: a pointer to the clock
|
||||
*
|
||||
* Add a clock @alias_name in @alias_dev which is an alias of the clock @name
|
||||
* in @dev. The direction _in_ or _out_ will the same as the original.
|
||||
* An alias clock must not be modified or used by @alias_dev and should
|
||||
* typically be only only for device composition purpose.
|
||||
*/
|
||||
Clock *qdev_alias_clock(DeviceState *dev, const char *name,
|
||||
DeviceState *alias_dev, const char *alias_name);
|
||||
|
||||
/**
|
||||
* qdev_finalize_clocklist:
|
||||
* @dev: the device being finalized
|
||||
*
|
||||
* Clear the clocklist from @dev. Only used internally in qdev.
|
||||
*/
|
||||
void qdev_finalize_clocklist(DeviceState *dev);
|
||||
|
||||
/**
|
||||
* ClockPortInitElem:
|
||||
* @name: name of the clock (can't be NULL)
|
||||
* @output: indicates whether the clock is input or output
|
||||
* @callback: for inputs, optional callback to be called on clock's update
|
||||
* with device as opaque
|
||||
* @offset: optional offset to store the ClockIn or ClockOut pointer in device
|
||||
* state structure (0 means unused)
|
||||
*/
|
||||
struct ClockPortInitElem {
|
||||
const char *name;
|
||||
bool is_output;
|
||||
ClockCallback *callback;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
#define clock_offset_value(devstate, field) \
|
||||
(offsetof(devstate, field) + \
|
||||
type_check(Clock *, typeof_field(devstate, field)))
|
||||
|
||||
#define QDEV_CLOCK(out_not_in, devstate, field, cb) { \
|
||||
.name = (stringify(field)), \
|
||||
.is_output = out_not_in, \
|
||||
.callback = cb, \
|
||||
.offset = clock_offset_value(devstate, field), \
|
||||
}
|
||||
|
||||
/**
|
||||
* QDEV_CLOCK_(IN|OUT):
|
||||
* @devstate: structure type. @dev argument of qdev_init_clocks below must be
|
||||
* a pointer to that same type.
|
||||
* @field: a field in @_devstate (must be Clock*)
|
||||
* @callback: (for input only) callback (or NULL) to be called with the device
|
||||
* state as argument
|
||||
*
|
||||
* The name of the clock will be derived from @field
|
||||
*/
|
||||
#define QDEV_CLOCK_IN(devstate, field, callback) \
|
||||
QDEV_CLOCK(false, devstate, field, callback)
|
||||
|
||||
#define QDEV_CLOCK_OUT(devstate, field) \
|
||||
QDEV_CLOCK(true, devstate, field, NULL)
|
||||
|
||||
#define QDEV_CLOCK_END { .name = NULL }
|
||||
|
||||
typedef struct ClockPortInitElem ClockPortInitArray[];
|
||||
|
||||
/**
|
||||
* qdev_init_clocks:
|
||||
* @dev: the device to add clocks to
|
||||
* @clocks: a QDEV_CLOCK_END-terminated array which contains the
|
||||
* clocks information.
|
||||
*/
|
||||
void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks);
|
||||
|
||||
#endif /* QDEV_CLOCK_H */
|
@ -149,6 +149,17 @@ struct NamedGPIOList {
|
||||
QLIST_ENTRY(NamedGPIOList) node;
|
||||
};
|
||||
|
||||
typedef struct Clock Clock;
|
||||
typedef struct NamedClockList NamedClockList;
|
||||
|
||||
struct NamedClockList {
|
||||
char *name;
|
||||
Clock *clock;
|
||||
bool output;
|
||||
bool alias;
|
||||
QLIST_ENTRY(NamedClockList) node;
|
||||
};
|
||||
|
||||
/**
|
||||
* DeviceState:
|
||||
* @realized: Indicates whether the device has been fully constructed.
|
||||
@ -171,6 +182,7 @@ struct DeviceState {
|
||||
bool allow_unplug_during_migration;
|
||||
BusState *parent_bus;
|
||||
QLIST_HEAD(, NamedGPIOList) gpios;
|
||||
QLIST_HEAD(, NamedClockList) clocks;
|
||||
QLIST_HEAD(, BusState) child_bus;
|
||||
int num_child_bus;
|
||||
int instance_id_alias;
|
||||
|
@ -39,8 +39,11 @@ void *load_device_tree_from_sysfs(void);
|
||||
* NULL. If there is no error but no matching node was found, the
|
||||
* returned array contains a single element equal to NULL. If an error
|
||||
* was encountered when parsing the blob, the function returns NULL
|
||||
*
|
||||
* @name may be NULL to wildcard names and only match compatibility
|
||||
* strings.
|
||||
*/
|
||||
char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
|
||||
char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
|
||||
Error **errp);
|
||||
|
||||
/**
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "migration/misc.h"
|
||||
#include "migration/migration.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
/*
|
||||
* Aliases were a bad idea from the start. Let's keep them
|
||||
@ -737,6 +738,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
|
||||
ObjectClass *class;
|
||||
BusState *child;
|
||||
NamedGPIOList *ngl;
|
||||
NamedClockList *ncl;
|
||||
|
||||
qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
|
||||
dev->id ? dev->id : "");
|
||||
@ -751,6 +753,13 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
|
||||
ngl->num_out);
|
||||
}
|
||||
}
|
||||
QLIST_FOREACH(ncl, &dev->clocks, node) {
|
||||
qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
|
||||
ncl->output ? "out" : "in",
|
||||
ncl->alias ? " (alias)" : "",
|
||||
ncl->name,
|
||||
CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
|
||||
}
|
||||
class = object_get_class(OBJECT(dev));
|
||||
do {
|
||||
qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent);
|
||||
|
@ -35,7 +35,14 @@ struct arm_boot_info;
|
||||
|
||||
#define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU
|
||||
|
||||
typedef struct ARMCPUInfo ARMCPUInfo;
|
||||
typedef struct ARMCPUInfo {
|
||||
const char *name;
|
||||
void (*initfn)(Object *obj);
|
||||
void (*class_init)(ObjectClass *oc, void *data);
|
||||
} ARMCPUInfo;
|
||||
|
||||
void arm_cpu_register(const ARMCPUInfo *info);
|
||||
void aarch64_cpu_register(const ARMCPUInfo *info);
|
||||
|
||||
/**
|
||||
* ARMCPUClass:
|
||||
|
@ -582,7 +582,8 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||
CPUARMState *env = &cpu->env;
|
||||
bool ret = false;
|
||||
|
||||
/* ARMv7-M interrupt masking works differently than -A or -R.
|
||||
/*
|
||||
* ARMv7-M interrupt masking works differently than -A or -R.
|
||||
* There is no FIQ/IRQ distinction. Instead of I and F bits
|
||||
* masking FIQ and IRQ interrupts, an exception is taken only
|
||||
* if it is higher priority than the current execution priority
|
||||
@ -1912,7 +1913,8 @@ static void arm1026_initfn(Object *obj)
|
||||
static void arm1136_r2_initfn(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
/* What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an
|
||||
/*
|
||||
* What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an
|
||||
* older core than plain "arm1136". In particular this does not
|
||||
* have the v6K features.
|
||||
* These ID register values are correct for 1136 but may be wrong
|
||||
@ -2693,18 +2695,13 @@ static void arm_max_initfn(Object *obj)
|
||||
|
||||
#endif /* !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) */
|
||||
|
||||
struct ARMCPUInfo {
|
||||
const char *name;
|
||||
void (*initfn)(Object *obj);
|
||||
void (*class_init)(ObjectClass *oc, void *data);
|
||||
};
|
||||
|
||||
static const ARMCPUInfo arm_cpus[] = {
|
||||
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
|
||||
{ .name = "arm926", .initfn = arm926_initfn },
|
||||
{ .name = "arm946", .initfn = arm946_initfn },
|
||||
{ .name = "arm1026", .initfn = arm1026_initfn },
|
||||
/* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
|
||||
/*
|
||||
* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
|
||||
* older core than plain "arm1136". In particular this does not
|
||||
* have the v6K features.
|
||||
*/
|
||||
@ -2864,7 +2861,7 @@ static void cpu_register_class_init(ObjectClass *oc, void *data)
|
||||
acc->info = data;
|
||||
}
|
||||
|
||||
static void cpu_register(const ARMCPUInfo *info)
|
||||
void arm_cpu_register(const ARMCPUInfo *info)
|
||||
{
|
||||
TypeInfo type_info = {
|
||||
.parent = TYPE_ARM_CPU,
|
||||
@ -2905,7 +2902,7 @@ static void arm_cpu_register_types(void)
|
||||
type_register_static(&idau_interface_type_info);
|
||||
|
||||
while (info->name) {
|
||||
cpu_register(info);
|
||||
arm_cpu_register(info);
|
||||
info++;
|
||||
}
|
||||
|
||||
|
@ -737,12 +737,6 @@ static void aarch64_max_initfn(Object *obj)
|
||||
cpu_max_set_sve_max_vq, NULL, NULL, &error_fatal);
|
||||
}
|
||||
|
||||
struct ARMCPUInfo {
|
||||
const char *name;
|
||||
void (*initfn)(Object *obj);
|
||||
void (*class_init)(ObjectClass *oc, void *data);
|
||||
};
|
||||
|
||||
static const ARMCPUInfo aarch64_cpus[] = {
|
||||
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
|
||||
{ .name = "cortex-a53", .initfn = aarch64_a53_initfn },
|
||||
@ -825,7 +819,7 @@ static void cpu_register_class_init(ObjectClass *oc, void *data)
|
||||
acc->info = data;
|
||||
}
|
||||
|
||||
static void aarch64_cpu_register(const ARMCPUInfo *info)
|
||||
void aarch64_cpu_register(const ARMCPUInfo *info)
|
||||
{
|
||||
TypeInfo type_info = {
|
||||
.parent = TYPE_AARCH64_CPU,
|
||||
|
@ -3442,6 +3442,7 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
|
||||
MMUAccessType access_type, ARMMMUIdx mmu_idx)
|
||||
{
|
||||
@ -3602,9 +3603,11 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
|
||||
}
|
||||
return par64;
|
||||
}
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
{
|
||||
#ifdef CONFIG_TCG
|
||||
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
||||
uint64_t par64;
|
||||
ARMMMUIdx mmu_idx;
|
||||
@ -3664,17 +3667,26 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
|
||||
par64 = do_ats_write(env, value, access_type, mmu_idx);
|
||||
|
||||
A32_BANKED_CURRENT_REG_SET(env, par, par64);
|
||||
#else
|
||||
/* Handled by hardware accelerator. */
|
||||
g_assert_not_reached();
|
||||
#endif /* CONFIG_TCG */
|
||||
}
|
||||
|
||||
static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
#ifdef CONFIG_TCG
|
||||
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
||||
uint64_t par64;
|
||||
|
||||
par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2);
|
||||
|
||||
A32_BANKED_CURRENT_REG_SET(env, par, par64);
|
||||
#else
|
||||
/* Handled by hardware accelerator. */
|
||||
g_assert_not_reached();
|
||||
#endif /* CONFIG_TCG */
|
||||
}
|
||||
|
||||
static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
@ -3689,6 +3701,7 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
#ifdef CONFIG_TCG
|
||||
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
|
||||
ARMMMUIdx mmu_idx;
|
||||
int secure = arm_is_secure_below_el3(env);
|
||||
@ -3728,6 +3741,10 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
}
|
||||
|
||||
env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx);
|
||||
#else
|
||||
/* Handled by hardware accelerator. */
|
||||
g_assert_not_reached();
|
||||
#endif /* CONFIG_TCG */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -275,19 +275,6 @@ DEF_HELPER_2(neon_hsub_u16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_hsub_s32, s32, s32, s32)
|
||||
DEF_HELPER_2(neon_hsub_u32, i32, i32, i32)
|
||||
|
||||
DEF_HELPER_2(neon_cgt_u8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cgt_s8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cgt_u16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cgt_s16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cgt_u32, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cgt_s32, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cge_u8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cge_s8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cge_u16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cge_s16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cge_u32, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_cge_s32, i32, i32, i32)
|
||||
|
||||
DEF_HELPER_2(neon_pmin_u8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_pmin_s8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_pmin_u16, i32, i32, i32)
|
||||
@ -347,9 +334,6 @@ DEF_HELPER_2(neon_mul_u16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_tst_u8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_tst_u16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_tst_u32, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_ceq_u8, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_ceq_u16, i32, i32, i32)
|
||||
DEF_HELPER_2(neon_ceq_u32, i32, i32, i32)
|
||||
|
||||
DEF_HELPER_1(neon_clz_u8, i32, i32)
|
||||
DEF_HELPER_1(neon_clz_u16, i32, i32)
|
||||
@ -686,6 +670,17 @@ DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr)
|
||||
DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr)
|
||||
DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr)
|
||||
|
||||
DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_clt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_clt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_cle0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_cle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_cgt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_cgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_cge0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_3(gvec_cge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
|
||||
|
||||
DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
|
||||
|
@ -562,24 +562,6 @@ uint32_t HELPER(neon_hsub_u32)(uint32_t src1, uint32_t src2)
|
||||
return dest;
|
||||
}
|
||||
|
||||
#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? ~0 : 0
|
||||
NEON_VOP(cgt_s8, neon_s8, 4)
|
||||
NEON_VOP(cgt_u8, neon_u8, 4)
|
||||
NEON_VOP(cgt_s16, neon_s16, 2)
|
||||
NEON_VOP(cgt_u16, neon_u16, 2)
|
||||
NEON_VOP(cgt_s32, neon_s32, 1)
|
||||
NEON_VOP(cgt_u32, neon_u32, 1)
|
||||
#undef NEON_FN
|
||||
|
||||
#define NEON_FN(dest, src1, src2) dest = (src1 >= src2) ? ~0 : 0
|
||||
NEON_VOP(cge_s8, neon_s8, 4)
|
||||
NEON_VOP(cge_u8, neon_u8, 4)
|
||||
NEON_VOP(cge_s16, neon_s16, 2)
|
||||
NEON_VOP(cge_u16, neon_u16, 2)
|
||||
NEON_VOP(cge_s32, neon_s32, 1)
|
||||
NEON_VOP(cge_u32, neon_u32, 1)
|
||||
#undef NEON_FN
|
||||
|
||||
#define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2
|
||||
NEON_POP(pmin_s8, neon_s8, 4)
|
||||
NEON_POP(pmin_u8, neon_u8, 4)
|
||||
@ -1135,12 +1117,6 @@ NEON_VOP(tst_u16, neon_u16, 2)
|
||||
NEON_VOP(tst_u32, neon_u32, 1)
|
||||
#undef NEON_FN
|
||||
|
||||
#define NEON_FN(dest, src1, src2) dest = (src1 == src2) ? -1 : 0
|
||||
NEON_VOP(ceq_u8, neon_u8, 4)
|
||||
NEON_VOP(ceq_u16, neon_u16, 2)
|
||||
NEON_VOP(ceq_u32, neon_u32, 1)
|
||||
#undef NEON_FN
|
||||
|
||||
/* Count Leading Sign/Zero Bits. */
|
||||
static inline int do_clz8(uint8_t x)
|
||||
{
|
||||
|
@ -594,6 +594,14 @@ static void gen_gvec_fn4(DisasContext *s, bool is_q, int rd, int rn, int rm,
|
||||
is_q ? 16 : 8, vec_full_reg_size(s));
|
||||
}
|
||||
|
||||
/* Expand a 2-operand AdvSIMD vector operation using an op descriptor. */
|
||||
static void gen_gvec_op2(DisasContext *s, bool is_q, int rd,
|
||||
int rn, const GVecGen2 *gvec_op)
|
||||
{
|
||||
tcg_gen_gvec_2(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn),
|
||||
is_q ? 16 : 8, vec_full_reg_size(s), gvec_op);
|
||||
}
|
||||
|
||||
/* Expand a 2-operand + immediate AdvSIMD vector operation using
|
||||
* an op descriptor.
|
||||
*/
|
||||
@ -12366,6 +12374,15 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x8: /* CMGT, CMGE */
|
||||
gen_gvec_op2(s, is_q, rd, rn, u ? &cge0_op[size] : &cgt0_op[size]);
|
||||
return;
|
||||
case 0x9: /* CMEQ, CMLE */
|
||||
gen_gvec_op2(s, is_q, rd, rn, u ? &cle0_op[size] : &ceq0_op[size]);
|
||||
return;
|
||||
case 0xa: /* CMLT */
|
||||
gen_gvec_op2(s, is_q, rd, rn, &clt0_op[size]);
|
||||
return;
|
||||
case 0xb:
|
||||
if (u) { /* ABS, NEG */
|
||||
gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_neg, size);
|
||||
@ -12403,29 +12420,12 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
|
||||
for (pass = 0; pass < (is_q ? 4 : 2); pass++) {
|
||||
TCGv_i32 tcg_op = tcg_temp_new_i32();
|
||||
TCGv_i32 tcg_res = tcg_temp_new_i32();
|
||||
TCGCond cond;
|
||||
|
||||
read_vec_element_i32(s, tcg_op, rn, pass, MO_32);
|
||||
|
||||
if (size == 2) {
|
||||
/* Special cases for 32 bit elements */
|
||||
switch (opcode) {
|
||||
case 0xa: /* CMLT */
|
||||
/* 32 bit integer comparison against zero, result is
|
||||
* test ? (2^32 - 1) : 0. We implement via setcond(test)
|
||||
* and inverting.
|
||||
*/
|
||||
cond = TCG_COND_LT;
|
||||
do_cmop:
|
||||
tcg_gen_setcondi_i32(cond, tcg_res, tcg_op, 0);
|
||||
tcg_gen_neg_i32(tcg_res, tcg_res);
|
||||
break;
|
||||
case 0x8: /* CMGT, CMGE */
|
||||
cond = u ? TCG_COND_GE : TCG_COND_GT;
|
||||
goto do_cmop;
|
||||
case 0x9: /* CMEQ, CMLE */
|
||||
cond = u ? TCG_COND_LE : TCG_COND_EQ;
|
||||
goto do_cmop;
|
||||
case 0x4: /* CLS */
|
||||
if (u) {
|
||||
tcg_gen_clzi_i32(tcg_res, tcg_op, 32);
|
||||
@ -12522,36 +12522,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
|
||||
genfn(tcg_res, cpu_env, tcg_op);
|
||||
break;
|
||||
}
|
||||
case 0x8: /* CMGT, CMGE */
|
||||
case 0x9: /* CMEQ, CMLE */
|
||||
case 0xa: /* CMLT */
|
||||
{
|
||||
static NeonGenTwoOpFn * const fns[3][2] = {
|
||||
{ gen_helper_neon_cgt_s8, gen_helper_neon_cgt_s16 },
|
||||
{ gen_helper_neon_cge_s8, gen_helper_neon_cge_s16 },
|
||||
{ gen_helper_neon_ceq_u8, gen_helper_neon_ceq_u16 },
|
||||
};
|
||||
NeonGenTwoOpFn *genfn;
|
||||
int comp;
|
||||
bool reverse;
|
||||
TCGv_i32 tcg_zero = tcg_const_i32(0);
|
||||
|
||||
/* comp = index into [CMGT, CMGE, CMEQ, CMLE, CMLT] */
|
||||
comp = (opcode - 0x8) * 2 + u;
|
||||
/* ...but LE, LT are implemented as reverse GE, GT */
|
||||
reverse = (comp > 2);
|
||||
if (reverse) {
|
||||
comp = 4 - comp;
|
||||
}
|
||||
genfn = fns[comp][size];
|
||||
if (reverse) {
|
||||
genfn(tcg_res, tcg_zero, tcg_op);
|
||||
} else {
|
||||
genfn(tcg_res, tcg_op, tcg_zero);
|
||||
}
|
||||
tcg_temp_free_i32(tcg_zero);
|
||||
break;
|
||||
}
|
||||
case 0x4: /* CLS, CLZ */
|
||||
if (u) {
|
||||
if (size == 0) {
|
||||
|
@ -3917,6 +3917,205 @@ static int do_v81_helper(DisasContext *s, gen_helper_gvec_3_ptr *fn,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void gen_ceq0_i32(TCGv_i32 d, TCGv_i32 a)
|
||||
{
|
||||
tcg_gen_setcondi_i32(TCG_COND_EQ, d, a, 0);
|
||||
tcg_gen_neg_i32(d, d);
|
||||
}
|
||||
|
||||
static void gen_ceq0_i64(TCGv_i64 d, TCGv_i64 a)
|
||||
{
|
||||
tcg_gen_setcondi_i64(TCG_COND_EQ, d, a, 0);
|
||||
tcg_gen_neg_i64(d, d);
|
||||
}
|
||||
|
||||
static void gen_ceq0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
|
||||
{
|
||||
TCGv_vec zero = tcg_const_zeros_vec_matching(d);
|
||||
tcg_gen_cmp_vec(TCG_COND_EQ, vece, d, a, zero);
|
||||
tcg_temp_free_vec(zero);
|
||||
}
|
||||
|
||||
static const TCGOpcode vecop_list_cmp[] = {
|
||||
INDEX_op_cmp_vec, 0
|
||||
};
|
||||
|
||||
const GVecGen2 ceq0_op[4] = {
|
||||
{ .fno = gen_helper_gvec_ceq0_b,
|
||||
.fniv = gen_ceq0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_8 },
|
||||
{ .fno = gen_helper_gvec_ceq0_h,
|
||||
.fniv = gen_ceq0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_16 },
|
||||
{ .fni4 = gen_ceq0_i32,
|
||||
.fniv = gen_ceq0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_32 },
|
||||
{ .fni8 = gen_ceq0_i64,
|
||||
.fniv = gen_ceq0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.prefer_i64 = TCG_TARGET_REG_BITS == 64,
|
||||
.vece = MO_64 },
|
||||
};
|
||||
|
||||
static void gen_cle0_i32(TCGv_i32 d, TCGv_i32 a)
|
||||
{
|
||||
tcg_gen_setcondi_i32(TCG_COND_LE, d, a, 0);
|
||||
tcg_gen_neg_i32(d, d);
|
||||
}
|
||||
|
||||
static void gen_cle0_i64(TCGv_i64 d, TCGv_i64 a)
|
||||
{
|
||||
tcg_gen_setcondi_i64(TCG_COND_LE, d, a, 0);
|
||||
tcg_gen_neg_i64(d, d);
|
||||
}
|
||||
|
||||
static void gen_cle0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
|
||||
{
|
||||
TCGv_vec zero = tcg_const_zeros_vec_matching(d);
|
||||
tcg_gen_cmp_vec(TCG_COND_LE, vece, d, a, zero);
|
||||
tcg_temp_free_vec(zero);
|
||||
}
|
||||
|
||||
const GVecGen2 cle0_op[4] = {
|
||||
{ .fno = gen_helper_gvec_cle0_b,
|
||||
.fniv = gen_cle0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_8 },
|
||||
{ .fno = gen_helper_gvec_cle0_h,
|
||||
.fniv = gen_cle0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_16 },
|
||||
{ .fni4 = gen_cle0_i32,
|
||||
.fniv = gen_cle0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_32 },
|
||||
{ .fni8 = gen_cle0_i64,
|
||||
.fniv = gen_cle0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.prefer_i64 = TCG_TARGET_REG_BITS == 64,
|
||||
.vece = MO_64 },
|
||||
};
|
||||
|
||||
static void gen_cge0_i32(TCGv_i32 d, TCGv_i32 a)
|
||||
{
|
||||
tcg_gen_setcondi_i32(TCG_COND_GE, d, a, 0);
|
||||
tcg_gen_neg_i32(d, d);
|
||||
}
|
||||
|
||||
static void gen_cge0_i64(TCGv_i64 d, TCGv_i64 a)
|
||||
{
|
||||
tcg_gen_setcondi_i64(TCG_COND_GE, d, a, 0);
|
||||
tcg_gen_neg_i64(d, d);
|
||||
}
|
||||
|
||||
static void gen_cge0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
|
||||
{
|
||||
TCGv_vec zero = tcg_const_zeros_vec_matching(d);
|
||||
tcg_gen_cmp_vec(TCG_COND_GE, vece, d, a, zero);
|
||||
tcg_temp_free_vec(zero);
|
||||
}
|
||||
|
||||
const GVecGen2 cge0_op[4] = {
|
||||
{ .fno = gen_helper_gvec_cge0_b,
|
||||
.fniv = gen_cge0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_8 },
|
||||
{ .fno = gen_helper_gvec_cge0_h,
|
||||
.fniv = gen_cge0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_16 },
|
||||
{ .fni4 = gen_cge0_i32,
|
||||
.fniv = gen_cge0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_32 },
|
||||
{ .fni8 = gen_cge0_i64,
|
||||
.fniv = gen_cge0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.prefer_i64 = TCG_TARGET_REG_BITS == 64,
|
||||
.vece = MO_64 },
|
||||
};
|
||||
|
||||
static void gen_clt0_i32(TCGv_i32 d, TCGv_i32 a)
|
||||
{
|
||||
tcg_gen_setcondi_i32(TCG_COND_LT, d, a, 0);
|
||||
tcg_gen_neg_i32(d, d);
|
||||
}
|
||||
|
||||
static void gen_clt0_i64(TCGv_i64 d, TCGv_i64 a)
|
||||
{
|
||||
tcg_gen_setcondi_i64(TCG_COND_LT, d, a, 0);
|
||||
tcg_gen_neg_i64(d, d);
|
||||
}
|
||||
|
||||
static void gen_clt0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
|
||||
{
|
||||
TCGv_vec zero = tcg_const_zeros_vec_matching(d);
|
||||
tcg_gen_cmp_vec(TCG_COND_LT, vece, d, a, zero);
|
||||
tcg_temp_free_vec(zero);
|
||||
}
|
||||
|
||||
const GVecGen2 clt0_op[4] = {
|
||||
{ .fno = gen_helper_gvec_clt0_b,
|
||||
.fniv = gen_clt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_8 },
|
||||
{ .fno = gen_helper_gvec_clt0_h,
|
||||
.fniv = gen_clt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_16 },
|
||||
{ .fni4 = gen_clt0_i32,
|
||||
.fniv = gen_clt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_32 },
|
||||
{ .fni8 = gen_clt0_i64,
|
||||
.fniv = gen_clt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.prefer_i64 = TCG_TARGET_REG_BITS == 64,
|
||||
.vece = MO_64 },
|
||||
};
|
||||
|
||||
static void gen_cgt0_i32(TCGv_i32 d, TCGv_i32 a)
|
||||
{
|
||||
tcg_gen_setcondi_i32(TCG_COND_GT, d, a, 0);
|
||||
tcg_gen_neg_i32(d, d);
|
||||
}
|
||||
|
||||
static void gen_cgt0_i64(TCGv_i64 d, TCGv_i64 a)
|
||||
{
|
||||
tcg_gen_setcondi_i64(TCG_COND_GT, d, a, 0);
|
||||
tcg_gen_neg_i64(d, d);
|
||||
}
|
||||
|
||||
static void gen_cgt0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
|
||||
{
|
||||
TCGv_vec zero = tcg_const_zeros_vec_matching(d);
|
||||
tcg_gen_cmp_vec(TCG_COND_GT, vece, d, a, zero);
|
||||
tcg_temp_free_vec(zero);
|
||||
}
|
||||
|
||||
const GVecGen2 cgt0_op[4] = {
|
||||
{ .fno = gen_helper_gvec_cgt0_b,
|
||||
.fniv = gen_cgt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_8 },
|
||||
{ .fno = gen_helper_gvec_cgt0_h,
|
||||
.fniv = gen_cgt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_16 },
|
||||
{ .fni4 = gen_cgt0_i32,
|
||||
.fniv = gen_cgt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.vece = MO_32 },
|
||||
{ .fni8 = gen_cgt0_i64,
|
||||
.fniv = gen_cgt0_vec,
|
||||
.opt_opc = vecop_list_cmp,
|
||||
.prefer_i64 = TCG_TARGET_REG_BITS == 64,
|
||||
.vece = MO_64 },
|
||||
};
|
||||
|
||||
static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
|
||||
{
|
||||
tcg_gen_vec_sar8i_i64(a, a, shift);
|
||||
@ -6481,6 +6680,27 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
|
||||
tcg_gen_gvec_abs(size, rd_ofs, rm_ofs, vec_size, vec_size);
|
||||
break;
|
||||
|
||||
case NEON_2RM_VCEQ0:
|
||||
tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
|
||||
vec_size, &ceq0_op[size]);
|
||||
break;
|
||||
case NEON_2RM_VCGT0:
|
||||
tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
|
||||
vec_size, &cgt0_op[size]);
|
||||
break;
|
||||
case NEON_2RM_VCLE0:
|
||||
tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
|
||||
vec_size, &cle0_op[size]);
|
||||
break;
|
||||
case NEON_2RM_VCGE0:
|
||||
tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
|
||||
vec_size, &cge0_op[size]);
|
||||
break;
|
||||
case NEON_2RM_VCLT0:
|
||||
tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
|
||||
vec_size, &clt0_op[size]);
|
||||
break;
|
||||
|
||||
default:
|
||||
elementwise:
|
||||
for (pass = 0; pass < (q ? 4 : 2); pass++) {
|
||||
@ -6543,42 +6763,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
|
||||
default: abort();
|
||||
}
|
||||
break;
|
||||
case NEON_2RM_VCGT0: case NEON_2RM_VCLE0:
|
||||
tmp2 = tcg_const_i32(0);
|
||||
switch(size) {
|
||||
case 0: gen_helper_neon_cgt_s8(tmp, tmp, tmp2); break;
|
||||
case 1: gen_helper_neon_cgt_s16(tmp, tmp, tmp2); break;
|
||||
case 2: gen_helper_neon_cgt_s32(tmp, tmp, tmp2); break;
|
||||
default: abort();
|
||||
}
|
||||
tcg_temp_free_i32(tmp2);
|
||||
if (op == NEON_2RM_VCLE0) {
|
||||
tcg_gen_not_i32(tmp, tmp);
|
||||
}
|
||||
break;
|
||||
case NEON_2RM_VCGE0: case NEON_2RM_VCLT0:
|
||||
tmp2 = tcg_const_i32(0);
|
||||
switch(size) {
|
||||
case 0: gen_helper_neon_cge_s8(tmp, tmp, tmp2); break;
|
||||
case 1: gen_helper_neon_cge_s16(tmp, tmp, tmp2); break;
|
||||
case 2: gen_helper_neon_cge_s32(tmp, tmp, tmp2); break;
|
||||
default: abort();
|
||||
}
|
||||
tcg_temp_free_i32(tmp2);
|
||||
if (op == NEON_2RM_VCLT0) {
|
||||
tcg_gen_not_i32(tmp, tmp);
|
||||
}
|
||||
break;
|
||||
case NEON_2RM_VCEQ0:
|
||||
tmp2 = tcg_const_i32(0);
|
||||
switch(size) {
|
||||
case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
|
||||
case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
|
||||
case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
|
||||
default: abort();
|
||||
}
|
||||
tcg_temp_free_i32(tmp2);
|
||||
break;
|
||||
case NEON_2RM_VCGT0_F:
|
||||
{
|
||||
TCGv_ptr fpstatus = get_fpstatus_ptr(1);
|
||||
|
@ -275,6 +275,11 @@ static inline void gen_swstep_exception(DisasContext *s, int isv, int ex)
|
||||
uint64_t vfp_expand_imm(int size, uint8_t imm8);
|
||||
|
||||
/* Vector operations shared between ARM and AArch64. */
|
||||
extern const GVecGen2 ceq0_op[4];
|
||||
extern const GVecGen2 clt0_op[4];
|
||||
extern const GVecGen2 cgt0_op[4];
|
||||
extern const GVecGen2 cle0_op[4];
|
||||
extern const GVecGen2 cge0_op[4];
|
||||
extern const GVecGen3 mla_op[4];
|
||||
extern const GVecGen3 mls_op[4];
|
||||
extern const GVecGen3 cmtst_op[4];
|
||||
|
@ -1257,3 +1257,28 @@ void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DO_CMP0(NAME, TYPE, OP) \
|
||||
void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \
|
||||
{ \
|
||||
intptr_t i, opr_sz = simd_oprsz(desc); \
|
||||
for (i = 0; i < opr_sz; i += sizeof(TYPE)) { \
|
||||
TYPE nn = *(TYPE *)(vn + i); \
|
||||
*(TYPE *)(vd + i) = -(nn OP 0); \
|
||||
} \
|
||||
clear_tail(vd, opr_sz, simd_maxsz(desc)); \
|
||||
}
|
||||
|
||||
DO_CMP0(gvec_ceq0_b, int8_t, ==)
|
||||
DO_CMP0(gvec_clt0_b, int8_t, <)
|
||||
DO_CMP0(gvec_cle0_b, int8_t, <=)
|
||||
DO_CMP0(gvec_cgt0_b, int8_t, >)
|
||||
DO_CMP0(gvec_cge0_b, int8_t, >=)
|
||||
|
||||
DO_CMP0(gvec_ceq0_h, int16_t, ==)
|
||||
DO_CMP0(gvec_clt0_h, int16_t, <)
|
||||
DO_CMP0(gvec_cle0_h, int16_t, <=)
|
||||
DO_CMP0(gvec_cgt0_h, int16_t, >)
|
||||
DO_CMP0(gvec_cge0_h, int16_t, >=)
|
||||
|
||||
#undef DO_CMP0
|
||||
|
@ -439,6 +439,7 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
|
||||
hw/core/fw-path-provider.o \
|
||||
hw/core/reset.o \
|
||||
hw/core/vmstate-if.o \
|
||||
hw/core/clock.o hw/core/qdev-clock.o \
|
||||
$(test-qapi-obj-y)
|
||||
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
|
||||
migration/vmstate.o migration/vmstate-types.o migration/qemu-file.o \
|
||||
|
@ -336,13 +336,13 @@ class BootLinuxConsole(Test):
|
||||
"""
|
||||
uboot_url = ('https://raw.githubusercontent.com/'
|
||||
'Subbaraya-Sundeep/qemu-test-binaries/'
|
||||
'fa030bd77a014a0b8e360d3b7011df89283a2f0b/u-boot')
|
||||
uboot_hash = 'abba5d9c24cdd2d49cdc2a8aa92976cf20737eff'
|
||||
'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot')
|
||||
uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2'
|
||||
uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
|
||||
spi_url = ('https://raw.githubusercontent.com/'
|
||||
'Subbaraya-Sundeep/qemu-test-binaries/'
|
||||
'fa030bd77a014a0b8e360d3b7011df89283a2f0b/spi.bin')
|
||||
spi_hash = '85f698329d38de63aea6e884a86fbde70890a78a'
|
||||
'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin')
|
||||
spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501'
|
||||
spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash)
|
||||
|
||||
self.vm.set_console()
|
||||
@ -352,7 +352,12 @@ class BootLinuxConsole(Test):
|
||||
'-drive', 'file=' + spi_path + ',if=mtd,format=raw',
|
||||
'-no-reboot')
|
||||
self.vm.launch()
|
||||
self.wait_for_console_pattern('init started: BusyBox')
|
||||
self.wait_for_console_pattern('Enter \'help\' for a list')
|
||||
|
||||
exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15',
|
||||
'eth0: link becomes ready')
|
||||
exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
|
||||
'3 packets transmitted, 3 packets received, 0% packet loss')
|
||||
|
||||
def do_test_arm_raspi2(self, uart_id):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user