linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: nicolas.ferre@atmel.com (Nicolas Ferre)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v4 03/17] clk: at91: add PMC base support
Date: Thu, 17 Oct 2013 17:46:44 +0200	[thread overview]
Message-ID: <52600664.7060007@atmel.com> (raw)
In-Reply-To: <1381477081-6041-4-git-send-email-b.brezillon@overkiz.com>

On 11/10/2013 09:37, Boris BREZILLON :
> This patch adds at91 PMC (Power Management Controller) base support.
>
> All at91 clocks managed by the PMC unit will use this framework.
>
> This framework provides the following fonctionalities:
> - define a new struct at91_pmc to hide PMC internals (lock, PMC memory
>    mapping, irq domain, ...)
> - read/write helper functions (pmc_read/write) to access PMC registers
> - lock/unlock helper functions (pmc_lock/unlock) to lock/unlock access to
>    pmc registers
> - a new irq domain and its associated irq chip to request PMC specific
>    interrupts (useful for clk prepare callbacks)
>
> The PMC unit is declared as a dt clk provider (CLK_OF_DECLARE), and every
> clk using this framework will declare a table of of_at91_clk_init_cb_t
> and add it to the pmc_clk_ids table.
>
> When the pmc dt clock setup function is called (by of_clk_init function),
> it triggers the registration of every supported child clk (those matching
> the definitions in pmc_clk_ids).
>
> This patch copies at91_pmc_base (memory mapping) and at91sam9_idle
> (function) from arch/arm/mach-at91/clock.c (which is not compiled if
> COMMON_CLK_AT91 is enabled).
>
> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

> ---
>   drivers/clk/Makefile      |    1 +
>   drivers/clk/at91/Makefile |    5 +
>   drivers/clk/at91/pmc.c    |  283 +++++++++++++++++++++++++++++++++++++++++++++
>   drivers/clk/at91/pmc.h    |   58 ++++++++++
>   4 files changed, 347 insertions(+)
>   create mode 100644 drivers/clk/at91/Makefile
>   create mode 100644 drivers/clk/at91/pmc.c
>   create mode 100644 drivers/clk/at91/pmc.h
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 7b11106..28c2678 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -32,6 +32,7 @@ obj-$(CONFIG_ARCH_VT8500)	+= clk-vt8500.o
>   obj-$(CONFIG_ARCH_ZYNQ)		+= zynq/
>   obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
>   obj-$(CONFIG_PLAT_SAMSUNG)	+= samsung/
> +obj-$(CONFIG_COMMON_CLK_AT91)	+= at91/
>
>   obj-$(CONFIG_X86)		+= x86/
>
> diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
> new file mode 100644
> index 0000000..1d4fb21
> --- /dev/null
> +++ b/drivers/clk/at91/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Makefile for at91 specific clk
> +#
> +
> +obj-y += pmc.o
> diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
> new file mode 100644
> index 0000000..d92e46c
> --- /dev/null
> +++ b/drivers/clk/at91/pmc.c
> @@ -0,0 +1,283 @@
> +/*
> + * drivers/clk/at91/pmc.c
> + *
> + *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
> + *
> + * 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/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/of_irq.h>
> +
> +#include <asm/proc-fns.h>
> +
> +#include "pmc.h"
> +
> +void __iomem *at91_pmc_base;
> +EXPORT_SYMBOL_GPL(at91_pmc_base);
> +
> +void at91sam9_idle(void)
> +{
> +	at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK);
> +	cpu_do_idle();
> +}
> +
> +static void pmc_irq_mask(struct irq_data *d)
> +{
> +	struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
> +
> +	pmc_write(pmc, AT91_PMC_IDR, 1 << d->hwirq);
> +}
> +
> +static void pmc_irq_unmask(struct irq_data *d)
> +{
> +	struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
> +
> +	pmc_write(pmc, AT91_PMC_IER, 1 << d->hwirq);
> +}
> +
> +static int pmc_irq_set_type(struct irq_data *d, unsigned type)
> +{
> +	if (type != IRQ_TYPE_LEVEL_HIGH) {
> +		pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct irq_chip pmc_irq = {
> +	.name = "PMC",
> +	.irq_disable = pmc_irq_mask,
> +	.irq_mask = pmc_irq_mask,
> +	.irq_unmask = pmc_irq_unmask,
> +	.irq_set_type = pmc_irq_set_type,
> +};
> +
> +static struct lock_class_key pmc_lock_class;
> +
> +static int pmc_irq_map(struct irq_domain *h, unsigned int virq,
> +		       irq_hw_number_t hw)
> +{
> +	struct at91_pmc	*pmc = h->host_data;
> +
> +	irq_set_lockdep_class(virq, &pmc_lock_class);
> +
> +	irq_set_chip_and_handler(virq, &pmc_irq,
> +				 handle_level_irq);
> +	set_irq_flags(virq, IRQF_VALID);
> +	irq_set_chip_data(virq, pmc);
> +
> +	return 0;
> +}
> +
> +static int pmc_irq_domain_xlate(struct irq_domain *d,
> +				struct device_node *ctrlr,
> +				const u32 *intspec, unsigned int intsize,
> +				irq_hw_number_t *out_hwirq,
> +				unsigned int *out_type)
> +{
> +	struct at91_pmc *pmc = d->host_data;
> +	const struct at91_pmc_caps *caps = pmc->caps;
> +
> +	if (WARN_ON(intsize < 1))
> +		return -EINVAL;
> +
> +	*out_hwirq = intspec[0];
> +
> +	if (!(caps->available_irqs & (1 << *out_hwirq)))
> +		return -EINVAL;
> +
> +	*out_type = IRQ_TYPE_LEVEL_HIGH;
> +
> +	return 0;
> +}
> +
> +static struct irq_domain_ops pmc_irq_ops = {
> +	.map	= pmc_irq_map,
> +	.xlate	= pmc_irq_domain_xlate,
> +};
> +
> +static irqreturn_t pmc_irq_handler(int irq, void *data)
> +{
> +	struct at91_pmc *pmc = (struct at91_pmc *)data;
> +	unsigned long sr;
> +	int n;
> +
> +	sr = pmc_read(pmc, AT91_PMC_SR) & pmc_read(pmc, AT91_PMC_IMR);
> +	if (!sr)
> +		return IRQ_NONE;
> +
> +	for_each_set_bit(n, &sr, BITS_PER_LONG)
> +		generic_handle_irq(irq_find_mapping(pmc->irqdomain, n));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct at91_pmc_caps at91rm9200_caps = {
> +	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
> +			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
> +			  AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
> +			  AT91_PMC_PCK3RDY,
> +};
> +
> +static const struct at91_pmc_caps at91sam9260_caps = {
> +	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
> +			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
> +			  AT91_PMC_PCK1RDY,
> +};
> +
> +static const struct at91_pmc_caps at91sam9g45_caps = {
> +	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
> +			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
> +			  AT91_PMC_PCK1RDY,
> +};
> +
> +static const struct at91_pmc_caps at91sam9n12_caps = {
> +	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
> +			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
> +			  AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
> +			  AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
> +};
> +
> +static const struct at91_pmc_caps at91sam9x5_caps = {
> +	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
> +			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
> +			  AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
> +			  AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
> +};
> +
> +static const struct at91_pmc_caps sama5d3_caps = {
> +	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
> +			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
> +			  AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
> +			  AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS |
> +			  AT91_PMC_CFDEV,
> +};
> +
> +static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
> +					     void __iomem *regbase, int virq,
> +					     const struct at91_pmc_caps *caps)
> +{
> +	struct at91_pmc *pmc;
> +
> +	if (!regbase || !virq ||  !caps)
> +		return NULL;
> +
> +	at91_pmc_base = regbase;
> +
> +	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
> +	if (!pmc)
> +		return NULL;
> +
> +	spin_lock_init(&pmc->lock);
> +	pmc->regbase = regbase;
> +	pmc->virq = virq;
> +	pmc->caps = caps;
> +
> +	pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc);
> +
> +	if (!pmc->irqdomain)
> +		goto out_free_pmc;
> +
> +	pmc_write(pmc, AT91_PMC_IDR, 0xffffffff);
> +	if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc))
> +		goto out_remove_irqdomain;
> +
> +	return pmc;
> +
> +out_remove_irqdomain:
> +	irq_domain_remove(pmc->irqdomain);
> +out_free_pmc:
> +	kfree(pmc);
> +
> +	return NULL;
> +}
> +
> +static const struct of_device_id pmc_clk_ids[] __initdata = {
> +	{ /*sentinel*/ }
> +};
> +
> +static void __init of_at91_pmc_setup(struct device_node *np,
> +				     const struct at91_pmc_caps *caps)
> +{
> +	struct at91_pmc *pmc;
> +	struct device_node *childnp;
> +	void (*clk_setup)(struct device_node *, struct at91_pmc *);
> +	const struct of_device_id *clk_id;
> +	void __iomem *regbase = of_iomap(np, 0);
> +	int virq;
> +
> +	if (!regbase)
> +		return;
> +
> +	virq = irq_of_parse_and_map(np, 0);
> +	if (!virq)
> +		return;
> +
> +	pmc = at91_pmc_init(np, regbase, virq, caps);
> +	if (!pmc)
> +		return;
> +	for_each_child_of_node(np, childnp) {
> +		clk_id = of_match_node(pmc_clk_ids, childnp);
> +		if (!clk_id)
> +			continue;
> +		clk_setup = clk_id->data;
> +		clk_setup(childnp, pmc);
> +	}
> +}
> +
> +static void __init of_at91rm9200_pmc_setup(struct device_node *np)
> +{
> +	of_at91_pmc_setup(np, &at91rm9200_caps);
> +}
> +CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-pmc",
> +	       of_at91rm9200_pmc_setup);
> +
> +static void __init of_at91sam9260_pmc_setup(struct device_node *np)
> +{
> +	of_at91_pmc_setup(np, &at91sam9260_caps);
> +}
> +CLK_OF_DECLARE(at91sam9260_clk_main, "atmel,at91sam9260-pmc",
> +	       of_at91sam9260_pmc_setup);
> +
> +static void __init of_at91sam9g45_pmc_setup(struct device_node *np)
> +{
> +	of_at91_pmc_setup(np, &at91sam9g45_caps);
> +}
> +CLK_OF_DECLARE(at91sam9g45_clk_main, "atmel,at91sam9g45-pmc",
> +	       of_at91sam9g45_pmc_setup);
> +
> +static void __init of_at91sam9n12_pmc_setup(struct device_node *np)
> +{
> +	of_at91_pmc_setup(np, &at91sam9n12_caps);
> +}
> +CLK_OF_DECLARE(at91sam9n12_clk_main, "atmel,at91sam9n12-pmc",
> +	       of_at91sam9n12_pmc_setup);
> +
> +static void __init of_at91sam9x5_pmc_setup(struct device_node *np)
> +{
> +	of_at91_pmc_setup(np, &at91sam9x5_caps);
> +}
> +CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-pmc",
> +	       of_at91sam9x5_pmc_setup);
> +
> +static void __init of_sama5d3_pmc_setup(struct device_node *np)
> +{
> +	of_at91_pmc_setup(np, &sama5d3_caps);
> +}
> +CLK_OF_DECLARE(sama5d3_clk_main, "atmel,sama5d3-pmc",
> +	       of_sama5d3_pmc_setup);
> diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
> new file mode 100644
> index 0000000..b7e8397
> --- /dev/null
> +++ b/drivers/clk/at91/pmc.h
> @@ -0,0 +1,58 @@
> +/*
> + * drivers/clk/at91/pmc.h
> + *
> + *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
> + *
> + * 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.
> + */
> +
> +#ifndef __PMC_H_
> +#define __PMC_H_
> +
> +#include <linux/io.h>
> +#include <linux/irqdomain.h>
> +#include <linux/spinlock.h>
> +
> +struct clk_range {
> +	unsigned long min;
> +	unsigned long max;
> +};
> +
> +#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
> +
> +struct at91_pmc_caps {
> +	u32 available_irqs;
> +};
> +
> +struct at91_pmc {
> +	void __iomem *regbase;
> +	int virq;
> +	spinlock_t lock;
> +	const struct at91_pmc_caps *caps;
> +	struct irq_domain *irqdomain;
> +};
> +
> +static inline void pmc_lock(struct at91_pmc *pmc)
> +{
> +	spin_lock(&pmc->lock);
> +}
> +
> +static inline void pmc_unlock(struct at91_pmc *pmc)
> +{
> +	spin_unlock(&pmc->lock);
> +}
> +
> +static inline u32 pmc_read(struct at91_pmc *pmc, int offset)
> +{
> +	return readl_relaxed(pmc->regbase + offset);
> +}
> +
> +static inline void pmc_write(struct at91_pmc *pmc, int offset, u32 value)
> +{
> +	return writel_relaxed(value, pmc->regbase + offset);
> +}
> +
> +#endif /* __PMC_H_ */
>


-- 
Nicolas Ferre

  reply	other threads:[~2013-10-17 15:46 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-10-11  7:37 [PATCH v4 00/17] ARM: at91: move to common clk framework Boris BREZILLON
2013-10-11  7:37 ` [PATCH v4 01/17] ARM: at91: move at91_pmc.h to include/linux/clk/at91_pmc.h Boris BREZILLON
2013-10-11 16:05   ` Felipe Balbi
2013-10-16 15:29   ` Nicolas Ferre
2013-10-11  7:37 ` [PATCH v4 02/17] ARM: at91: add Kconfig options for common clk support Boris BREZILLON
2013-10-16 15:33   ` Nicolas Ferre
2013-10-11  7:37 ` [PATCH v4 03/17] clk: at91: add PMC base support Boris BREZILLON
2013-10-17 15:46   ` Nicolas Ferre [this message]
2013-10-11  8:41 ` [PATCH v4 04/17] clk: at91: add PMC macro file for dt definitions Boris BREZILLON
2013-10-17 15:47   ` Nicolas Ferre
2013-10-11  8:44 ` [PATCH v4 05/17] clk: at91: add PMC main clock Boris BREZILLON
2013-10-17 15:58   ` Nicolas Ferre
2013-10-11  8:48 ` [PATCH v4 06/17] clk: at91: add PMC pll clocks Boris BREZILLON
2013-10-17 15:59   ` Nicolas Ferre
2013-10-11  8:51 ` [PATCH v4 07/17] clk: at91: add PMC master clock Boris BREZILLON
2013-10-17 15:59   ` Nicolas Ferre
2013-10-11  9:41 ` [PATCH v4 08/17] clk: at91: add PMC system clocks Boris BREZILLON
2013-10-17 15:59   ` Nicolas Ferre
2013-10-11  9:44 ` [PATCH v4 09/17] clk: at91: add PMC peripheral clocks Boris BREZILLON
2013-10-17 16:02   ` Nicolas Ferre
2013-10-11  9:53 ` [PATCH v4 10/17] clk: at91: add peripheral clk macros for peripheral clk dt bindings Boris BREZILLON
2013-10-17 16:03   ` Nicolas Ferre
2013-10-11  9:57 ` [PATCH v4 11/17] clk: at91: add PMC programmable clocks Boris BREZILLON
2013-10-17 16:08   ` Nicolas Ferre
2013-10-11 10:22 ` [PATCH v4 12/17] clk: at91: add PMC utmi clock Boris BREZILLON
2013-10-17 16:10   ` Nicolas Ferre
2013-10-11 11:15 ` [PATCH v4 13/17] clk: at91: add PMC usb clock Boris BREZILLON
2013-10-17 16:18   ` Nicolas Ferre
2013-10-11 11:27 ` [PATCH v4 14/17] clk: at91: add PMC smd clock Boris BREZILLON
2013-10-17 16:27   ` Nicolas Ferre
2013-10-17 16:55     ` [PATCH v5 13/17] clk: at91: add PMC usb clock Boris BREZILLON
2013-10-18  7:53       ` Nicolas Ferre
2013-10-11 11:38 ` [PATCH v4 15/17] clk: at91: add PMC clk device tree binding doc Boris BREZILLON
2013-10-17 16:37   ` Nicolas Ferre
2013-10-11 11:46 ` [PATCH v4 16/17] ARM: at91: move pit timer to common clk framework Boris BREZILLON
2013-10-11 11:49 ` [PATCH v4 17/17] ARM: at91: add new compatible strings for pmc driver Boris BREZILLON
2013-10-18  8:18 ` [PATCH v4 00/17] ARM: at91: move to common clk framework Nicolas Ferre
2013-11-07 23:29   ` Mike Turquette

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=52600664.7060007@atmel.com \
    --to=nicolas.ferre@atmel.com \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).