All of lore.kernel.org
 help / color / mirror / Atom feed
From: emilio@elopez.com.ar (Emilio López)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH RFC] clk: Introduce userspace clock driver
Date: Fri, 10 May 2013 14:44:44 -0300	[thread overview]
Message-ID: <518D320C.4010707@elopez.com.ar> (raw)
In-Reply-To: <1368207091-32538-2-git-send-email-soren.brinkmann@xilinx.com>

Hi,

El 10/05/13 14:31, Soren Brinkmann escribi?:
> The userspace clock driver can be used to expose clock controls through
> sysfs to userspace. The driver creates entries in /sys/class/clk.
> 
> Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
> ---
>  .../devicetree/bindings/clock/clk-userspace.txt    |   7 +
>  drivers/clk/Kconfig                                |   9 ++
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/clk-userspace.c                        | 169 +++++++++++++++++++++
>  4 files changed, 186 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/clk-userspace.txt
>  create mode 100644 drivers/clk/clk-userspace.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/clk-userspace.txt b/Documentation/devicetree/bindings/clock/clk-userspace.txt
> new file mode 100644
> index 0000000..2d153c7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/clk-userspace.txt
> @@ -0,0 +1,7 @@
> +
> +Example:
> +	usclk: usclk {
> +		compatible = "clk-userspace";
> +		clocks = <&foo 15>, <&bar>;
> +		clock-count = <2>;
> +	};

Does this belong on DT? It isn't describing hardware, is it?

> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 0357ac4..b35b62c 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -81,6 +81,15 @@ config COMMON_CLK_AXI_CLKGEN
>  	  Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
>  	  FPGAs. It is commonly used in Analog Devices' reference designs.
>  
> +config COMMON_CLK_USERSPACE
> +	bool "Userspace Clock Controls"
> +	depends on OF
> +	depends on SYSFS
> +	help
> +	---help---
> +	  Expose clock controls through sysfs to userspace. Clocks are selected
> +	  through the device tree and the controls are exposed in
> +	  /sys/class/clk.
>  endmenu
>  
>  source "drivers/clk/mvebu/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index fa435bc..f2f68c8 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-composite.o
> +obj-$(CONFIG_COMMON_CLK_USERSPACE) += clk-userspace.o
>  
>  # SoCs specific
>  obj-$(CONFIG_ARCH_BCM2835)	+= clk-bcm2835.o
> diff --git a/drivers/clk/clk-userspace.c b/drivers/clk/clk-userspace.c
> new file mode 100644
> index 0000000..931cf92
> --- /dev/null
> +++ b/drivers/clk/clk-userspace.c
> @@ -0,0 +1,169 @@
> +/*
> + * Userspace clock driver
> + *
> + *  Copyright (C) 2013 Xilinx
> + *
> + *  S?ren Brinkmann <soren.brinkmann@xilinx.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License v2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Expose clock controls through sysfs to userspace.
> + *
> + * By writing 0/1 to 'enable' the clock can be disabled/enabled. Reading
> + * that file returns the current state - 0 = disabled, 1 = enabled.
> + *
> + * Reading 'set_rate' returns the current clock frequency in Hz. Writing
> + * the file requests setting a new frequency in Hz.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +
> +#define DRIVER_NAME	"clk-userspace"
> +
> +struct usclk_data {
> +	struct clk *clk;
> +	int enabled;
> +};
> +
> +static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->enabled);
> +}
> +
> +static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
> +		const char *buf, size_t count)
> +{
> +	unsigned long enable;
> +	int ret;
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	ret = kstrtoul(buf, 0, &enable);
> +	if (ret)
> +		return -EINVAL;
> +
> +	enable = !!enable;
> +	if (enable == pdata->enabled)
> +		return count;
> +
> +	if (enable)
> +		ret = clk_prepare_enable(pdata->clk);
> +	else
> +		clk_disable_unprepare(pdata->clk);
> +
> +	if (ret)
> +		return -EBUSY;
> +
> +	pdata->enabled = enable;
> +	return count;
> +}
> +
> +static DEVICE_ATTR(enable, 0644, enable_show, enable_store);
> +
> +static ssize_t set_rate_show(struct device *dev, struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lu\n", clk_get_rate(pdata->clk));
> +}
> +
> +static ssize_t set_rate_store(struct device *dev, struct device_attribute *attr,
> +		const char *buf, size_t count)
> +{
> +	int ret = 0;
> +	unsigned long rate;
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	ret = kstrtoul(buf, 0, &rate);
> +	if (ret)
> +		return -EINVAL;
> +
> +	rate = clk_round_rate(pdata->clk, rate);
> +	ret = clk_set_rate(pdata->clk, rate);
> +	if (ret)
> +		return -EBUSY;
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(set_rate, 0644, set_rate_show, set_rate_store);
> +
> +static const struct attribute *usclk_attrs[] = {
> +	&dev_attr_enable.attr,
> +	&dev_attr_set_rate.attr,
> +	NULL
> +};

For debugging purposes, being able to change parents would be nice too.
Maybe this belongs to debugfs instead of sysfs though.

> +
> +static const struct attribute_group usclk_attr_grp = {
> +	.attrs = (struct attribute **)usclk_attrs,
> +};
> +
> +static int usclk_setup(void)
> +{
> +	int ret;
> +	int i;
> +	struct usclk_data *pdata;
> +	u32 clock_count;
> +	struct class *clk_class;
> +	struct device *dev;
> +	struct device_node *np = of_find_compatible_node(NULL, NULL,
> +			"clk-userspace");
> +
> +	ret = of_property_read_u32(np, "clock-count", &clock_count);
> +	if (ret || !clock_count)
> +		return ret;
> +
> +	pdata = kzalloc(clock_count * sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return -ENOMEM;
> +
> +	clk_class = class_create(THIS_MODULE, "clk");
> +	if (!clk_class) {
> +		pr_err("unable to create class\n");
> +		goto err_free;
> +	}
> +
> +	for (i = 0; i < clock_count; i++) {
> +		pdata[i].clk = of_clk_get(np, i);
> +		if (IS_ERR(pdata[i].clk)) {
> +			pr_warn("input clock #%u not found\n", i);
> +			continue;
> +		}
> +
> +		dev = device_create(clk_class, NULL, MKDEV(0, 0), NULL,
> +				of_clk_get_parent_name(np, i));
> +		if (!dev) {
> +			pr_warn("unable to create device #%d\n", i);
> +			continue;
> +		}
> +
> +		dev_set_drvdata(dev, &pdata[i]);
> +		sysfs_create_group(&dev->kobj, &usclk_attr_grp);
> +	}
> +
> +	return 0;
> +
> +err_free:
> +	kfree(pdata);
> +
> +	return ret;
> +}
> +late_initcall(usclk_setup);
> 

WARNING: multiple messages have this Message-ID (diff)
From: "Emilio López" <emilio@elopez.com.ar>
To: Soren Brinkmann <soren.brinkmann@xilinx.com>
Cc: Mike Turquette <mturquette@linaro.org>,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH RFC] clk: Introduce userspace clock driver
Date: Fri, 10 May 2013 14:44:44 -0300	[thread overview]
Message-ID: <518D320C.4010707@elopez.com.ar> (raw)
In-Reply-To: <1368207091-32538-2-git-send-email-soren.brinkmann@xilinx.com>

Hi,

El 10/05/13 14:31, Soren Brinkmann escribió:
> The userspace clock driver can be used to expose clock controls through
> sysfs to userspace. The driver creates entries in /sys/class/clk.
> 
> Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
> ---
>  .../devicetree/bindings/clock/clk-userspace.txt    |   7 +
>  drivers/clk/Kconfig                                |   9 ++
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/clk-userspace.c                        | 169 +++++++++++++++++++++
>  4 files changed, 186 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/clk-userspace.txt
>  create mode 100644 drivers/clk/clk-userspace.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/clk-userspace.txt b/Documentation/devicetree/bindings/clock/clk-userspace.txt
> new file mode 100644
> index 0000000..2d153c7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/clk-userspace.txt
> @@ -0,0 +1,7 @@
> +
> +Example:
> +	usclk: usclk {
> +		compatible = "clk-userspace";
> +		clocks = <&foo 15>, <&bar>;
> +		clock-count = <2>;
> +	};

Does this belong on DT? It isn't describing hardware, is it?

> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 0357ac4..b35b62c 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -81,6 +81,15 @@ config COMMON_CLK_AXI_CLKGEN
>  	  Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
>  	  FPGAs. It is commonly used in Analog Devices' reference designs.
>  
> +config COMMON_CLK_USERSPACE
> +	bool "Userspace Clock Controls"
> +	depends on OF
> +	depends on SYSFS
> +	help
> +	---help---
> +	  Expose clock controls through sysfs to userspace. Clocks are selected
> +	  through the device tree and the controls are exposed in
> +	  /sys/class/clk.
>  endmenu
>  
>  source "drivers/clk/mvebu/Kconfig"
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index fa435bc..f2f68c8 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_COMMON_CLK)	+= clk-fixed-rate.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-gate.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-mux.o
>  obj-$(CONFIG_COMMON_CLK)	+= clk-composite.o
> +obj-$(CONFIG_COMMON_CLK_USERSPACE) += clk-userspace.o
>  
>  # SoCs specific
>  obj-$(CONFIG_ARCH_BCM2835)	+= clk-bcm2835.o
> diff --git a/drivers/clk/clk-userspace.c b/drivers/clk/clk-userspace.c
> new file mode 100644
> index 0000000..931cf92
> --- /dev/null
> +++ b/drivers/clk/clk-userspace.c
> @@ -0,0 +1,169 @@
> +/*
> + * Userspace clock driver
> + *
> + *  Copyright (C) 2013 Xilinx
> + *
> + *  Sören Brinkmann <soren.brinkmann@xilinx.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License v2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Expose clock controls through sysfs to userspace.
> + *
> + * By writing 0/1 to 'enable' the clock can be disabled/enabled. Reading
> + * that file returns the current state - 0 = disabled, 1 = enabled.
> + *
> + * Reading 'set_rate' returns the current clock frequency in Hz. Writing
> + * the file requests setting a new frequency in Hz.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/fs.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/device.h>
> +#include <linux/slab.h>
> +
> +#define DRIVER_NAME	"clk-userspace"
> +
> +struct usclk_data {
> +	struct clk *clk;
> +	int enabled;
> +};
> +
> +static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->enabled);
> +}
> +
> +static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
> +		const char *buf, size_t count)
> +{
> +	unsigned long enable;
> +	int ret;
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	ret = kstrtoul(buf, 0, &enable);
> +	if (ret)
> +		return -EINVAL;
> +
> +	enable = !!enable;
> +	if (enable == pdata->enabled)
> +		return count;
> +
> +	if (enable)
> +		ret = clk_prepare_enable(pdata->clk);
> +	else
> +		clk_disable_unprepare(pdata->clk);
> +
> +	if (ret)
> +		return -EBUSY;
> +
> +	pdata->enabled = enable;
> +	return count;
> +}
> +
> +static DEVICE_ATTR(enable, 0644, enable_show, enable_store);
> +
> +static ssize_t set_rate_show(struct device *dev, struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	return scnprintf(buf, PAGE_SIZE, "%lu\n", clk_get_rate(pdata->clk));
> +}
> +
> +static ssize_t set_rate_store(struct device *dev, struct device_attribute *attr,
> +		const char *buf, size_t count)
> +{
> +	int ret = 0;
> +	unsigned long rate;
> +	struct usclk_data *pdata = dev_get_drvdata(dev);
> +
> +	ret = kstrtoul(buf, 0, &rate);
> +	if (ret)
> +		return -EINVAL;
> +
> +	rate = clk_round_rate(pdata->clk, rate);
> +	ret = clk_set_rate(pdata->clk, rate);
> +	if (ret)
> +		return -EBUSY;
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(set_rate, 0644, set_rate_show, set_rate_store);
> +
> +static const struct attribute *usclk_attrs[] = {
> +	&dev_attr_enable.attr,
> +	&dev_attr_set_rate.attr,
> +	NULL
> +};

For debugging purposes, being able to change parents would be nice too.
Maybe this belongs to debugfs instead of sysfs though.

> +
> +static const struct attribute_group usclk_attr_grp = {
> +	.attrs = (struct attribute **)usclk_attrs,
> +};
> +
> +static int usclk_setup(void)
> +{
> +	int ret;
> +	int i;
> +	struct usclk_data *pdata;
> +	u32 clock_count;
> +	struct class *clk_class;
> +	struct device *dev;
> +	struct device_node *np = of_find_compatible_node(NULL, NULL,
> +			"clk-userspace");
> +
> +	ret = of_property_read_u32(np, "clock-count", &clock_count);
> +	if (ret || !clock_count)
> +		return ret;
> +
> +	pdata = kzalloc(clock_count * sizeof(*pdata), GFP_KERNEL);
> +	if (!pdata)
> +		return -ENOMEM;
> +
> +	clk_class = class_create(THIS_MODULE, "clk");
> +	if (!clk_class) {
> +		pr_err("unable to create class\n");
> +		goto err_free;
> +	}
> +
> +	for (i = 0; i < clock_count; i++) {
> +		pdata[i].clk = of_clk_get(np, i);
> +		if (IS_ERR(pdata[i].clk)) {
> +			pr_warn("input clock #%u not found\n", i);
> +			continue;
> +		}
> +
> +		dev = device_create(clk_class, NULL, MKDEV(0, 0), NULL,
> +				of_clk_get_parent_name(np, i));
> +		if (!dev) {
> +			pr_warn("unable to create device #%d\n", i);
> +			continue;
> +		}
> +
> +		dev_set_drvdata(dev, &pdata[i]);
> +		sysfs_create_group(&dev->kobj, &usclk_attr_grp);
> +	}
> +
> +	return 0;
> +
> +err_free:
> +	kfree(pdata);
> +
> +	return ret;
> +}
> +late_initcall(usclk_setup);
> 


  reply	other threads:[~2013-05-10 17:44 UTC|newest]

Thread overview: 76+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-10 17:31 [PATCH RFC] User space clock driver Soren Brinkmann
2013-05-10 17:31 ` Soren Brinkmann
2013-05-10 17:31 ` [PATCH RFC] clk: Introduce userspace " Soren Brinkmann
2013-05-10 17:31   ` Soren Brinkmann
2013-05-10 17:44   ` Emilio López [this message]
2013-05-10 17:44     ` Emilio López
2013-05-10 18:15     ` Sören Brinkmann
2013-05-10 18:15       ` Sören Brinkmann
2013-05-10 18:49       ` Emilio López
2013-05-10 18:49         ` Emilio López
2013-05-10 22:18         ` Mike Turquette
2013-05-10 22:18           ` Mike Turquette
2013-05-10 23:01           ` Saravana Kannan
2013-05-10 23:01             ` Saravana Kannan
2013-05-10 23:06             ` Sören Brinkmann
2013-05-10 23:06               ` Sören Brinkmann
2013-05-10 23:25               ` Saravana Kannan
2013-05-10 23:25                 ` Saravana Kannan
2013-05-10 23:36                 ` Sören Brinkmann
2013-05-10 23:36                   ` Sören Brinkmann
2013-05-11 14:21             ` Mark Brown
2013-05-11 14:21               ` Mark Brown
2013-05-16  4:23               ` Saravana Kannan
2013-05-16  4:23                 ` Saravana Kannan
2013-05-16 18:21                 ` Mark Brown
2013-05-16 18:21                   ` Mark Brown
2013-05-10 23:08           ` Sören Brinkmann
2013-05-10 23:08             ` Sören Brinkmann
2013-05-13  8:31           ` Peter De Schrijver
2013-05-13  8:31             ` Peter De Schrijver
     [not found]   ` <CAHp75Vcr10d=XesGQvrC_v+ijdp3nK+m=w5E7d6GCo1Z9ogWnw@mail.gmail.com>
2013-05-10 18:03     ` Sören Brinkmann
2013-05-10 18:03       ` Sören Brinkmann
2013-05-10 21:24   ` Mark Brown
2013-05-10 21:24     ` Mark Brown
2013-05-11 16:54     ` Sören Brinkmann
2013-05-11 16:54       ` Sören Brinkmann
2013-05-12 14:33       ` Mark Brown
2013-05-12 14:33         ` Mark Brown
2013-05-12 19:05         ` Sören Brinkmann
2013-05-12 19:05           ` Sören Brinkmann
2013-05-13  5:21           ` Mark Brown
2013-05-13  5:21             ` Mark Brown
2013-05-13 16:09             ` Sören Brinkmann
2013-05-13 16:09               ` Sören Brinkmann
2013-05-13 16:21               ` Sebastian Hesselbarth
2013-05-13 16:21                 ` Sebastian Hesselbarth
2013-05-13 17:24                 ` Sören Brinkmann
2013-05-13 17:24                   ` Sören Brinkmann
2013-05-13 17:37                   ` Sebastian Hesselbarth
2013-05-13 17:37                     ` Sebastian Hesselbarth
2013-05-13 17:58                     ` Sören Brinkmann
2013-05-13 17:58                       ` Sören Brinkmann
2013-05-13 18:18                       ` Sebastian Hesselbarth
2013-05-13 18:18                         ` Sebastian Hesselbarth
2013-05-14 16:46                       ` Mike Turquette
2013-05-14 16:46                         ` Mike Turquette
2013-05-14 18:09                         ` Philip Balister
2013-05-14 18:09                           ` Philip Balister
2013-05-15  4:46                           ` Mark Brown
2013-05-15  4:46                             ` Mark Brown
2013-05-16  4:28                             ` Saravana Kannan
2013-05-16  4:28                               ` Saravana Kannan
2013-05-16 14:44                               ` Philip Balister
2013-05-16 14:44                                 ` Philip Balister
2013-05-16 17:26                                 ` Mark Brown
2013-05-16 17:26                                   ` Mark Brown
2013-05-16 18:55                                   ` Sören Brinkmann
2013-05-16 18:55                                     ` Sören Brinkmann
2013-05-17 11:02                                     ` Mark Brown
2013-05-17 11:02                                       ` Mark Brown
2013-05-13 18:16                 ` Mark Brown
2013-05-13 18:16                   ` Mark Brown
2013-05-13 18:20                   ` Sebastian Hesselbarth
2013-05-13 18:20                     ` Sebastian Hesselbarth
2013-05-13 18:44                     ` Mark Brown
2013-05-13 18:44                       ` Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=518D320C.4010707@elopez.com.ar \
    --to=emilio@elopez.com.ar \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.