diff mbox

[08/10] ARM: clps711x: Add CLPS711X cpuidle driver

Message ID 1374172501-26796-9-git-send-email-shc_work@mail.ru (mailing list archive)
State New, archived
Headers show

Commit Message

Alexander Shiyan July 18, 2013, 6:34 p.m. UTC
This adds the cpuidle driver for Cirrus Logic CLPS711X series SoCs.
Designed primarily for migration CLPS711X subarch for multiplatform & DT.

Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
---
 drivers/cpuidle/Kconfig            |  6 +++
 drivers/cpuidle/Makefile           |  1 +
 drivers/cpuidle/cpuidle-clps711x.c | 80 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+)
 create mode 100644 drivers/cpuidle/cpuidle-clps711x.c

Comments

Daniel Lezcano July 20, 2013, 9:42 p.m. UTC | #1
On 07/18/2013 08:34 PM, Alexander Shiyan wrote:
> This adds the cpuidle driver for Cirrus Logic CLPS711X series SoCs.
> Designed primarily for migration CLPS711X subarch for multiplatform & DT.
> 
> Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
> ---
>  drivers/cpuidle/Kconfig            |  6 +++
>  drivers/cpuidle/Makefile           |  1 +
>  drivers/cpuidle/cpuidle-clps711x.c | 80 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 87 insertions(+)
>  create mode 100644 drivers/cpuidle/cpuidle-clps711x.c
> 
> diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
> index 0e2cd5c..c68a0cf 100644
> --- a/drivers/cpuidle/Kconfig
> +++ b/drivers/cpuidle/Kconfig
> @@ -36,6 +36,12 @@ config CPU_IDLE_CALXEDA
>  	help
>  	  Select this to enable cpuidle on Calxeda processors.
>  
> +config CPU_IDLE_CLPS711X
> +	bool "CPU Idle Driver for CLPS711X processors"
> +	depends on ARCH_CLPS711X
> +	help
> +	  Select this to enable cpuidle on Cirrus Logic CLPS711X processors.
> +
>  config CPU_IDLE_ZYNQ
>  	bool "CPU Idle Driver for Xilinx Zynq processors"
>  	depends on ARCH_ZYNQ
> diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
> index 8767a7b..d07d2cb 100644
> --- a/drivers/cpuidle/Makefile
> +++ b/drivers/cpuidle/Makefile
> @@ -6,5 +6,6 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
>  obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
>  
>  obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
> +obj-$(CONFIG_CPU_IDLE_CLPS711X)	+= cpuidle-clps711x.o
>  obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
>  obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o
> diff --git a/drivers/cpuidle/cpuidle-clps711x.c b/drivers/cpuidle/cpuidle-clps711x.c
> new file mode 100644
> index 0000000..9f0129f
> --- /dev/null
> +++ b/drivers/cpuidle/cpuidle-clps711x.c
> @@ -0,0 +1,80 @@
> +/*
> + *  CLPS711X CPU idle driver
> + *
> + *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/cpuidle.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +static void __iomem *clps711x_halt;
> +
> +static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
> +				 struct cpuidle_driver *drv, int index)
> +{
> +	writel(1, clps711x_halt);

In what the 'clps711x_halt' differs from the usual WFI (cpu_do_idle) ?

> +	asm volatile ("mov r0, r0");
> +	asm volatile ("mov r0, r0");

Why are needed these two volatile ?

> +	return index;
> +}
> +
> +static struct cpuidle_driver clps711x_idle_driver = {
> +	.name			= "clps711x_idle",
> +	.owner			= THIS_MODULE,
> +	.state_count		= 1,
> +	.states[0]	= {
> +		.name		= "HALT",
> +		.desc		= "CLPS711X WFI",
> +		.enter		= clps711x_cpuidle_halt,
> +		.exit_latency	= 1,
> +	},
> +};
> +
> +static int __init clps711x_cpuidle_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	clps711x_halt = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(clps711x_halt))
> +		return PTR_ERR(clps711x_halt);
> +
> +	return cpuidle_register(&clps711x_idle_driver, NULL);
> +}
> +
> +static int clps711x_cpuidle_remove(struct platform_device *pdev)
> +{
> +	cpuidle_unregister(&clps711x_idle_driver);
> +
> +	return 0;
> +}

The driver is not compiled as module, will this function be called ?

> +
> +static const struct of_device_id clps711x_cpuidle_dt_ids[] = {
> +	{ .compatible = "cirrus,clps711x-cpuidle", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, clps711x_cpuidle_dt_ids);
> +
> +static struct platform_driver clps711x_cpuidle_driver = {
> +	.driver	= {
> +		.name		= "clps711x-cpuidle",
> +		.owner		= THIS_MODULE,
> +		.of_match_table	= clps711x_cpuidle_dt_ids,
> +	},
> +	.remove	= clps711x_cpuidle_remove,
> +};
> +module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);

+1

> +
> +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
> +MODULE_DESCRIPTION("CLPS711X CPU idle driver");
> +MODULE_LICENSE("GPL");
Alexander Shiyan July 21, 2013, 4:11 a.m. UTC | #2
On Sat, 20 Jul 2013 23:42:11 +0200
Daniel Lezcano <daniel.lezcano@linaro.org> wrote:

> On 07/18/2013 08:34 PM, Alexander Shiyan wrote:
> > This adds the cpuidle driver for Cirrus Logic CLPS711X series SoCs.
> > Designed primarily for migration CLPS711X subarch for multiplatform & DT.
> > 
> > Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
> > ---
> >  drivers/cpuidle/Kconfig            |  6 +++
> >  drivers/cpuidle/Makefile           |  1 +
> >  drivers/cpuidle/cpuidle-clps711x.c | 80 ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 87 insertions(+)
> >  create mode 100644 drivers/cpuidle/cpuidle-clps711x.c
[...]
> > +static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
> > +				 struct cpuidle_driver *drv, int index)
> > +{
> > +	writel(1, clps711x_halt);
> 
> In what the 'clps711x_halt' differs from the usual WFI (cpu_do_idle) ?

AFAIK, ARM720T does not implement the WFI instruction.
"HALT" register in CLPS711X do the same:
"A write to this location will put the system into the Idle State by
halting the clock to the processor until an interrupt is generated."

> > +	asm volatile ("mov r0, r0");
> > +	asm volatile ("mov r0, r0");
> 
> Why are needed these two volatile ?

Two NOP instructions necessary following the enable or disable of the MMU.
Documentation not say anything about using this for "HALT", maybe it's the
remnants of the old code. I will remove it.

[...]
> > +static int clps711x_cpuidle_remove(struct platform_device *pdev)
> > +{
> > +	cpuidle_unregister(&clps711x_idle_driver);
> > +
> > +	return 0;
> > +}
> 
> The driver is not compiled as module, will this function be called ?

You're right. I will remove it.

> > +static const struct of_device_id clps711x_cpuidle_dt_ids[] = {
> > +	{ .compatible = "cirrus,clps711x-cpuidle", },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, clps711x_cpuidle_dt_ids);
> > +
> > +static struct platform_driver clps711x_cpuidle_driver = {
> > +	.driver	= {
> > +		.name		= "clps711x-cpuidle",
> > +		.owner		= THIS_MODULE,
> > +		.of_match_table	= clps711x_cpuidle_dt_ids,
> > +	},
> > +	.remove	= clps711x_cpuidle_remove,
> > +};
> > +module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
> 
> +1

??? What here?

[...]
Daniel Lezcano July 21, 2013, 8:32 a.m. UTC | #3
On 07/21/2013 06:11 AM, Alexander Shiyan wrote:
> On Sat, 20 Jul 2013 23:42:11 +0200
> Daniel Lezcano <daniel.lezcano@linaro.org> wrote:
> 
>> On 07/18/2013 08:34 PM, Alexander Shiyan wrote:
>>> This adds the cpuidle driver for Cirrus Logic CLPS711X series SoCs.
>>> Designed primarily for migration CLPS711X subarch for multiplatform & DT.
>>>
>>> Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
>>> ---
>>>  drivers/cpuidle/Kconfig            |  6 +++
>>>  drivers/cpuidle/Makefile           |  1 +
>>>  drivers/cpuidle/cpuidle-clps711x.c | 80 ++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 87 insertions(+)
>>>  create mode 100644 drivers/cpuidle/cpuidle-clps711x.c
> [...]
>>> +static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
>>> +				 struct cpuidle_driver *drv, int index)
>>> +{
>>> +	writel(1, clps711x_halt);
>>
>> In what the 'clps711x_halt' differs from the usual WFI (cpu_do_idle) ?
> 
> AFAIK, ARM720T does not implement the WFI instruction.
> "HALT" register in CLPS711X do the same:
> "A write to this location will put the system into the Idle State by
> halting the clock to the processor until an interrupt is generated."

I am wondering if you are not using more states, may be you can consider
to add a clps711x idle function for the 'arm_pm_idle' callback instead
of creating a new driver.

>>> +	asm volatile ("mov r0, r0");
>>> +	asm volatile ("mov r0, r0");
>>
>> Why are needed these two volatile ?
> 
> Two NOP instructions necessary following the enable or disable of the MMU.
> Documentation not say anything about using this for "HALT", maybe it's the
> remnants of the old code. I will remove it.

Ok.

> [...]
>>> +static int clps711x_cpuidle_remove(struct platform_device *pdev)
>>> +{
>>> +	cpuidle_unregister(&clps711x_idle_driver);
>>> +
>>> +	return 0;
>>> +}
>>
>> The driver is not compiled as module, will this function be called ?
> 
> You're right. I will remove it.
> 
>>> +static const struct of_device_id clps711x_cpuidle_dt_ids[] = {
>>> +	{ .compatible = "cirrus,clps711x-cpuidle", },
>>> +	{ }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, clps711x_cpuidle_dt_ids);
>>> +
>>> +static struct platform_driver clps711x_cpuidle_driver = {
>>> +	.driver	= {
>>> +		.name		= "clps711x-cpuidle",
>>> +		.owner		= THIS_MODULE,
>>> +		.of_match_table	= clps711x_cpuidle_dt_ids,
>>> +	},
>>> +	.remove	= clps711x_cpuidle_remove,
>>> +};
>>> +module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
>>
>> +1
> 
> ??? What here?

I like the approach :)

Thanks
  -- Daniel
Alexander Shiyan July 21, 2013, 10:04 a.m. UTC | #4
On Sun, 21 Jul 2013 10:32:31 +0200
Daniel Lezcano <daniel.lezcano@linaro.org> wrote:

> On 07/21/2013 06:11 AM, Alexander Shiyan wrote:
> > On Sat, 20 Jul 2013 23:42:11 +0200
> > Daniel Lezcano <daniel.lezcano@linaro.org> wrote:
> > 
> >> On 07/18/2013 08:34 PM, Alexander Shiyan wrote:
> >>> This adds the cpuidle driver for Cirrus Logic CLPS711X series SoCs.
> >>> Designed primarily for migration CLPS711X subarch for multiplatform & DT.
> >>>
> >>> Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
> >>> ---
> >>>  drivers/cpuidle/Kconfig            |  6 +++
> >>>  drivers/cpuidle/Makefile           |  1 +
> >>>  drivers/cpuidle/cpuidle-clps711x.c | 80 ++++++++++++++++++++++++++++++++++++++
> >>>  3 files changed, 87 insertions(+)
> >>>  create mode 100644 drivers/cpuidle/cpuidle-clps711x.c
> > [...]
> >>> +static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
> >>> +				 struct cpuidle_driver *drv, int index)
> >>> +{
> >>> +	writel(1, clps711x_halt);
> >>
> >> In what the 'clps711x_halt' differs from the usual WFI (cpu_do_idle) ?
> > 
> > AFAIK, ARM720T does not implement the WFI instruction.
> > "HALT" register in CLPS711X do the same:
> > "A write to this location will put the system into the Idle State by
> > halting the clock to the processor until an interrupt is generated."
> 
> I am wondering if you are not using more states, may be you can consider
> to add a clps711x idle function for the 'arm_pm_idle' callback instead
> of creating a new driver.

Currently, this is exactly what is done :)
But I really would like to further add a second mode (STDBY),
which allow to use all PM features.
At this time add this mode is too hard, because there are some barriers
for this, which will be resolved when all drivers for this platform will
contains DT-support.

[...]
Russell King - ARM Linux July 22, 2013, 11:40 a.m. UTC | #5
On Sun, Jul 21, 2013 at 08:11:38AM +0400, Alexander Shiyan wrote:
> > > +static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
> > > +				 struct cpuidle_driver *drv, int index)
> > > +{
> > > +	writel(1, clps711x_halt);
> > 
> > In what the 'clps711x_halt' differs from the usual WFI (cpu_do_idle) ?
> 
> AFAIK, ARM720T does not implement the WFI instruction.
> "HALT" register in CLPS711X do the same:
> "A write to this location will put the system into the Idle State by
> halting the clock to the processor until an interrupt is generated."
> 
> > > +	asm volatile ("mov r0, r0");
> > > +	asm volatile ("mov r0, r0");
> > 
> > Why are needed these two volatile ?
> 
> Two NOP instructions necessary following the enable or disable of the MMU.
> Documentation not say anything about using this for "HALT", maybe it's the
> remnants of the old code. I will remove it.

However, bear in mind that this is not how you insert two nops after
something; the compiler is free to add anything it likes before,
between separate asm() statements, or after them, even if they're
marked volatile.
Mark Rutland Aug. 2, 2013, 11:10 a.m. UTC | #6
On Thu, Jul 18, 2013 at 07:34:59PM +0100, Alexander Shiyan wrote:
> This adds the cpuidle driver for Cirrus Logic CLPS711X series SoCs.
> Designed primarily for migration CLPS711X subarch for multiplatform & DT.
> 
> Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
> ---
>  drivers/cpuidle/Kconfig            |  6 +++
>  drivers/cpuidle/Makefile           |  1 +
>  drivers/cpuidle/cpuidle-clps711x.c | 80 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 87 insertions(+)
>  create mode 100644 drivers/cpuidle/cpuidle-clps711x.c
> 
> diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
> index 0e2cd5c..c68a0cf 100644
> --- a/drivers/cpuidle/Kconfig
> +++ b/drivers/cpuidle/Kconfig
> @@ -36,6 +36,12 @@ config CPU_IDLE_CALXEDA
>  	help
>  	  Select this to enable cpuidle on Calxeda processors.
>  
> +config CPU_IDLE_CLPS711X
> +	bool "CPU Idle Driver for CLPS711X processors"
> +	depends on ARCH_CLPS711X
> +	help
> +	  Select this to enable cpuidle on Cirrus Logic CLPS711X processors.
> +
>  config CPU_IDLE_ZYNQ
>  	bool "CPU Idle Driver for Xilinx Zynq processors"
>  	depends on ARCH_ZYNQ
> diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
> index 8767a7b..d07d2cb 100644
> --- a/drivers/cpuidle/Makefile
> +++ b/drivers/cpuidle/Makefile
> @@ -6,5 +6,6 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
>  obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
>  
>  obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
> +obj-$(CONFIG_CPU_IDLE_CLPS711X)	+= cpuidle-clps711x.o
>  obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
>  obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o
> diff --git a/drivers/cpuidle/cpuidle-clps711x.c b/drivers/cpuidle/cpuidle-clps711x.c
> new file mode 100644
> index 0000000..9f0129f
> --- /dev/null
> +++ b/drivers/cpuidle/cpuidle-clps711x.c
> @@ -0,0 +1,80 @@
> +/*
> + *  CLPS711X CPU idle driver
> + *
> + *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/cpuidle.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +static void __iomem *clps711x_halt;
> +
> +static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
> +				 struct cpuidle_driver *drv, int index)
> +{
> +	writel(1, clps711x_halt);
> +	asm volatile ("mov r0, r0");
> +	asm volatile ("mov r0, r0");

Huh?

> +
> +	return index;
> +}
> +
> +static struct cpuidle_driver clps711x_idle_driver = {
> +	.name			= "clps711x_idle",
> +	.owner			= THIS_MODULE,
> +	.state_count		= 1,
> +	.states[0]	= {
> +		.name		= "HALT",
> +		.desc		= "CLPS711X WFI",
> +		.enter		= clps711x_cpuidle_halt,
> +		.exit_latency	= 1,
> +	},
> +};
> +
> +static int __init clps711x_cpuidle_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	clps711x_halt = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(clps711x_halt))
> +		return PTR_ERR(clps711x_halt);
> +
> +	return cpuidle_register(&clps711x_idle_driver, NULL);
> +}
> +
> +static int clps711x_cpuidle_remove(struct platform_device *pdev)
> +{
> +	cpuidle_unregister(&clps711x_idle_driver);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id clps711x_cpuidle_dt_ids[] = {
> +	{ .compatible = "cirrus,clps711x-cpuidle", },
> +	{ }
> +};

I don't like this -- cpuidle is a Linux abstraction, not a real device.
Additionally you haven't provided a binding.

What is the device being poked actually called? Is it a register in a
larger power management unit?

Thanks,
Mark.

> +MODULE_DEVICE_TABLE(of, clps711x_cpuidle_dt_ids);
> +
> +static struct platform_driver clps711x_cpuidle_driver = {
> +	.driver	= {
> +		.name		= "clps711x-cpuidle",
> +		.owner		= THIS_MODULE,
> +		.of_match_table	= clps711x_cpuidle_dt_ids,
> +	},
> +	.remove	= clps711x_cpuidle_remove,
> +};
> +module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
> +
> +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
> +MODULE_DESCRIPTION("CLPS711X CPU idle driver");
> +MODULE_LICENSE("GPL");
> -- 
> 1.8.1.5
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
diff mbox

Patch

diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index 0e2cd5c..c68a0cf 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -36,6 +36,12 @@  config CPU_IDLE_CALXEDA
 	help
 	  Select this to enable cpuidle on Calxeda processors.
 
+config CPU_IDLE_CLPS711X
+	bool "CPU Idle Driver for CLPS711X processors"
+	depends on ARCH_CLPS711X
+	help
+	  Select this to enable cpuidle on Cirrus Logic CLPS711X processors.
+
 config CPU_IDLE_ZYNQ
 	bool "CPU Idle Driver for Xilinx Zynq processors"
 	depends on ARCH_ZYNQ
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 8767a7b..d07d2cb 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -6,5 +6,6 @@  obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
 obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
 
 obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
+obj-$(CONFIG_CPU_IDLE_CLPS711X)	+= cpuidle-clps711x.o
 obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
 obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o
diff --git a/drivers/cpuidle/cpuidle-clps711x.c b/drivers/cpuidle/cpuidle-clps711x.c
new file mode 100644
index 0000000..9f0129f
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-clps711x.c
@@ -0,0 +1,80 @@ 
+/*
+ *  CLPS711X CPU idle driver
+ *
+ *  Copyright (C) 2013 Alexander Shiyan <shc_work@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/cpuidle.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+static void __iomem *clps711x_halt;
+
+static int clps711x_cpuidle_halt(struct cpuidle_device *dev,
+				 struct cpuidle_driver *drv, int index)
+{
+	writel(1, clps711x_halt);
+	asm volatile ("mov r0, r0");
+	asm volatile ("mov r0, r0");
+
+	return index;
+}
+
+static struct cpuidle_driver clps711x_idle_driver = {
+	.name			= "clps711x_idle",
+	.owner			= THIS_MODULE,
+	.state_count		= 1,
+	.states[0]	= {
+		.name		= "HALT",
+		.desc		= "CLPS711X WFI",
+		.enter		= clps711x_cpuidle_halt,
+		.exit_latency	= 1,
+	},
+};
+
+static int __init clps711x_cpuidle_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	clps711x_halt = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(clps711x_halt))
+		return PTR_ERR(clps711x_halt);
+
+	return cpuidle_register(&clps711x_idle_driver, NULL);
+}
+
+static int clps711x_cpuidle_remove(struct platform_device *pdev)
+{
+	cpuidle_unregister(&clps711x_idle_driver);
+
+	return 0;
+}
+
+static const struct of_device_id clps711x_cpuidle_dt_ids[] = {
+	{ .compatible = "cirrus,clps711x-cpuidle", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, clps711x_cpuidle_dt_ids);
+
+static struct platform_driver clps711x_cpuidle_driver = {
+	.driver	= {
+		.name		= "clps711x-cpuidle",
+		.owner		= THIS_MODULE,
+		.of_match_table	= clps711x_cpuidle_dt_ids,
+	},
+	.remove	= clps711x_cpuidle_remove,
+};
+module_platform_driver_probe(clps711x_cpuidle_driver, clps711x_cpuidle_probe);
+
+MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
+MODULE_DESCRIPTION("CLPS711X CPU idle driver");
+MODULE_LICENSE("GPL");