From: "Hans J. Koch" <hjk@linutronix.de>
To: Davide Rizzo <elpa.rizzo@gmail.com>
Cc: linux-kernel@vger.kernel.org, hjk@linutronix.de, gregkh@suse.de,
ben-linux@fluff.org
Subject: Re: [PATCH 1/3] Driver for user access to internal clocks
Date: Thu, 8 Jan 2009 12:27:42 +0100 [thread overview]
Message-ID: <20090108112741.GA3053@local> (raw)
In-Reply-To: <8447d6730901080149l4146139an9fad00fd88d72fb9@mail.gmail.com>
On Thu, Jan 08, 2009 at 10:49:12AM +0100, Davide Rizzo wrote:
> This driver is for user level programs to interact with system clocks.
> It allows to read and modify rates and parents, using virtual files.
> It requires the implementation of 2 additional functions in the clk interface:
> clk_for_each() and clk_name().
> Actually I implemented that functions only for Samsung S3C24xx platform.
>
> Signed-off-by: Davide Rizzo <elpa-rizzo@gmail.com>
> ---
> diff -urNp linux-2.6.28/drivers/uio/clock.c
> linux-2.6.28.elpa/drivers/uio/clock.c
> --- linux-2.6.28/drivers/uio/clock.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.28.elpa/drivers/uio/clock.c 2009-01-07 18:52:09.000000000 +0100
Davide,
please don't put this in the drivers/uio/ directory. This is no UIO driver.
Thanks,
Hans
> @@ -0,0 +1,313 @@
> +/*
> + driver/misc/clock.c
Ah, it used to have a different place ;-)
> +
> + Written Feb 2008 by Davide Rizzo <davide@elpa.it>
> +
> + This driver allows to read and modify internal clocks' rates using
> + virtual files. User can also read and modify parents.
> +
> + This driver requires implementation of clk_name() and clk_enum() functions
> + in architecture specific clock.c
> +
> + 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.
> +
> + 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, write to the Free Software
> + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> +*/
> +
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +
> +struct clk_info {
> + struct mutex mutex;
> + struct list_head head;
> + struct device *dev; /* here because needed by create_clock_attr */
> + int count;
> +};
> +
> +struct attr {
> + struct list_head list;
> + struct device_attribute at;
> +};
> +
> +static ssize_t clk_show(struct device *dev, struct device_attribute *attr,
> + char *buffer)
> +{
> + unsigned long rate;
> + struct clk_info *info = dev_get_drvdata(dev);
> + struct clk *clk = clk_get(dev, attr->attr.name);
> +
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + rate = 0;
> +
> + if (!IS_ERR(clk)) {
> +
> + mutex_lock(&info->mutex);
> +
> + rate = clk_get_rate(clk);
> +
> + mutex_unlock(&info->mutex);
> +
> + clk_put(clk);
> +
> + }
> + if (snprintf(buffer, PAGE_SIZE, "%ld\n", rate) > PAGE_SIZE)
> + buffer[PAGE_SIZE - 1] = '\0';
> + return strlen(buffer);
> +}
> +
> +static ssize_t clk_store(struct device *dev, struct device_attribute *attr,
> + const char *buffer, size_t count)
> +{
> + struct clk_info *info = dev_get_drvdata(dev);
> + unsigned long rate;
> + struct clk *clk;
> + int err = strict_strtoul(buffer, 10, &rate);
> +
> + if (err)
> + return err;
> +
> + clk = clk_get(dev, attr->attr.name);
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + mutex_lock(&info->mutex);
> +
> + if (rate != 0) {
> + clk_set_rate(clk, clk_round_rate(clk, rate));
> + clk_enable(clk);
> + } else
> + clk_disable(clk);
> +
> + mutex_unlock(&info->mutex);
> +
> + clk_put(clk);
> +
> + return count;
> +}
> +
> +static ssize_t clk_parent_show(struct device *dev,
> + struct device_attribute *attr, char *buffer)
> +{
> + struct clk_info *info = dev_get_drvdata(dev);
> + struct clk *clk, *parent;
> + char *s = kstrdup(attr->attr.name, GFP_KERNEL);
> + if (!s)
> + return -ENOMEM;
> + s[strlen(attr->attr.name) - 7] = '\0';
> + clk = clk_get(dev, s);
> + kfree(s);
> + buffer[0] = '\0';
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> + if (!IS_ERR(clk)) {
> +
> + mutex_lock(&info->mutex);
> +
> + parent = clk_get_parent(clk);
> + if (parent && !IS_ERR(parent)) {
> + const char *full_name = clk_get_name(parent);
> + if (IS_ERR(full_name)) {
> + mutex_unlock(&info->mutex);
> + return PTR_ERR(full_name);
> + }
> + strlcpy(buffer, full_name, PAGE_SIZE);
> + kfree(full_name);
> + }
> + strlcat(buffer, "\n", PAGE_SIZE);
> +
> + mutex_unlock(&info->mutex);
> +
> + clk_put(clk);
> + }
> + return strlen(buffer);
> +}
> +
> +static ssize_t clk_parent_store(struct device *dev,
> + struct device_attribute *attr, const char *buffer, size_t count)
> +{
> + struct clk_info *info = dev_get_drvdata(dev);
> + struct clk *clk, *parent;
> + char *s;
> +
> + parent = clk_get(dev, buffer);
> + if (IS_ERR(parent))
> + return PTR_ERR(parent);
> +
> + s = kstrdup(attr->attr.name, GFP_KERNEL);
> + if (!s)
> + return -ENOMEM;
> + s[strlen(attr->attr.name) - 7] = '\0';
> + clk = clk_get(dev, s);
> + kfree(s);
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + if (!IS_ERR(clk)) {
> +
> + mutex_lock(&info->mutex);
> +
> + clk_set_parent(clk, parent);
> +
> + mutex_unlock(&info->mutex);
> +
> + clk_put(clk);
> + }
> + clk_put(parent);
> + return count;
> +}
> +
> +static int remove_driver(struct platform_device *pdev)
> +{
> + struct clk_info *info = platform_get_drvdata(pdev);
> + struct attr *atp;
> +
> + mutex_lock(&info->mutex);
> +
> + /* Remove all virtual files */
> + while (!list_empty(&info->head)) {
> + atp = list_first_entry(&info->head, struct attr, list);
> + device_remove_file(&pdev->dev, &atp->at);
> + kfree(atp->at.attr.name);
> + list_del(&atp->list);
> + kfree(atp);
> + }
> + platform_set_drvdata(pdev, NULL);
> +
> + mutex_unlock(&info->mutex);
> +
> + kfree(info);
> + dev_notice(&pdev->dev, "removed\n");
> + return 0;
> +}
> +
> +static int create_clock_attr(struct clk *clk, void *data)
> +{
> + struct attr *atp;
> + int err, len;
> + struct clk_info *info = data;
> + const char *name = clk_get_name(clk);
> +
> + /* Retrieve clock's name */
> + if (name == NULL || IS_ERR(name)) {
> + /* Don't return error, simply skip this one */
> + dev_err(info->dev, "invalid clock's name\n");
> + return PTR_ERR(name);
> + }
> +
> + /* Create rate virtual file */
> + atp = kzalloc(sizeof(struct attr), GFP_KERNEL);
> + if (!atp) {
> + dev_err(info->dev, "out of kernel memory\n");
> + return -ENOMEM;
> + }
> + atp->at.attr.mode = S_IRUGO | S_IWUSR;
> + atp->at.attr.name = kstrdup(name, GFP_KERNEL);
> + if (!atp->at.attr.name) {
> + kfree(atp);
> + dev_err(info->dev, "out of kernel memory\n");
> + return -ENOMEM;
> + }
> + atp->at.show = clk_show;
> + atp->at.store = clk_store;
> + err = device_create_file(info->dev, &atp->at);
> + if (err) {
> + kfree(atp->at.attr.name);
> + kfree(atp);
> + dev_err(info->dev, "failed to create file\n");
> + return err;
> + }
> + list_add(&atp->list, &info->head);
> +
> + /* Create parent virtual file */
> + atp = kzalloc(sizeof(struct attr), GFP_KERNEL);
> + if (!atp) {
> + dev_err(info->dev, "out of kernel memory\n");
> + return -ENOMEM;
> + }
> + atp->at.attr.mode = S_IRUGO | S_IWUSR;
> + len = strlen(name) + 8;
> + atp->at.attr.name = kmalloc(len, GFP_KERNEL);
> + if (!atp->at.attr.name) {
> + kfree(atp);
> + dev_err(info->dev, "out of kernel memory\n");
> + return -ENOMEM;
> + }
> + strlcpy((char *)atp->at.attr.name, name, len);
> + strlcat((char *)atp->at.attr.name, "-parent", len);
> + atp->at.show = clk_parent_show;
> + atp->at.store = clk_parent_store;
> + err = device_create_file(info->dev, &atp->at);
> + if (err) {
> + kfree(atp->at.attr.name);
> + kfree(atp);
> + dev_err(info->dev, "failed to create file\n");
> + return err;
> + }
> + list_add(&atp->list, &info->head);
> +
> + info->count++;
> + return 0;
> +}
> +
> +static int probe_driver(struct platform_device *pdev)
> +{
> + struct clk_info *info;
> + int err;
> +
> + info = kzalloc(sizeof(*info), GFP_KERNEL);
> + if (!info) {
> + dev_err(&pdev->dev, "out of kernel memory\n");
> + return -ENOMEM;
> + }
> + mutex_init(&info->mutex);
> + INIT_LIST_HEAD(&info->head);
> + info->dev = &pdev->dev;
> + platform_set_drvdata(pdev, info);
> + err = clk_for_each(create_clock_attr, info);
> + if (err)
> + remove_driver(pdev);
> + else
> + dev_notice(&pdev->dev, "probed OK - %d clocks recognized\n",
> + info->count);
> + return err;
> +}
> +
> +static struct platform_driver driver = {
> + .probe = probe_driver,
> + .remove = remove_driver,
> + .driver = {
> + .name = "clocks",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init clk_init_module(void)
> +{
> + return platform_driver_register(&driver);
> +}
> +
> +static void __exit clk_cleanup_module(void)
> +{
> + platform_driver_unregister(&driver);
> +}
> +
> +module_init(clk_init_module);
> +module_exit(clk_cleanup_module);
> +
> +MODULE_AUTHOR("Davide Rizzo <davide@elpa.it>");
> +MODULE_DESCRIPTION("Clock driver");
> +MODULE_SUPPORTED_DEVICE("clocks");
> +MODULE_LICENSE("GPL");
> diff -urNp linux-2.6.28/drivers/uio/Kconfig
> linux-2.6.28.elpa/drivers/uio/Kconfig
> --- linux-2.6.28/drivers/uio/Kconfig 2008-12-25 00:26:37.000000000 +0100
> +++ linux-2.6.28.elpa/drivers/uio/Kconfig 2009-01-07 18:58:10.000000000 +0100
> @@ -13,6 +13,18 @@ menuconfig UIO
>
> if UIO
>
> +
> +config UIO_CLOCK
> + tristate "General purpose clock driver"
> + default y
> + ---help---
> + This driver allows to configure and control internal clocks'
> + rates and parents through virtual files.
> + This driver requires implementation of some additional functions
> + in architecture specific low-level drivers:
> + clk_for_each() and clk_get_name()
> + Currently they're implemented only on Samsung S3C24xx platforms.
> +
> config UIO_CIF
> tristate "generic Hilscher CIF Card driver"
> depends on PCI
> diff -urNp linux-2.6.28/drivers/uio/Makefile
> linux-2.6.28.elpa/drivers/uio/Makefile
> --- linux-2.6.28/drivers/uio/Makefile 2008-12-25 00:26:37.000000000 +0100
> +++ linux-2.6.28.elpa/drivers/uio/Makefile 2009-01-06 18:20:17.000000000 +0100
> @@ -1,4 +1,5 @@
> obj-$(CONFIG_UIO) += uio.o
> +obj-$(CONFIG_UIO_CLOCK) += clock.o
> obj-$(CONFIG_UIO_CIF) += uio_cif.o
> obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
> obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o
next prev parent reply other threads:[~2009-01-08 11:28 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-08 9:49 [PATCH 1/3] Driver for user access to internal clocks Davide Rizzo
2009-01-08 11:27 ` Hans J. Koch [this message]
2009-01-08 16:06 ` Davide Rizzo
2009-01-08 16:30 ` Greg KH
2009-01-28 19:17 ` Davide Rizzo
[not found] ` <8447d6730901281117g3a8b7ddfp26e937111b6b6304-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2009-02-09 17:54 ` [PATCH 2/2] " Davide Rizzo
2009-02-09 17:54 ` Davide Rizzo
[not found] ` <20090108163024.GB26413-l3A5Bk7waGM@public.gmane.org>
2009-01-28 19:20 ` [PATCH 1/3] " Davide Rizzo
2009-01-28 19:20 ` Davide Rizzo
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=20090108112741.GA3053@local \
--to=hjk@linutronix.de \
--cc=ben-linux@fluff.org \
--cc=elpa.rizzo@gmail.com \
--cc=gregkh@suse.de \
--cc=linux-kernel@vger.kernel.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.