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
