diff mbox

[RFC/WIP] xen: clk: introudce pvclk for device passthrough

Message ID 1452921760-21294-1-git-send-email-van.freenix@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Peng Fan Jan. 16, 2016, 5:22 a.m. UTC
This patch was just a initial patch, not sure whether this way
is ok from you side for handlding clk when doing platform device
passhthrough. Any comments are appreciated, and your comments may
give me a better direction.

Patch was basically tested with passthrough uart2 to DomU on
freescale i.MX7D(Cortex-A7 Dual) platform with two linux os
running based on xen hypervisor.

I have not written the userspace libxl pvclk code, so I use the libxl pvusb
code for test, only need to change the compatible string in this patch
from "vclk" to "vusb", and "clk-ring-ref" to "urb-ring-ref". If this patch
is a acceptable way, I'll begin coding the userspace libxl pvclk.

I follow this doc [1] to do platform device passthrough, but I also
met the issue how to handle clk in the DomU guest. I do not want
to remove the clk api such clk_prepare_enable/disable and etc in the
uart driver, since it work well without xen. But if want the uart driver
to work well in DomU, I need to take care the clk usage. Without
this patch, clk_prepare_enable/disable will fail because no clk tree
in DomU. So I wrote this patch,

Since xen pv use asynchronous notification, clk_enable/disable can not
be implemented in the map, so I use clk_prepare_enable/disable in backend
to map clk_prepare/unprepare in frontend.

Partial dts for DomU linux:
"
        aliases {
		serial0 = &uart2;
		clk0 = &clks;
	};

	passthrough {
		compatible = "simple-bus";
		ranges; #address-cells = <1>;
		#size-cells = <1>;

		clks: clks {
			compatible = "xen,xen-clk";
			#clock-cells = <1>;
			clock-output-names = "uart2_root_clk";
		};

		uart2: serial@10000000 {
			compatible = "fsl,imx7d-uart", "fsl,imx6q-uart", "fsl,imx21-uart";
			reg = <0x10000000 0x10000>;
			interrupts = <0 27 4>;
			interrupt-parent = <65000>;
			clocks = <&clks 0>, <&clks 0>;
			clock-names = "ipg", "per";
		};
	};
"

Xen front clk driver will register itself by probing "xen,xen-clk", register
clk tree by parsing "clock-output-names" and register as ROOT clk.
See the uart2 node, ipg and per clks are also registered in Dom0,
when DomU want to prepare or set rate for the ipg clk, DomU will use pvclk
interface to pass the name "ipg" and the ID(PREPARE, SET_RATE and etc)to Dom0,
Dom0 will use __clk_lookup to find out the "struct clk *" structure and
prepare_enable or set_rate according to the ID from DomU. The ID is defined
in clkif.h in this patch.

The mapping between frontend and backend is as following:

  Frontend                  Backend
clk_prepare             clk_prepare_enable
clk_unprepare           clk_unprepare_disable
clk_set_rate            clk_set_rate
clk_get_rate            clk_get_rate

Frontend work flow example:
1. clk_prepare("ipg")
    |->ID: PREPARE, name: "ipg" packed into a structure
              |->notify backend
2. wait_completion
    Dom0 finished clk_prepare_enable and send event channel interrupt
    to DomU, In DomU frontend interrupt handler, call complete to wakeup.

[1] https://events.linuxfoundation.org/sites/events/files/slides/talk_5.pdf

Signed-off-by: Peng Fan <van.freenix@gmail.com>
Cc: xen-devel@lists.xenproject.org
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: David Vrabel <david.vrabel@citrix.com>
Cc: Julien Grall <julien.grall@citrix.com>
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Cc: Ian Campbell <ian.campbell@citrix.com>
Cc: Michael Turquette <mturquette@baylibre.com>
Cc: Stephen Boyd <sboyd@codeaurora.org>

---
 drivers/clk/Makefile             |   2 +
 drivers/clk/xen/Makefile         |   1 +
 drivers/clk/xen/clk-xen.c        | 197 ++++++++++++++++++
 drivers/clk/xen/xen-clkback.c    | 357 ++++++++++++++++++++++++++++++++
 drivers/clk/xen/xen-clkfront.c   | 432 +++++++++++++++++++++++++++++++++++++++
 include/xen/interface/io/clkif.h |  41 ++++
 6 files changed, 1030 insertions(+)
 create mode 100644 drivers/clk/xen/Makefile
 create mode 100644 drivers/clk/xen/clk-xen.c
 create mode 100644 drivers/clk/xen/xen-clkback.c
 create mode 100644 drivers/clk/xen/xen-clkfront.c
 create mode 100644 include/xen/interface/io/clkif.h

Comments

George Dunlap Jan. 18, 2016, 11:22 a.m. UTC | #1
On Sat, Jan 16, 2016 at 5:22 AM, Peng Fan <van.freenix@gmail.com> wrote:
> This patch was just a initial patch, not sure whether this way
> is ok from you side for handlding clk when doing platform device
> passhthrough. Any comments are appreciated, and your comments may
> give me a better direction.

Hey Peng,

Just speaking from the perspective of a Xen dev who's not an ARM dev:
a few more words on the relationship between pvclk and
device-passthrough would be helpful to set the context.  It sounds
like:

* On ARM, passing through a device requires a clocksource (at least
for many devices)

* dom0 has the hardware clocksource, but at the moment domUs don't
have a suitable clocksource

* This patch implements pvclk front/backend suitable for such devices

Is that right?  In which case something like the following would be helpful:

"This patch introduces pvclk, a paravirtualized clock source suitable
for devices to use when passing through to domUs on ARM systems."

(Obviously change it as necessary to make it accurate.)

Thanks,
 -George
David Vrabel Jan. 18, 2016, 11:24 a.m. UTC | #2
On 16/01/16 05:22, Peng Fan wrote:
> This patch was just a initial patch, not sure whether this way
> is ok from you side for handlding clk when doing platform device
> passhthrough. Any comments are appreciated, and your comments may
> give me a better direction.

There's no documentation on the interface, which makes it difficult to
review.  At a first look it looks very specific to the particular Linux
implementation of a clk subsystem.

> --- /dev/null
> +++ b/include/xen/interface/io/clkif.h
> @@ -0,0 +1,41 @@
> +/*
> + * The code contained herein is licensed under the GNU General Public
> + * License. You may obtain a copy of the GNU General Public License
> + * Version 2 or later at the following locations:
> + *
> + * http://www.opensource.org/licenses/gpl-license.html
> + * http://www.gnu.org/copyleft/gpl.html
> + */

ABIs should be under a more permissive license so they can be used by
other (non-GPLv2) operating systems.

> +
> +#ifndef __XEN_PUBLIC_IO_CLKIF_H__
> +#define __XEN_PUBLIC_IO_CLKIF_H__
> +
> +#include <xen/interface/io/ring.h>
> +#include <xen/interface/grant_table.h>
> +
> +/**/
> +enum {
> +	XENCLK_PREPARE,		/* clk_prepare_enable */
> +	XENCLK_UNPREPARE,	/* clk_unprepare_disable */
> +	XENCLK_GET_RATE,	/* clk_get_rate */
> +	XENCLK_SET_RATE,	/* clk_set_rate */
> +	XENCLK_END,
> +};
> +
> +struct xen_clkif_request {
> +	int id;

You should use fixed width types so the ABI is the same on 32-bit and
64-bit guests.

> +	unsigned long rate;
> +	char clk_name[32];

Where does the frontend get these names from?  31 character names seems
rather limiting.

> +};
> +
> +struct xen_clkif_response {
> +	int id;
> +	int success;
> +	unsigned long rate;
> +	char clk_name[32];
> +};

I don't think you need to the name in the response.  The id will tie the
response to the request.

> +
> +DEFINE_RING_TYPES(xen_clkif, struct xen_clkif_request, struct xen_clkif_response);
> +#define XEN_CLK_RING_SIZE __CONST_RING_SIZE(xen_clkif, PAGE_SIZE)
> +
> +#endif
>
Ian Campbell Jan. 18, 2016, 12:41 p.m. UTC | #3
On Mon, 2016-01-18 at 11:24 +0000, David Vrabel wrote:
> On 16/01/16 05:22, Peng Fan wrote:
> > This patch was just a initial patch, not sure whether this way
> > is ok from you side for handlding clk when doing platform device
> > passhthrough. Any comments are appreciated, and your comments may
> > give me a better direction.
> 
> There's no documentation on the interface, which makes it difficult to
> review.  At a first look it looks very specific to the particular Linux
> implementation of a clk subsystem.
> 
> > --- /dev/null
> > +++ b/include/xen/interface/io/clkif.h
> > @@ -0,0 +1,41 @@
> > +/*
> > + * The code contained herein is licensed under the GNU General Public
> > + * License. You may obtain a copy of the GNU General Public License
> > + * Version 2 or later at the following locations:
> > + *
> > + * http://www.opensource.org/licenses/gpl-license.html
> > + * http://www.gnu.org/copyleft/gpl.html
> > + */
> 
> ABIs should be under a more permissive license so they can be used by
> other (non-GPLv2) operating systems.

... along the same lines proposals for new ABIs should be made in the form
of patches to xen.git:xen/include/public/io/ before being submitted as an
implementation for one particular os.


> > +	unsigned long rate;
> > +	char clk_name[32];
> 
> Where does the frontend get these names from?  31 character names seems
> rather limiting.

Indeed.

At a guess I would assume they come from the device-tree given to the guest
and tie into the host device tree.

I think a better model might be for each clk to have it's own subdirectory
under the overall clock bus node, e.g. something like:

/local/domain/<...>/clk/0/nr-clks = 4
/local/domain/<...>/clk/0/clk-0/ ...
/local/domain/<...>/clk/0/clk-1/ ...
/lo
cal/domain/<...>/clk/0/clk-2/ ...
/local/domain/<...>/clk/0/clk-3/ ...

and for each subdirectory to contain the a node containing the corresponding firmware table identifier (so path in the DT case), which the toolstack knows, so it can setup the f/e and b/e to correspond as necessary, and the f/e device needn't necessarily use the same name as the backend/host).

The request would then include the index and not the name (and as David observes the response only needs the id).

As well as properly documenting the meaning of the operations 
the clkif.h should also define the xenstore nodes and contain the binary layouts of the req/rsp structs (see netif.h for examples of both, blkif.h also includes examples of the former).

I'd also like to see a description of the DT bindings, which I assume must be needed such that the devices clocks property has something to refer to. For example maybe it doesn't make sense for xenstore to contain the path, but for the pvclk node in xenstore to contain the index.

> > +
> > +DEFINE_RING_TYPES(xen_clkif, struct xen_clkif_request, struct
> > xen_clkif_response);
> > +#define XEN_CLK_RING_SIZE __CONST_RING_SIZE(xen_clkif, PAGE_SIZE)
> > +
> > +#endif
> > 
>
Peng Fan Jan. 19, 2016, 1:18 a.m. UTC | #4
Hello George,

On Mon, Jan 18, 2016 at 11:22:44AM +0000, George Dunlap wrote:
>On Sat, Jan 16, 2016 at 5:22 AM, Peng Fan <van.freenix@gmail.com> wrote:
>> This patch was just a initial patch, not sure whether this way
>> is ok from you side for handlding clk when doing platform device
>> passhthrough. Any comments are appreciated, and your comments may
>> give me a better direction.
>
>Hey Peng,
>
>Just speaking from the perspective of a Xen dev who's not an ARM dev:
>a few more words on the relationship between pvclk and
>device-passthrough would be helpful to set the context.  It sounds
>like:
>
>* On ARM, passing through a device requires a clocksource (at least
>for many devices)
>
>* dom0 has the hardware clocksource, but at the moment domUs don't
>have a suitable clocksource
>
>* This patch implements pvclk front/backend suitable for such devices
>
>Is that right?  In which case something like the following would be helpful:

Yeah. right. You have a better explaination than me :)

>
>"This patch introduces pvclk, a paravirtualized clock source suitable
>for devices to use when passing through to domUs on ARM systems."
>
>(Obviously change it as necessary to make it accurate.)

I'll add your words into the commit log.

Thanks,
Peng.

>
>Thanks,
> -George
Peng Fan Jan. 19, 2016, 1:33 a.m. UTC | #5
Hello David,

On Mon, Jan 18, 2016 at 11:24:08AM +0000, David Vrabel wrote:
>On 16/01/16 05:22, Peng Fan wrote:
>> This patch was just a initial patch, not sure whether this way
>> is ok from you side for handlding clk when doing platform device
>> passhthrough. Any comments are appreciated, and your comments may
>> give me a better direction.
>
>There's no documentation on the interface, which makes it difficult to
>review.  At a first look it looks very specific to the particular Linux
>implementation of a clk subsystem.

My bad. I'll add more text in commit log or cover letter in V2 to describe
the interface.

>
>> --- /dev/null
>> +++ b/include/xen/interface/io/clkif.h
>> @@ -0,0 +1,41 @@
>> +/*
>> + * The code contained herein is licensed under the GNU General Public
>> + * License. You may obtain a copy of the GNU General Public License
>> + * Version 2 or later at the following locations:
>> + *
>> + * http://www.opensource.org/licenses/gpl-license.html
>> + * http://www.gnu.org/copyleft/gpl.html
>> + */
>
>ABIs should be under a more permissive license so they can be used by
>other (non-GPLv2) operating systems.

ok. I'll change this.

>
>> +
>> +#ifndef __XEN_PUBLIC_IO_CLKIF_H__
>> +#define __XEN_PUBLIC_IO_CLKIF_H__
>> +
>> +#include <xen/interface/io/ring.h>
>> +#include <xen/interface/grant_table.h>
>> +
>> +/**/
>> +enum {
>> +	XENCLK_PREPARE,		/* clk_prepare_enable */
>> +	XENCLK_UNPREPARE,	/* clk_unprepare_disable */
>> +	XENCLK_GET_RATE,	/* clk_get_rate */
>> +	XENCLK_SET_RATE,	/* clk_set_rate */
>> +	XENCLK_END,
>> +};
>> +
>> +struct xen_clkif_request {
>> +	int id;
>
>You should use fixed width types so the ABI is the same on 32-bit and
>64-bit guests.

Will fix in V2.

>
>> +	unsigned long rate;
>> +	char clk_name[32];
>
>Where does the frontend get these names from?  31 character names seems
>rather limiting.

The clk_name is got from DomU device tree. In the commit log, I wrote this:
"
                clks: clks {
			compatible = "xen,xen-clk";
			#clock-cells = <1>;
			clock-output-names = "uart2_root_clk";
		};
"
The clk_name is one of the entry from clock-output-names, clk_name will
be passed to Dom0, Dom0 will use the clk_name to find the clk structure using
__clk_lookup.


>
>> +};
>> +
>> +struct xen_clkif_response {
>> +	int id;
>> +	int success;
>> +	unsigned long rate;
>> +	char clk_name[32];
>> +};
>
>I don't think you need to the name in the response.  The id will tie the
>response to the request.

My original idea is to  use id and clk_name to check whether the response is for the requester.
You can see in the patch:
" if ((id == comp->id) && !strncmp(name, comp->clk_name, sizeof(comp->clk_name)))"

Maybe clk_name is redundant here. I will remove it in V2.

Thanks for your comments.

Peng.

>
>> +
>> +DEFINE_RING_TYPES(xen_clkif, struct xen_clkif_request, struct xen_clkif_response);
>> +#define XEN_CLK_RING_SIZE __CONST_RING_SIZE(xen_clkif, PAGE_SIZE)
>> +
>> +#endif
>> 
>
Peng Fan Jan. 19, 2016, 2:43 a.m. UTC | #6
Hello Ian,

On Mon, Jan 18, 2016 at 12:41:59PM +0000, Ian Campbell wrote:
>On Mon, 2016-01-18 at 11:24 +0000, David Vrabel wrote:
>> On 16/01/16 05:22, Peng Fan wrote:
>> > This patch was just a initial patch, not sure whether this way
>> > is ok from you side for handlding clk when doing platform device
>> > passhthrough. Any comments are appreciated, and your comments may
>> > give me a better direction.
>> 
>> There's no documentation on the interface, which makes it difficult to
>> review.  At a first look it looks very specific to the particular Linux
>> implementation of a clk subsystem.
>> 
>> > --- /dev/null
>> > +++ b/include/xen/interface/io/clkif.h
>> > @@ -0,0 +1,41 @@
>> > +/*
>> > + * The code contained herein is licensed under the GNU General Public
>> > + * License. You may obtain a copy of the GNU General Public License
>> > + * Version 2 or later at the following locations:
>> > + *
>> > + * http://www.opensource.org/licenses/gpl-license.html
>> > + * http://www.gnu.org/copyleft/gpl.html
>> > + */
>> 
>> ABIs should be under a more permissive license so they can be used by
>> other (non-GPLv2) operating systems.
>
>... along the same lines proposals for new ABIs should be made in the form
>of patches to xen.git:xen/include/public/io/ before being submitted as an
>implementation for one particular os.

I had no idea about this before. Do you mean that before I implement
pvclk for linux, I need to first post the clkif part to xen devel?

If it is, I'll split the interface part and send this part to
xen-devel@lists.xenproject.org for review.

>
>
>> > +	unsigned long rate;
>> > +	char clk_name[32];
>> 
>> Where does the frontend get these names from?  31 character names seems
>> rather limiting.
>
>Indeed.
>
>At a guess I would assume they come from the device-tree given to the guest
>and tie into the host device tree.

Yeah. the clk_name is got from DomU dts, and in Dom0 there is also a same
name.

>
>I think a better model might be for each clk to have it's own subdirectory
>under the overall clock bus node, e.g. something like:
>
>/local/domain/<...>/clk/0/nr-clks = 4
>/local/domain/<...>/clk/0/clk-0/ ...
>/local/domain/<...>/clk/0/clk-1/ ...
>/lo
>cal/domain/<...>/clk/0/clk-2/ ...
>/local/domain/<...>/clk/0/clk-3/ ...
>
>and for each subdirectory to contain the a node containing the corresponding firmware table identifier (so path in the DT case), which the toolstack knows, so it can setup the f/e and b/e to correspond as necessary, and the f/e device needn't necessarily use the same name as the backend/host).
>
>The request would then include the index and not the name (and as David observes the response only needs the id).

For now, I have not began the userspace libxl part for pvclk, I use the libxl pvusb code for test (:
The id acctually means what operation is needed, such as CLK_PREPARE, CLK_UNPREPARE, CLK_SET_RATE, CLK_GET_RATE. I'll add more text to document this in V2.

>
>As well as properly documenting the meaning of the operations 
>the clkif.h should also define the xenstore nodes and contain the binary layouts of the req/rsp structs (see netif.h for examples of both, blkif.h also includes examples of the former).

ok. I'll take netif/blkif for reference.

>
>I'd also like to see a description of the DT bindings, which I assume must be needed such that the devices clocks property has something to refer to. For example maybe it doesn't make sense for xenstore to contain the path, but for the pvclk node in xenstore to contain the index.

The DT bindings for xen pvclk, I use this:
"
                clks: clks {
			compatible = "xen,xen-clk";
			#clock-cells = <1>;
			clock-output-names = "uart2_root_clk";
		};
"
the clock-output-names will be parsed and be registered as clk root. The device will use index to refer to the clk, as the following:
"
                clocks = <&clks 0>, <&clks 0>;
	        clock-names = "ipg", "per";
"
0 means the first one in clock-output-names.

To the xenstore part, I am not sure whether need to expose the clock relate info to xenstore. I just want to store the clock names in xenstore to let
user know what clocks are now handled by DomU.

How about the following?

doamin1:
/local/domain/1/device/vclk/nr-clks
/local/domain/1/device/vclk/clk-0/name
/local/domain/1/device/vclk/clk-1/name

domain2:
/local/domain/2/device/vclk/nr-clks
/local/domain/2/device/vclk/clk-0/name
/local/domain/2/device/vclk/clk-1/name

domain0:
/local/domain/0/backend/vclk/1/0/nr-clks
/local/domain/0/backend/vclk/1/0/clk-0/name
/local/domain/0/backend/vclk/1/0/clk-1/name
/local/domain/0/backend/vclk/1/1/nr-clks
/local/domain/0/backend/vclk/1/1/clk-0/name
/local/domain/0/backend/vclk/1/1/clk-1/name

Or do not store the name and nr-clks in domain0?

Thanks,
Peng.

>
>> > +
>> > +DEFINE_RING_TYPES(xen_clkif, struct xen_clkif_request, struct
>> > xen_clkif_response);
>> > +#define XEN_CLK_RING_SIZE __CONST_RING_SIZE(xen_clkif, PAGE_SIZE)
>> > +
>> > +#endif
>> > 
>>
Ian Campbell Jan. 19, 2016, 10:06 a.m. UTC | #7
On Tue, 2016-01-19 at 10:43 +0800, Peng Fan wrote:
> Hello Ian,
> 
> On Mon, Jan 18, 2016 at 12:41:59PM +0000, Ian Campbell wrote:
> > On Mon, 2016-01-18 at 11:24 +0000, David Vrabel wrote:
> > > On 16/01/16 05:22, Peng Fan wrote:
> > > > This patch was just a initial patch, not sure whether this way
> > > > is ok from you side for handlding clk when doing platform device
> > > > passhthrough. Any comments are appreciated, and your comments may
> > > > give me a better direction.
> > > 
> > > There's no documentation on the interface, which makes it difficult
> > > to
> > > review.  At a first look it looks very specific to the particular
> > > Linux
> > > implementation of a clk subsystem.
> > > 
> > > > --- /dev/null
> > > > +++ b/include/xen/interface/io/clkif.h
> > > > @@ -0,0 +1,41 @@
> > > > +/*
> > > > + * The code contained herein is licensed under the GNU General
> > > > Public
> > > > + * License. You may obtain a copy of the GNU General Public
> > > > License
> > > > + * Version 2 or later at the following locations:
> > > > + *
> > > > + * http://www.opensource.org/licenses/gpl-license.html
> > > > + * http://www.gnu.org/copyleft/gpl.html
> > > > + */
> > > 
> > > ABIs should be under a more permissive license so they can be used by
> > > other (non-GPLv2) operating systems.
> > 
> > ... along the same lines proposals for new ABIs should be made in the
> > form
> > of patches to xen.git:xen/include/public/io/ before being submitted as
> > an
> > implementation for one particular os.
> 
> I had no idea about this before. Do you mean that before I implement
> pvclk for linux, I need to first post the clkif part to xen devel?
> 
> If it is, I'll split the interface part and send this part to
> xen-devel@lists.xenproject.org for review.

Yes, please.

xen.git contains the canonical definition of all Xen PV protocols, copies
are then taking into OSes for implementation purposes.

> 
> > 
> > 
> > > > +	unsigned long rate;
> > > > +	char clk_name[32];
> > > 
> > > Where does the frontend get these names from?  31 character names
> > > seems
> > > rather limiting.
> > 
> > Indeed.
> > 
> > At a guess I would assume they come from the device-tree given to the
> > guest
> > and tie into the host device tree.
> 
> Yeah. the clk_name is got from DomU dts, and in Dom0 there is also a same
> name.
> 
> > 
> > I think a better model might be for each clk to have it's own
> > subdirectory
> > under the overall clock bus node, e.g. something like:
> > 
> > /local/domain/<...>/clk/0/nr-clks = 4
> > /local/domain/<...>/clk/0/clk-0/ ...
> > /local/domain/<...>/clk/0/clk-1/ ...
> > /lo
> > cal/domain/<...>/clk/0/clk-2/ ...
> > /local/domain/<...>/clk/0/clk-3/ ...
> > 
> > and for each subdirectory to contain the a node containing the
> > corresponding firmware table identifier (so path in the DT case), which
> > the toolstack knows, so it can setup the f/e and b/e to correspond as
> > necessary, and the f/e device needn't necessarily use the same name as
> > the backend/host).
> > 
> > The request would then include the index and not the name (and as David
> > observes the response only needs the id).
> 
> For now, I have not began the userspace libxl part for pvclk, I use the
> libxl pvusb code for test (:

Sure, but eventually this will need implementing in the toolstack and the
protocol design should be what is most suitable for the usecase, not what
happens to be most convenient for testing via some quick hack.

> The id acctually means what operation is needed, such as CLK_PREPARE,
> CLK_UNPREPARE, CLK_SET_RATE, CLK_GET_RATE. I'll add more text to document
> this in V2.

Ah, then for consistency with other PV protocols I would suggest renaming
your "id" as "cmd" and adding an "id" field which is simply echoed in the
response to allow the frontend to match responses to requests.

Note however that the important thing in my paragraphs above was the
decoupling of the naming from the f/e and b/e and avoiding the use of the
DT specific path in the ring requests.

The PV protocol should ideally be independent of DT (lets assume we will
want to use it for e.g. ACPI too), although there would probably in this
case need to be a binding from the DT world to the pvclk world to allow the
guest DT to remain consistent (i.e. so devices have something they can
point at which can be resolved into a pvclk).

> > 
> > I'd also like to see a description of the DT bindings, which I assume
> > must be needed such that the devices clocks property has something to
> > refer to. For example maybe it doesn't make sense for xenstore to
> > contain the path, but for the pvclk node in xenstore to contain the
> > index.
> 
> The DT bindings for xen pvclk, I use this:
> "
>                 clks: clks {
> 			compatible = "xen,xen-clk";
> 			#clock-cells = <1>;
> 			clock-output-names = "uart2_root_clk";
> 		};
> "
> the clock-output-names will be parsed and be registered as clk root. The
> device will use index to refer to the clk, as the following:
> "
>                 clocks = <&clks 0>, <&clks 0>;
> 	        clock-names = "ipg", "per";
> "
> 0 means the first one in clock-output-names.

This binding should be defined in Documentation/devicetree/Bindings in the
usual way.

> 
> To the xenstore part, I am not sure whether need to expose the clock
> relate info to xenstore. I just want to store the clock names in xenstore
> to let
> user know what clocks are now handled by DomU.
> 
> How about the following?
> 
> doamin1:
> /local/domain/1/device/vclk/nr-clks
> /local/domain/1/device/vclk/clk-0/name
> /local/domain/1/device/vclk/clk-1/name

                             ^/0

> 
> domain2:
> /local/domain/2/device/vclk/nr-clks
> /local/domain/2/device/vclk/clk-0/name
> /local/domain/2/device/vclk/clk-1/name

                             ^/0

> 
> domain0:
> /local/domain/0/backend/vclk/1/0/nr-clks
> /local/domain/0/backend/vclk/1/0/clk-0/name
> /local/domain/0/backend/vclk/1/0/clk-1/name

Correct.

> /local/domain/0/backend/vclk/1/1/nr-clks
> /local/domain/0/backend/vclk/1/1/clk-0/name
> /local/domain/0/backend/vclk/1/1/clk-1/name

Should be:
                              /2/0

In /local/domain/*/device/vclk/<N> the <N> is the devid or bus number
(better to think of it as the latter in this case).

In /local/domain/<X>/backend/vclk/<Y>/<Z>/ you have:
    X -- the backend domid (may not be 0 in all cases, needs to be planned
    for but not necessarily implemented)
    Y -- the frontend domain
    Z -- the devid/bus within frontend domain

> Or do not store the name and nr-clks in domain0?

The f/e directory should contain the identifier which the frontend needs to
 do the lookup from the firmware tables into pvclk space -- i.e. the name
given in the f/e device tree node (maybe call it dt-name?).

The b/e directory should contain the name of the clock from the backend
(i.e. host) pov i.e. the thing which needs to be looked up in the host
firmware tables.

The two names are not necessarily the same, just as a thought experiment
consider a DT using domU on an ACPI host or vice versa.

Ian.
diff mbox

Patch

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 820714c..7668ecc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -15,6 +15,8 @@  ifeq ($(CONFIG_OF), y)
 obj-$(CONFIG_COMMON_CLK)	+= clk-conf.o
 endif
 
+obj-$(CONFIG_XEN)		+= xen/
+
 # hardware specific clock types
 # please keep this section sorted lexicographically by file/directory path name
 obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
diff --git a/drivers/clk/xen/Makefile b/drivers/clk/xen/Makefile
new file mode 100644
index 0000000..60abe25
--- /dev/null
+++ b/drivers/clk/xen/Makefile
@@ -0,0 +1 @@ 
+obj-y		+= clk-xen.o xen-clkfront.o xen-clkback.o
diff --git a/drivers/clk/xen/clk-xen.c b/drivers/clk/xen/clk-xen.c
new file mode 100644
index 0000000..412a785
--- /dev/null
+++ b/drivers/clk/xen/clk-xen.c
@@ -0,0 +1,197 @@ 
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <xen/interface/io/clkif.h>
+
+#define to_clk_xen(_hw) container_of(_hw, struct xen_clk, hw)
+
+int xen_clkfront_do_request(int id, const char *name, unsigned long rate);
+int xen_clkfront_wait_response(int id, const char *name, unsigned long *rate);
+static int xen_clkfront_prepare(struct clk_hw *hw)
+{
+	struct clk *clk = hw->clk;
+	const char *name = __clk_get_name(clk);
+	unsigned long rate;
+	int err;
+
+	err = xen_clkfront_do_request(XENCLK_PREPARE, name, 0);
+	if (err)
+		return 0;
+
+	err = xen_clkfront_wait_response(XENCLK_PREPARE, name, NULL);
+	if (err)
+		return -EIO;
+
+	return 0;
+}
+
+void xen_clkfront_unprepare(struct clk_hw *hw)
+{
+	struct clk *clk = hw->clk;
+	const char *name = __clk_get_name(clk);
+	unsigned long rate;
+	int err;
+
+	err = xen_clkfront_do_request(XENCLK_UNPREPARE, name, 0);
+	if (err)
+		return 0;
+
+	xen_clkfront_wait_response(XENCLK_UNPREPARE, name, NULL);
+
+	return 0;
+}
+
+/* clk_enable */
+int xen_clkfront_enable(struct clk_hw *hw)
+{
+	/*
+	 * clk_enable API can be used in interrupt context,
+	 * but here the pvclk framework only works in sleepable context.
+	 * So in DomU frontend, clk_prepare takes the responsibility
+	 * for enable clk in backend.
+	 */
+	return 0;
+}
+
+/* clk_disable */
+void xen_clkfront_disable(struct clk_hw *hw)
+{
+}
+
+/* clk_get_rate */
+static unsigned long xen_clkfront_recalc_rate(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct clk *clk = hw->clk;
+	const char *name = __clk_get_name(clk);
+	unsigned long rate;
+	int err;
+	
+	if (!name) {
+		BUG_ON(!name);
+		return 0;
+	}
+
+	err = xen_clkfront_do_request(XENCLK_GET_RATE, name, 0);
+	if (err)
+		return 0;
+
+	err = xen_clkfront_wait_response(XENCLK_GET_RATE, name, &rate);
+	if (err)
+		return 0;
+
+	return rate;
+}
+
+/* clk_set_rate */
+int xen_clkfront_set_rate(struct clk_hw *hw, unsigned long rate,
+			  unsigned long parent_rate)
+{
+	struct clk *clk = hw->clk;
+	const char *name = __clk_get_name(clk);
+	int err;
+
+	if (!name) {
+		BUG_ON(!name);
+		return 0;
+	}
+
+	err = xen_clkfront_do_request(XENCLK_SET_RATE, name, rate);
+	if (err)
+		return 0;
+
+	err = xen_clkfront_wait_response(XENCLK_SET_RATE, name, NULL);
+	if (err)
+		return -EINVAL;
+
+	return 0;
+}
+
+long xen_clkfront_determine_rate(struct clk_hw *hw,
+				 unsigned long rate,
+				 unsigned long min_rate,
+				 unsigned long max_rate,
+				 unsigned long *best_parent_rate,
+				 struct clk_hw **best_parent_hw)
+{
+	/* directly return rate, let backend does this work */
+	return rate;
+}
+
+const struct clk_ops xen_clkfront_ops = {
+	.prepare = xen_clkfront_prepare,
+	.unprepare = xen_clkfront_unprepare,
+	.enable = xen_clkfront_enable,
+	.disable = xen_clkfront_disable,
+	.recalc_rate = xen_clkfront_recalc_rate,
+	.determine_rate = xen_clkfront_determine_rate,
+	.set_rate = xen_clkfront_set_rate,
+};
+EXPORT_SYMBOL_GPL(xen_clkfront_ops);
+
+struct xen_clk {
+	struct clk_hw hw;
+	u8 flags;
+	spinlock_t *lock;
+};
+
+struct clk *clk_register_xen(struct device *dev, const char *name,
+			     const char *parent_name, unsigned long flags,
+			     spinlock_t *lock)
+{
+	struct clk *clk;
+	struct clk_init_data init;
+	struct xen_clk *xenclk;
+
+	xenclk = kzalloc(sizeof(struct xen_clk), GFP_KERNEL);
+	if (!xenclk) {
+		pr_err("%s: cound not allocate xen clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = name;
+	init.ops = &xen_clkfront_ops;
+	/* register as root clk in frontend */
+	init.flags = flags | CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE | CLK_IS_ROOT;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+
+	xenclk->hw.init = &init;
+
+	clk = clk_register(dev, &xenclk->hw);
+	if (IS_ERR(clk)) {
+		pr_err("clk_register failure %s\n", name);
+		kfree(xenclk);
+	}
+
+	return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_xen);
+
+void clk_unregister_xen(struct clk *clk)
+{
+	struct xen_clk *xenclk;
+	struct clk_hw *hw;
+
+	hw = __clk_get_hw(clk);
+	if (!hw)
+		return;
+
+	xenclk = to_clk_xen(hw);
+
+	clk_unregister(clk);
+	kfree(xenclk);
+}
+EXPORT_SYMBOL_GPL(clk_unregister_xen);
diff --git a/drivers/clk/xen/xen-clkback.c b/drivers/clk/xen/xen-clkback.c
new file mode 100644
index 0000000..bc4c14b8
--- /dev/null
+++ b/drivers/clk/xen/xen-clkback.c
@@ -0,0 +1,357 @@ 
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include <xen/xen.h>
+#include <xen/events.h>
+#include <xen/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/page.h>
+
+#include <linux/clk.h>
+
+#include <xen/interface/grant_table.h>
+#include <xen/interface/io/clkif.h>
+
+extern struct clk *__clk_lookup(const char *name);
+extern bool __clk_is_prepared(struct clk *clk);
+
+struct xen_clkback_info {
+	domid_t domid;
+	unsigned irq;
+	unsigned long handle;
+	struct xenbus_device *clkdev;
+	spinlock_t clk_ring_lock;
+	struct xen_clkif_back_ring clk_ring;
+	atomic_t refcnt;
+	int is_connected;
+	int ring_error;
+};
+
+static void xen_clkback_do_response(struct xen_clkback_info *info,
+				    int id, char *name, unsigned long rate,
+				    int success)
+{
+	struct xen_clkif_response *res;
+	unsigned long flags;
+	int notify;
+
+	spin_lock_irqsave(&info->clk_ring_lock, flags);
+	res = RING_GET_RESPONSE(&info->clk_ring, info->clk_ring.rsp_prod_pvt);
+
+	res->success = success;
+	res->id = id;
+	res->rate = rate;
+	strncpy(res->clk_name, name, sizeof(res->clk_name));
+	info->clk_ring.rsp_prod_pvt++;
+
+	/* More stuff */
+	barrier();
+	RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&info->clk_ring, notify);
+	spin_unlock_irqrestore(&info->clk_ring_lock, flags);
+
+	if (notify)
+		notify_remote_via_irq(info->irq);
+}
+
+static int xen_clkback_handle_int(struct xen_clkback_info *info)
+{
+	struct xen_clkif_back_ring *clk_ring = &info->clk_ring;
+	struct xen_clkif_request req;
+	RING_IDX rc, rp;
+	int more_to_do;
+	struct clk *clk;
+	int err;
+	unsigned long rate;
+
+	rc = clk_ring->req_cons;
+	rp = clk_ring->sring->req_prod;
+	rmb();	/* req_cons is written by frontend. */
+	
+	if (RING_REQUEST_PROD_OVERFLOW(clk_ring, rp)) {
+		rc = clk_ring->rsp_prod_pvt;
+		info->ring_error = 1;
+		return 0;
+	}
+
+	while (rc != rp) {
+		if (RING_REQUEST_CONS_OVERFLOW(clk_ring, rc))
+			break;
+
+		req = *RING_GET_REQUEST(clk_ring, rc);
+		clk = __clk_lookup(req.clk_name);
+		if (!clk)
+			pr_err("no clk node for %s\n", req.clk_name);
+
+		switch (req.id) {
+		case XENCLK_PREPARE:
+			err = clk_prepare_enable(clk);
+			xen_clkback_do_response(info, req.id, req.clk_name, 0, err);
+			break;
+		case XENCLK_UNPREPARE:
+			clk_disable_unprepare(clk);
+			err = !__clk_is_prepared(clk);
+			xen_clkback_do_response(info, req.id, req.clk_name, 0, err);
+			break;
+		case XENCLK_GET_RATE:
+			rate = clk_get_rate(clk);
+			xen_clkback_do_response(info, req.id, req.clk_name, rate, 0);
+			break;
+		case XENCLK_SET_RATE:
+			err = clk_set_rate(clk, req.rate);
+			xen_clkback_do_response(info, req.id, req.clk_name, 0, err);
+			break;
+		}
+		clk_ring->req_cons = ++rc;
+
+		cond_resched();
+	}
+
+	RING_FINAL_CHECK_FOR_REQUESTS(clk_ring, more_to_do);
+
+	return !!more_to_do;
+}
+
+static irqreturn_t xen_clkback_be_int(int irq, void *dev_id)
+{
+	struct xen_clkback_info *info = dev_id;
+
+	if (info->ring_error)
+		return IRQ_HANDLED;
+
+	while (xen_clkback_handle_int(info))
+		cond_resched();
+
+	return IRQ_HANDLED;
+}
+
+static int xen_clkback_map(struct xen_clkback_info *info,
+			   grant_ref_t *clk_ring_ref, evtchn_port_t evtchn)
+{
+	int err;
+	void *addr;
+	struct xen_clkif_sring *clk_sring;
+
+	if (info->irq)
+		return 0;
+
+	err = xenbus_map_ring_valloc(info->clkdev, clk_ring_ref, 1, &addr);
+	if (err)
+		return err;
+
+	clk_sring = addr;
+
+	BACK_RING_INIT(&info->clk_ring, clk_sring, PAGE_SIZE);
+
+	err = bind_interdomain_evtchn_to_irq(info->domid, evtchn);
+	if (err < 0)
+		goto fail_evtchn;
+	info->irq = err;
+
+	err = request_threaded_irq(info->irq, NULL, xen_clkback_be_int,
+				   IRQF_ONESHOT, "xen-clkback", info);
+	if (err) {
+		pr_err("bind evtchn to irq failure!\n");
+		goto free_irq;
+	}
+
+	return 0;
+free_irq:
+	unbind_from_irqhandler(info->irq, info);
+	info->irq = 0;
+	info->clk_ring.sring = NULL;
+fail_evtchn:
+	xenbus_unmap_ring_vfree(info->clkdev, clk_sring);
+	return err;
+}
+
+static int xen_clkback_connect_rings(struct xen_clkback_info *info)
+{
+	struct xenbus_device *dev = info->clkdev;
+	unsigned clk_ring_ref, evtchn;
+	int err;
+
+	err = xenbus_gather(XBT_NIL, dev->otherend,
+			    "clk-ring-ref", "%u", &clk_ring_ref,
+			    "event-channel", "%u", &evtchn, NULL);
+	if (err) {
+		xenbus_dev_fatal(dev, err,
+				 "reading %s/ring-ref and event-channel",
+				 dev->otherend);
+		return err;
+	}
+
+	pr_info("xen-pvclk: clk-ring-ref %u, event-channel %u\n",
+		clk_ring_ref, evtchn);
+
+	err = xen_clkback_map(info, &clk_ring_ref, evtchn);
+	if (err)
+		xenbus_dev_fatal(dev, err, "mapping urb-ring-ref %u evtchn %u",
+			clk_ring_ref, evtchn);
+
+	return err;
+}
+
+static void xen_clkback_disconnect(struct xen_clkback_info *info)
+{
+	if (info->irq) {
+		unbind_from_irqhandler(info->irq, info);
+		info->irq = 0;
+	}
+
+	if (info->clk_ring.sring) {
+		xenbus_unmap_ring_vfree(info->clkdev, info->clk_ring.sring);
+		info->clk_ring.sring = NULL;
+	}
+}
+
+static void xen_clkback_frontend_changed(struct xenbus_device *dev,
+					 enum xenbus_state frontend_state)
+{
+	struct xen_clkback_info *info = dev_get_drvdata(&dev->dev);
+
+	switch (frontend_state) {
+	case XenbusStateInitialised:
+	case XenbusStateReconfiguring:
+	case XenbusStateReconfigured:
+		break;
+
+	case XenbusStateInitialising:
+		if (dev->state == XenbusStateClosed) {
+			pr_info("xen-pvclk: %s: prepare for reconnect\n",
+				dev->nodename);
+			xenbus_switch_state(dev, XenbusStateInitWait);
+		}
+		break;
+	case XenbusStateConnected:
+		if (dev->state != XenbusStateConnected)
+			xenbus_switch_state(dev, XenbusStateConnected);
+
+		xen_clkback_connect_rings(info);
+		break;
+	case XenbusStateClosing:
+		xen_clkback_disconnect(info);
+		xenbus_switch_state(dev, XenbusStateClosing);
+		break;
+	case XenbusStateClosed:
+		xenbus_switch_state(dev, XenbusStateClosed);
+		if (xenbus_dev_is_online(dev))
+			break;
+		/* fall through if not online */
+	case XenbusStateUnknown:
+		device_unregister(&dev->dev);
+		break;
+
+	default:
+		xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+				 frontend_state);
+		break;
+	}
+}
+
+static struct xen_clkback_info *xen_clkback_alloc(domid_t domid,
+						  unsigned long handle)
+{
+	struct xen_clkback_info *info;
+
+	info = kzalloc(sizeof(struct xen_clkback_info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+
+	info->domid = domid;
+	info->handle = handle;
+	spin_lock_init(&info->clk_ring_lock);
+	atomic_set(&info->refcnt, 0);
+	info->ring_error = 0;
+
+	return info;
+}
+
+static int xen_clkback_probe(struct xenbus_device *dev,
+			     const struct xenbus_device_id *id)
+{
+	struct xen_clkback_info *info;
+	unsigned long handle;
+	int err;
+
+	if (kstrtoul(strrchr(dev->otherend, '/') + 1, 0, &handle))
+		return -EINVAL;
+
+	info = xen_clkback_alloc(dev->otherend_id, handle);
+	if (!info) {
+		xenbus_dev_fatal(dev, -ENOMEM, "Allocating backend interface");
+		return -ENOMEM;
+	}
+
+	info->clkdev = dev;
+	dev_set_drvdata(&dev->dev, info);
+
+	err = xenbus_switch_state(dev, XenbusStateInitWait);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int xen_clkback_remove(struct xenbus_device *dev)
+{
+	struct xen_clkback_info *info = dev_get_drvdata(&dev->dev);
+
+	if (!info)
+		return 0;
+
+	xen_clkback_disconnect(info);
+
+	kfree(info);
+	dev_set_drvdata(&dev->dev, NULL);
+
+	return 0;
+}
+
+static const struct xenbus_device_id xen_clkback_ids[] = {
+	{ "vclk" },
+	{ "" },
+};
+
+static struct xenbus_driver xen_clkback_driver = {
+	.ids			= xen_clkback_ids,
+	.probe			= xen_clkback_probe,
+	.otherend_changed	= xen_clkback_frontend_changed,
+	.remove			= xen_clkback_remove,
+};
+
+static int __init xen_clkback_init(void)
+{
+	int err;
+
+	if (!xen_domain())
+		return -ENODEV;
+
+	err = xenbus_register_backend(&xen_clkback_driver);
+	if (err)
+		return err;
+
+	/* Can we avoid libxl pvclk? doing xenstore in kernel ? */
+	return 0;
+}
+module_init(xen_clkback_init);
+
+static void __exit xen_clkback_exit(void)
+{
+	xenbus_unregister_driver(&xen_clkback_driver);
+}
+module_exit(xen_clkback_exit);
+
+MODULE_ALIAS("xen-clkback:vclk");
+MODULE_AUTHOR("Peng Fan <van.freenix@gmail.com>");
+MODULE_DESCRIPTION("Xen CLK backend driver (clkback)");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/clk/xen/xen-clkfront.c b/drivers/clk/xen/xen-clkfront.c
new file mode 100644
index 0000000..5ce787b
--- /dev/null
+++ b/drivers/clk/xen/xen-clkfront.c
@@ -0,0 +1,432 @@ 
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/list.h>
+#include <linux/io.h>
+
+#include <xen/xen.h>
+#include <xen/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/events.h>
+#include <xen/page.h>
+
+#include <xen/interface/io/clkif.h>
+
+#define GRANT_INVALID_REF	0
+DEFINE_SPINLOCK(xen_clk_lock);
+static struct clk **clks;
+static struct clk_onecell_data clk_data;
+static struct xen_clkfront_info *ginfo;
+extern struct clk *clk_register_xen(struct device *dev, const char *name,
+				    const char *parent_name,
+				    unsigned long flags,
+				    spinlock_t *lock);
+static int __init xen_clkfront_register(int num, const char **clks_name)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		clks[i] = clk_register_xen(NULL, clks_name[i], NULL,
+					   CLK_GATE_SET_TO_DISABLE,
+					   &xen_clk_lock);
+		if (IS_ERR_OR_NULL(clks[i]))
+			return PTR_ERR(clks[i]);
+	}
+
+	return 0;
+}
+
+static void __init xen_clkfront_deregister(int num)
+{
+	int i;
+
+	for (i = 0; i < num; i++) {
+		if (clks[i])
+			clk_unregister_gate(clks[i]);
+	}
+}
+
+static const struct xenbus_device_id xen_clkfront_ids[] = {
+	{ "vclk" },
+	{ "" },
+};
+
+struct xen_clkfront_comp {
+	struct completion completion;
+	unsigned long rate;
+	int success;
+	int id;
+	char clk_name[32];
+};
+
+struct xen_clkfront_info {
+	spinlock_t lock;
+	struct xenbus_device *clkdev;
+	int clk_ring_ref;
+	struct xen_clkif_front_ring clk_ring;
+	unsigned int evtchn;
+	unsigned int irq;
+	struct xen_clkfront_comp comp[XENCLK_END];
+};
+
+static int xen_clkfront_probe(struct xenbus_device *dev,
+			      const struct xenbus_device_id *id)
+{
+	struct xen_clkfront_info *info;
+	int i;
+
+	info = kzalloc(sizeof(struct xen_clkfront_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->clkdev = dev;
+	dev_set_drvdata(&dev->dev, info);
+
+	for (i = 0; i < ARRAY_SIZE(info->comp); i++)
+		init_completion(&(info->comp[i].completion));
+
+	ginfo = info;
+
+	return 0;
+}
+
+static void xen_clkfront_destroy_rings(struct xen_clkfront_info *info)
+{
+	if (info->irq)
+		unbind_from_irqhandler(info->irq, info);
+	info->irq = 0;
+
+	if (info->clk_ring_ref != GRANT_INVALID_REF) {
+		gnttab_end_foreign_access(info->clk_ring_ref, 0,
+					  (unsigned long)info->clk_ring.sring);
+		info->clk_ring_ref = GRANT_INVALID_REF;
+	}
+	info->clk_ring.sring = NULL;
+}
+
+static int xen_clkfront_handle_int(struct xen_clkfront_info *info)
+{
+	struct xen_clkif_response *res;
+	RING_IDX i, rp;
+	int more_to_do = 0;
+	unsigned long flags;
+	struct xen_clkfront_comp *comp;
+
+	spin_lock_irqsave(&info->lock, flags);
+	rp = info->clk_ring.sring->rsp_prod;
+	rmb(); /* ensure we see queued responses up to "rp" */
+
+	for (i = info->clk_ring.rsp_cons; i != rp; i++) {
+		res = RING_GET_RESPONSE(&info->clk_ring, i);
+		BUG_ON(res->id >= XENCLK_END);
+		comp = &info->comp[res->id];
+		comp->id = res->id;
+		comp->success = res->success;
+		comp->rate = res->rate;
+		strncpy(comp->clk_name, res->clk_name, sizeof(res->clk_name));
+		complete(&comp->completion);
+	}
+	info->clk_ring.rsp_cons = i;
+
+	if (i != info->clk_ring.req_prod_pvt)
+		RING_FINAL_CHECK_FOR_RESPONSES(&info->clk_ring, more_to_do);
+	else
+		info->clk_ring.sring->rsp_event = i + 1;
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	return more_to_do;
+}
+
+static irqreturn_t xen_clkfront_int(int irq, void *dev_id)
+{
+	struct xen_clkfront_info *info = dev_id;
+
+	while (xen_clkfront_handle_int(info))
+		cond_resched();
+
+	return IRQ_HANDLED;
+}
+
+static int xen_clkfront_setup_rings(struct xenbus_device *dev,
+				    struct xen_clkfront_info *info)
+{
+	struct xen_clkif_sring *clk_sring;
+	grant_ref_t gref;
+	int err;
+
+	info->clk_ring_ref = GRANT_INVALID_REF;
+
+	clk_sring = (struct xen_clkif_sring *)get_zeroed_page(
+						GFP_NOIO | __GFP_HIGH);
+	if (!clk_sring) {
+		xenbus_dev_fatal(dev, -ENOMEM, "allocating clk sring");
+		return -ENOMEM;
+	}
+
+	SHARED_RING_INIT(clk_sring);
+	FRONT_RING_INIT(&info->clk_ring, clk_sring, PAGE_SIZE);
+
+	err = xenbus_grant_ring(dev, clk_sring, 1, &gref);
+	if (err < 0) {
+		free_page((unsigned long)clk_sring);
+		info->clk_ring.sring = NULL;
+		goto fail;
+	}
+	info->clk_ring_ref = gref;
+
+	err = xenbus_alloc_evtchn(dev, &info->evtchn);
+	if (err) {
+		xenbus_dev_fatal(dev, err, "xenbus_alloc_evtchn");
+		goto fail;
+	}
+
+	err = bind_evtchn_to_irqhandler(info->evtchn, xen_clkfront_int, 0,
+					"xen_clkif", info);
+	if (err <= 0) {
+		xenbus_dev_fatal(dev, err, "bind_evtchn_to_irqhandler failed");
+		goto fail;
+	}
+
+	info->irq = err;
+
+	return 0;
+
+fail:
+	xen_clkfront_destroy_rings(info);
+	return err;
+}
+
+int xen_clkfront_wait_response(int id, const char *name, unsigned long *rate)
+{
+	struct xen_clkfront_info *info = ginfo;
+	struct xen_clkfront_comp *comp = &info->comp[id];
+
+	if (!ginfo) {
+		pr_err("Not initialized\n");
+		return -EIO;
+	}
+
+	wait_for_completion(&comp->completion);
+
+	if ((id == comp->id) && !strncmp(name, comp->clk_name, sizeof(comp->clk_name))) {
+		if (rate)
+			*rate = comp->rate;
+		return 0;
+	}
+
+	return -EIO;
+}
+
+int xen_clkfront_do_request(int id, const char *name, unsigned long rate)
+{
+	struct xen_clkfront_info *info = ginfo;
+	struct xen_clkif_request *req;
+	int notify;
+
+	if (!info) {
+		pr_err("Not initialized\n");
+		return -EIO;
+	}
+
+	req = RING_GET_REQUEST(&info->clk_ring, info->clk_ring.req_prod_pvt);
+	req->id = id;
+	req->rate = rate;
+	strncpy(req->clk_name, name, sizeof(req->clk_name));
+
+	info->clk_ring.req_prod_pvt++;
+	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->clk_ring, notify);
+
+	if (notify)
+		notify_remote_via_irq(info->irq);
+
+	return 0;
+}
+
+static int xen_clkfront_connect(struct xenbus_device *dev)
+{
+	struct xen_clkfront_info *info = dev_get_drvdata(&dev->dev);
+	struct xenbus_transaction xbt;
+	int err;
+	char *message;
+
+	err = xen_clkfront_setup_rings(dev, info);
+	if (err) {
+		pr_err("%s: failure....", __func__);
+		return err;
+	}
+again:
+	err = xenbus_transaction_start(&xbt);
+	if (err) {
+		xenbus_dev_fatal(dev, err, "starting transaction");
+		goto destroy_ring;
+	}
+
+	err = xenbus_printf(xbt, dev->nodename, "clk-ring-ref", "%u",
+			    info->clk_ring_ref);
+	if (err) {
+		message = "writing clk-ring-ref";
+		goto abort_transaction;
+	}
+
+	err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+			    info->evtchn);
+	if (err) {
+		message = "writing event-channel";
+		goto abort_transaction;
+	}
+
+	err = xenbus_transaction_end(xbt, 0);
+	if (err) {
+		if (err == -EAGAIN)
+			goto again;
+		xenbus_dev_fatal(dev, err, "completing transaction");
+		goto destroy_ring;
+	}
+
+	return 0;
+
+abort_transaction:
+	xenbus_transaction_end(xbt, 1);
+	xenbus_dev_fatal(dev, err, "%s", message);
+
+destroy_ring:
+	xen_clkfront_destroy_rings(info);
+
+	return err;
+}
+
+static void xen_clkfront_disconnect(struct xenbus_device *dev)
+{
+	xenbus_frontend_closed(dev);
+}
+
+static void xen_clkfront_backend_changed(struct xenbus_device *dev,
+					 enum xenbus_state backend_state)
+{
+	switch (backend_state) {
+	case XenbusStateInitialising:
+	case XenbusStateReconfiguring:
+	case XenbusStateReconfigured:
+	case XenbusStateUnknown:
+		break;
+
+	case XenbusStateInitWait:
+	case XenbusStateInitialised:
+	case XenbusStateConnected:
+		if (dev->state != XenbusStateInitialising)
+			break;
+		if (!xen_clkfront_connect(dev))
+			xenbus_switch_state(dev, XenbusStateConnected);
+		break;
+
+	case XenbusStateClosed:
+		if (dev->state == XenbusStateClosed)
+			break;
+		/* Missed the backend's Closing state -- fallthrough */
+	case XenbusStateClosing:
+		xen_clkfront_disconnect(dev);
+		break;
+
+	default:
+		xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
+				 backend_state);
+		break;
+	}
+}
+
+static int xen_clkfront_remove(struct xenbus_device *dev)
+{
+	struct xen_clkfront_info *info = dev_get_drvdata(&dev->dev);
+
+	xen_clkfront_destroy_rings(info);
+
+	kfree(info);
+
+	ginfo = NULL;
+
+	return -EINVAL;
+}
+
+static struct xenbus_driver xen_clkfront_driver = {
+	.ids = xen_clkfront_ids,
+	.probe = xen_clkfront_probe,
+	.otherend_changed = xen_clkfront_backend_changed,
+	.remove = xen_clkfront_remove,
+};
+
+static int __init xen_clkfront_init(void)
+{
+	struct device_node *np;
+	int nr, ret;
+	const char **clks_name;
+
+	if (!xen_domain())
+		return -ENODEV;
+
+	np = of_find_compatible_node(NULL, NULL, "xen,xen-clk");
+	if (!np) {
+		printk("error node\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_count_strings(np, "clock-output-names");
+	if (ret <= 0) {
+		of_node_put(np);
+		return ret;
+	}
+
+	nr = ret;
+
+	clks_name = kzalloc(sizeof(char *) * nr, GFP_KERNEL);
+	if (!clks_name)
+		return -ENOMEM;
+
+	ret = of_property_read_string_array(np, "clock-output-names",
+					    clks_name, nr);
+
+	if (ret < 0)
+		goto free_clks_name;
+
+	clks = kzalloc(sizeof(struct clk *) * nr, GFP_KERNEL);
+	if (!clks) {
+		ret = PTR_ERR(clks);
+		goto free_clks_name;
+	}
+
+	ret = xen_clkfront_register(nr, clks_name);
+	if (ret != 0)
+		goto free_clks;
+
+	clk_data.clks = clks;
+	clk_data.clk_num = nr;
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+
+	ret = xenbus_register_frontend(&xen_clkfront_driver);
+	if (ret) {
+		pr_err("register frontend failure\n");
+		goto free_clks;
+	}
+
+	of_node_put(np);
+
+	return 0;
+
+free_clks:
+	xen_clkfront_deregister(nr);
+	kfree(clks);
+free_clks_name:
+	of_node_put(np);
+	kfree(clks_name);
+	return ret;
+}
+subsys_initcall(xen_clkfront_init);
diff --git a/include/xen/interface/io/clkif.h b/include/xen/interface/io/clkif.h
new file mode 100644
index 0000000..d5e3616
--- /dev/null
+++ b/include/xen/interface/io/clkif.h
@@ -0,0 +1,41 @@ 
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __XEN_PUBLIC_IO_CLKIF_H__
+#define __XEN_PUBLIC_IO_CLKIF_H__
+
+#include <xen/interface/io/ring.h>
+#include <xen/interface/grant_table.h>
+
+/**/
+enum {
+	XENCLK_PREPARE,		/* clk_prepare_enable */
+	XENCLK_UNPREPARE,	/* clk_unprepare_disable */
+	XENCLK_GET_RATE,	/* clk_get_rate */
+	XENCLK_SET_RATE,	/* clk_set_rate */
+	XENCLK_END,
+};
+
+struct xen_clkif_request {
+	int id;
+	unsigned long rate;
+	char clk_name[32];
+};
+
+struct xen_clkif_response {
+	int id;
+	int success;
+	unsigned long rate;
+	char clk_name[32];
+};
+
+DEFINE_RING_TYPES(xen_clkif, struct xen_clkif_request, struct xen_clkif_response);
+#define XEN_CLK_RING_SIZE __CONST_RING_SIZE(xen_clkif, PAGE_SIZE)
+
+#endif