From patchwork Wed Sep 4 09:38:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: damien.hedde@greensocs.con X-Patchwork-Id: 11130187 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A5F851395 for ; Wed, 4 Sep 2019 13:05:13 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 770A62186A for ; Wed, 4 Sep 2019 13:05:13 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 770A62186A Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=greensocs.con Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Received: from localhost ([::1]:57562 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1i5UyF-0005N0-QJ for patchwork-qemu-devel@patchwork.kernel.org; Wed, 04 Sep 2019 09:05:11 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:47909) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1i5Rqu-0004ry-1v for qemu-devel@nongnu.org; Wed, 04 Sep 2019 05:45:26 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1i5Rqr-00007C-2S for qemu-devel@nongnu.org; Wed, 04 Sep 2019 05:45:23 -0400 Received: from beetle.greensocs.com ([5.135.226.135]:40546) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1i5Rql-0008N0-Gp; Wed, 04 Sep 2019 05:45:15 -0400 Received: from crumble.bar.greensocs.com (crumble.bar.greensocs.com [172.16.11.102]) by beetle.greensocs.com (Postfix) with ESMTPS id 1D1CC96F5C; Wed, 4 Sep 2019 09:38:54 +0000 (UTC) From: damien.hedde@greensocs.con To: qemu-devel@nongnu.org Date: Wed, 4 Sep 2019 11:38:40 +0200 Message-Id: <20190904093843.8765-7-damien.hedde@greensocs.con> X-Mailer: git-send-email 2.22.0 In-Reply-To: <20190904093843.8765-1-damien.hedde@greensocs.con> References: <20190904093843.8765-1-damien.hedde@greensocs.con> MIME-Version: 1.0 X-Spam: Yes X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 5.135.226.135 X-Mailman-Approved-At: Wed, 04 Sep 2019 08:47:47 -0400 Subject: [Qemu-devel] [PATCH v6 6/9] docs/clocks: add device's clock documentation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Damien Hedde , peter.maydell@linaro.org, berrange@redhat.com, ehabkost@redhat.com, alistair@alistair23.me, mark.burton@greensocs.com, pbonzini@redhat.com, qemu-arm@nongnu.org, marcandre.lureau@redhat.com, edgar.iglesias@gmail.com, philmd@redhat.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" From: Damien Hedde Add the documentation about the clock inputs and outputs in devices. This is based on the original work of Frederic Konrad. Signed-off-by: Damien Hedde --- docs/devel/clock.txt | 246 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 docs/devel/clock.txt diff --git a/docs/devel/clock.txt b/docs/devel/clock.txt new file mode 100644 index 0000000000..18f79922d0 --- /dev/null +++ b/docs/devel/clock.txt @@ -0,0 +1,246 @@ + +What are clocks +=============== + +Clocks are objects representing input and output clocks of objects. They are +QOM objects developed for the purpose of modeling the distribution of clocks in +QEMU. + +This allows 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 objects are ClockIn for the input and ClockOut for the output. The QOM +names are respectively CLOCK_IN and CLOCK_OUT. + +CLOCK_IN and CLOCK_OUT are typically child of some device and created in a +similar way as gpios. ClockIn and ClockOut of different devices can be +connected together. It is possible to create a clock which is not related +to a device; for example to model a clock source of a machine. + +Here is an example of devices with clocks: + + +-------------------+ +--------------------+ + | Device B | | Device C | + +--------------------+ | +-----+ | | +-----+ | + | Device A | | |Clock|>>----->>|Clock| | + | +-----+ | | +-----+ |Out 3| | | |In 5| | + | |Clock|>>--+-->>|Clock| +-----+ | | +-----+ | + | |Out 1| | | | |In 2 | +-----+ | | +-----+ | + | +-----+ | | | +-----+ |Clock|>>----->>|Clock| | + +--------------------+ | | |Out 4| | | |In 6| | + | | +-----+ | | +-----+ | + | +-------------------+ +--------------------+ + | + | +--------------------+ + | | Device D | + | | +-----+ | + +-->>|Clock| | + | |In 7| | + | +-----+ | + +--------------------+ + +Clocks are defined in include/hw/clock.h header and device related functions +are defined in hw/qdev-clock.h header. + +The clock state +=============== + +The state of a clock is its frequency; it is stored as an integer representing +it in Hertz. 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. + +Only the CLOCK_IN object keeps the value of a clock; this allows a device to +fetch the current input frequency at any time. When an output is updated, the +value is immediately propagated to all connected CLOCK_IN. + +Adding clocks to a device +========================= + +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 clock. +Output is more simple, 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 ClockIn/ClockOut 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: 1 input and 1 +output. + +/* device structure containing pointer to the clock objects */ +typedef struct MyDeviceState { + DeviceState parent_obj; + ClockIn *clk_in; + ClockOut *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 clk_in + * field of a MyDeviceState structure with callback clk_in_callback. + * + a clock output named "clk_out" whose pointer is stored in 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); + [...] +} + +Connecting two clocks together +============================== + +Let's say we have 2 devices A and B. A has an output clock named "clk_out" and +B has an input clock named "clk_in". + +The clocks are connected together using the function qdev_connect_clock: +qdev_connect_clock(B, "clk_in", A, "clk_out", &error_abort); +The device which has the input must be the first argument. + +It is possible to connect several input clocks to the same output. Every +input callback will be called when the output changes. + +It is not possible to disconnect a clock or to change the clock connection +after it is done. + +Unconnected input clocks +======================== + +A newly created input clock has a stored frequency value of 0. It means the +clock will be considered as disabled until one sets a new frequency to the +output clock it is connected to. If the clock remains unconnected it will +always keep its initial value of 0. +If this is not the wanted behaviour, clock_init_frequency should be called +on the ClockIn object during device instance init. +For example: +clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback, dev); +clock_init_frequency(clk, 100 * 1000 * 1000); // default value is 100Mhz + +Using clock input frequency +=========================== + +A device can get the current frequency of an input using the +clock_get_frequency(). It returns the last set frequency (or the init value). + +It is also possible to register a callback on input clock frequency changes. +Here is an example: +void clock_callback(void *opaque) { + MyDeviceState *s = (MyDeviceState *) opaque; + /* + * opaque may not be the device state pointer, but most probably it is. + * (It depends on what is given to the qdev_init_clock_in function) + */ + + /* do something with the new frequency */ + fprintf(stdout, "device new frequency is %" PRIu64 "Hz\n", + clock_get_frequency(dev->my_clk_input)); +} + +Changing a clock output +======================= + +A device can change its outputs using the clock_set_frequency function. It +will trigger updates on every connected inputs. + +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: +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: +clock_set_frequency(dev->clkout, 1000 * 1000 * 1000); /* 1Ghz */ +This operation must be done while holding the qemu io lock. + +One can change clocks only when it is allowed to have side effects on other +objects. In consequence, it is forbidden: ++ during migration, ++ and in the init phase of reset. + +Forwarding 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_pass_clock() can be used to achieve this behaviour. Note, that +it is possible to expose the clock under a different name. This works for both +inputs or outputs. + +For example, if device B is a child of device A, device_a_instance_init may +do something like this: +void device_a_instance_init(Object *obj) +{ + AState *A = DEVICE_A(obj); + BState *B; + /* create B object as child of A */ + [...] + /* forward B's clock to A */ + qdev_pass_clock(A, "b_clk", B, "clk"); + /* + * Now A has a clock "b_clk" which forwards to + * the "clk" of its child B. + */ +} + +This function does not return any clock object. It is not possible to add +a callback on a forwarded input clock: in the above example, only B can use +the clock. + +Migration +========= + +Only the ClockIn object has a state. ClockOut is not concerned by migration. + +In case the frequency of in input clock is needed for a device's migration, +this state must be migrated. The VMSTATE_CLOCKIN macro defines an entry to +be added in a vmstate description. + +For example, if a device has a clock input and the device state looks like: +MyDeviceState { + DeviceState parent_obj; + ClockIn *clk; +}; + +Then, to add the clock frequency to the device's migrated state, the vmstate +description is: +VMStateDescription my_device_vmstate = { + .name = "my_device", + .fields = (VMStateField[]) { + VMSTATE_CLOCKIN(clk, MyDeviceState), + VMSTATE_END_OF_LIST() + } +}; + +When adding a input clock support to an existing device, you must care about +migration compatibility. To this end, you can use the clock_init_frequency in +a pre_load function to setup a default value in case the source vm does not +migrate the frequency.