Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
 
 
 
 
 
 
  4 */
  5
  6#include <linux/clk.h>
  7#include <linux/clk-provider.h>
  8#include <linux/clkdev.h>
  9#include <linux/clk/at91_pmc.h>
 10#include <linux/of.h>
 11#include <linux/of_address.h>
 12#include <linux/mfd/syscon.h>
 13#include <linux/platform_device.h>
 14#include <linux/regmap.h>
 15#include <linux/syscore_ops.h>
 
 
 16
 17#include <asm/proc-fns.h>
 18
 19#include "pmc.h"
 20
 21#define PMC_MAX_IDS 128
 22#define PMC_MAX_PCKS 8
 
 
 
 
 
 
 23
 24int of_at91_get_clk_range(struct device_node *np, const char *propname,
 25			  struct clk_range *range)
 26{
 27	u32 min, max;
 28	int ret;
 29
 30	ret = of_property_read_u32_index(np, propname, 0, &min);
 31	if (ret)
 32		return ret;
 33
 34	ret = of_property_read_u32_index(np, propname, 1, &max);
 35	if (ret)
 36		return ret;
 37
 38	if (range) {
 39		range->min = min;
 40		range->max = max;
 41	}
 42
 43	return 0;
 44}
 45EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
 46
 47struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
 48{
 49	unsigned int type = clkspec->args[0];
 50	unsigned int idx = clkspec->args[1];
 51	struct pmc_data *pmc_data = data;
 52
 53	switch (type) {
 54	case PMC_TYPE_CORE:
 55		if (idx < pmc_data->ncore)
 56			return pmc_data->chws[idx];
 57		break;
 58	case PMC_TYPE_SYSTEM:
 59		if (idx < pmc_data->nsystem)
 60			return pmc_data->shws[idx];
 61		break;
 62	case PMC_TYPE_PERIPHERAL:
 63		if (idx < pmc_data->nperiph)
 64			return pmc_data->phws[idx];
 65		break;
 66	case PMC_TYPE_GCK:
 67		if (idx < pmc_data->ngck)
 68			return pmc_data->ghws[idx];
 69		break;
 70	case PMC_TYPE_PROGRAMMABLE:
 71		if (idx < pmc_data->npck)
 72			return pmc_data->pchws[idx];
 73		break;
 74	default:
 75		break;
 76	}
 77
 78	pr_err("%s: invalid type (%u) or index (%u)\n", __func__, type, idx);
 
 79
 80	return ERR_PTR(-EINVAL);
 
 
 
 
 81}
 82
 83struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
 84				   unsigned int nperiph, unsigned int ngck,
 85				   unsigned int npck)
 86{
 87	unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
 88	struct pmc_data *pmc_data;
 
 
 89
 90	pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),
 91			   GFP_KERNEL);
 92	if (!pmc_data)
 93		return NULL;
 94
 95	pmc_data->ncore = ncore;
 96	pmc_data->chws = pmc_data->hwtable;
 
 
 
 
 
 97
 98	pmc_data->nsystem = nsystem;
 99	pmc_data->shws = pmc_data->chws + ncore;
100
101	pmc_data->nperiph = nperiph;
102	pmc_data->phws = pmc_data->shws + nsystem;
 
 
103
104	pmc_data->ngck = ngck;
105	pmc_data->ghws = pmc_data->phws + nperiph;
106
107	pmc_data->npck = npck;
108	pmc_data->pchws = pmc_data->ghws + ngck;
 
 
109
110	return pmc_data;
111}
112
113#ifdef CONFIG_PM
 
 
 
 
 
 
 
114
115/* Address in SECURAM that say if we suspend to backup mode. */
116static void __iomem *at91_pmc_backup_suspend;
117
118static int at91_pmc_suspend(void)
119{
120	unsigned int backup;
121
122	if (!at91_pmc_backup_suspend)
123		return 0;
124
125	backup = readl_relaxed(at91_pmc_backup_suspend);
126	if (!backup)
127		return 0;
128
129	return clk_save_context();
130}
131
132static void at91_pmc_resume(void)
133{
134	unsigned int backup;
 
135
136	if (!at91_pmc_backup_suspend)
137		return;
 
 
 
 
 
 
 
138
139	backup = readl_relaxed(at91_pmc_backup_suspend);
140	if (!backup)
141		return;
142
143	clk_restore_context();
144}
145
146static struct syscore_ops pmc_syscore_ops = {
147	.suspend = at91_pmc_suspend,
148	.resume = at91_pmc_resume,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149};
150
151static const struct of_device_id pmc_dt_ids[] = {
152	{ .compatible = "atmel,sama5d2-pmc" },
153	{ .compatible = "microchip,sama7g5-pmc", },
154	{ /* sentinel */ }
 
 
155};
156
157static int __init pmc_register_ops(void)
 
 
158{
159	struct device_node *np;
160
161	np = of_find_matching_node(NULL, pmc_dt_ids);
162	if (!np)
163		return -ENODEV;
164
165	if (!of_device_is_available(np)) {
166		of_node_put(np);
167		return -ENODEV;
168	}
169	of_node_put(np);
170
171	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
172	if (!np)
173		return -ENODEV;
174
175	if (!of_device_is_available(np)) {
176		of_node_put(np);
177		return -ENODEV;
178	}
179	of_node_put(np);
180
181	at91_pmc_backup_suspend = of_iomap(np, 0);
182	if (!at91_pmc_backup_suspend) {
183		pr_warn("%s(): unable to map securam\n", __func__);
184		return -ENOMEM;
185	}
186
187	register_syscore_ops(&pmc_syscore_ops);
188
189	return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190}
191/* This has to happen before arch_initcall because of the tcb_clksrc driver */
192postcore_initcall(pmc_register_ops);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193#endif
v3.15
 
  1/*
  2 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  3 *
  4 * This program is free software; you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License as published by
  6 * the Free Software Foundation; either version 2 of the License, or
  7 * (at your option) any later version.
  8 *
  9 */
 10
 
 11#include <linux/clk-provider.h>
 12#include <linux/clkdev.h>
 13#include <linux/clk/at91_pmc.h>
 14#include <linux/of.h>
 15#include <linux/of_address.h>
 16#include <linux/io.h>
 17#include <linux/interrupt.h>
 18#include <linux/irq.h>
 19#include <linux/irqchip/chained_irq.h>
 20#include <linux/irqdomain.h>
 21#include <linux/of_irq.h>
 22
 23#include <asm/proc-fns.h>
 24
 25#include "pmc.h"
 26
 27void __iomem *at91_pmc_base;
 28EXPORT_SYMBOL_GPL(at91_pmc_base);
 29
 30void at91sam9_idle(void)
 31{
 32	at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK);
 33	cpu_do_idle();
 34}
 35
 36int of_at91_get_clk_range(struct device_node *np, const char *propname,
 37			  struct clk_range *range)
 38{
 39	u32 min, max;
 40	int ret;
 41
 42	ret = of_property_read_u32_index(np, propname, 0, &min);
 43	if (ret)
 44		return ret;
 45
 46	ret = of_property_read_u32_index(np, propname, 1, &max);
 47	if (ret)
 48		return ret;
 49
 50	if (range) {
 51		range->min = min;
 52		range->max = max;
 53	}
 54
 55	return 0;
 56}
 57EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
 58
 59static void pmc_irq_mask(struct irq_data *d)
 60{
 61	struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 62
 63	pmc_write(pmc, AT91_PMC_IDR, 1 << d->hwirq);
 64}
 65
 66static void pmc_irq_unmask(struct irq_data *d)
 67{
 68	struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
 69
 70	pmc_write(pmc, AT91_PMC_IER, 1 << d->hwirq);
 71}
 72
 73static int pmc_irq_set_type(struct irq_data *d, unsigned type)
 
 
 74{
 75	if (type != IRQ_TYPE_LEVEL_HIGH) {
 76		pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n");
 77		return -EINVAL;
 78	}
 79
 80	return 0;
 81}
 
 
 82
 83static struct irq_chip pmc_irq = {
 84	.name = "PMC",
 85	.irq_disable = pmc_irq_mask,
 86	.irq_mask = pmc_irq_mask,
 87	.irq_unmask = pmc_irq_unmask,
 88	.irq_set_type = pmc_irq_set_type,
 89};
 90
 91static struct lock_class_key pmc_lock_class;
 
 92
 93static int pmc_irq_map(struct irq_domain *h, unsigned int virq,
 94		       irq_hw_number_t hw)
 95{
 96	struct at91_pmc	*pmc = h->host_data;
 97
 98	irq_set_lockdep_class(virq, &pmc_lock_class);
 
 99
100	irq_set_chip_and_handler(virq, &pmc_irq,
101				 handle_level_irq);
102	set_irq_flags(virq, IRQF_VALID);
103	irq_set_chip_data(virq, pmc);
104
105	return 0;
106}
107
108static int pmc_irq_domain_xlate(struct irq_domain *d,
109				struct device_node *ctrlr,
110				const u32 *intspec, unsigned int intsize,
111				irq_hw_number_t *out_hwirq,
112				unsigned int *out_type)
113{
114	struct at91_pmc *pmc = d->host_data;
115	const struct at91_pmc_caps *caps = pmc->caps;
116
117	if (WARN_ON(intsize < 1))
118		return -EINVAL;
119
120	*out_hwirq = intspec[0];
 
 
121
122	if (!(caps->available_irqs & (1 << *out_hwirq)))
123		return -EINVAL;
124
125	*out_type = IRQ_TYPE_LEVEL_HIGH;
 
 
126
127	return 0;
128}
129
130static struct irq_domain_ops pmc_irq_ops = {
131	.map	= pmc_irq_map,
132	.xlate	= pmc_irq_domain_xlate,
133};
134
135static irqreturn_t pmc_irq_handler(int irq, void *data)
136{
137	struct at91_pmc *pmc = (struct at91_pmc *)data;
138	unsigned long sr;
139	int n;
140
141	sr = pmc_read(pmc, AT91_PMC_SR) & pmc_read(pmc, AT91_PMC_IMR);
142	if (!sr)
143		return IRQ_NONE;
144
145	for_each_set_bit(n, &sr, BITS_PER_LONG)
146		generic_handle_irq(irq_find_mapping(pmc->irqdomain, n));
 
147
148	return IRQ_HANDLED;
149}
150
151static const struct at91_pmc_caps at91rm9200_caps = {
152	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
153			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
154			  AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
155			  AT91_PMC_PCK3RDY,
156};
157
158static const struct at91_pmc_caps at91sam9260_caps = {
159	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
160			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
161			  AT91_PMC_PCK1RDY,
162};
163
164static const struct at91_pmc_caps at91sam9g45_caps = {
165	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
166			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
167			  AT91_PMC_PCK1RDY,
168};
169
170static const struct at91_pmc_caps at91sam9n12_caps = {
171	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB |
172			  AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY |
173			  AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
174			  AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
175};
176
177static const struct at91_pmc_caps at91sam9x5_caps = {
178	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
179			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
180			  AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS |
181			  AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
182};
183
184static const struct at91_pmc_caps sama5d3_caps = {
185	.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
186			  AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
187			  AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
188			  AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS |
189			  AT91_PMC_CFDEV,
190};
191
192static struct at91_pmc *__init at91_pmc_init(struct device_node *np,
193					     void __iomem *regbase, int virq,
194					     const struct at91_pmc_caps *caps)
195{
196	struct at91_pmc *pmc;
197
198	if (!regbase || !virq ||  !caps)
199		return NULL;
 
200
201	at91_pmc_base = regbase;
 
 
 
 
202
203	pmc = kzalloc(sizeof(*pmc), GFP_KERNEL);
204	if (!pmc)
205		return NULL;
 
 
 
 
 
 
206
207	spin_lock_init(&pmc->lock);
208	pmc->regbase = regbase;
209	pmc->virq = virq;
210	pmc->caps = caps;
 
211
212	pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc);
213
214	if (!pmc->irqdomain)
215		goto out_free_pmc;
216
217	pmc_write(pmc, AT91_PMC_IDR, 0xffffffff);
218	if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc))
219		goto out_remove_irqdomain;
220
221	return pmc;
222
223out_remove_irqdomain:
224	irq_domain_remove(pmc->irqdomain);
225out_free_pmc:
226	kfree(pmc);
227
228	return NULL;
229}
230
231static const struct of_device_id pmc_clk_ids[] __initconst = {
232	/* Main clock */
233	{
234		.compatible = "atmel,at91rm9200-clk-main",
235		.data = of_at91rm9200_clk_main_setup,
236	},
237	/* PLL clocks */
238	{
239		.compatible = "atmel,at91rm9200-clk-pll",
240		.data = of_at91rm9200_clk_pll_setup,
241	},
242	{
243		.compatible = "atmel,at91sam9g45-clk-pll",
244		.data = of_at91sam9g45_clk_pll_setup,
245	},
246	{
247		.compatible = "atmel,at91sam9g20-clk-pllb",
248		.data = of_at91sam9g20_clk_pllb_setup,
249	},
250	{
251		.compatible = "atmel,sama5d3-clk-pll",
252		.data = of_sama5d3_clk_pll_setup,
253	},
254	{
255		.compatible = "atmel,at91sam9x5-clk-plldiv",
256		.data = of_at91sam9x5_clk_plldiv_setup,
257	},
258	/* Master clock */
259	{
260		.compatible = "atmel,at91rm9200-clk-master",
261		.data = of_at91rm9200_clk_master_setup,
262	},
263	{
264		.compatible = "atmel,at91sam9x5-clk-master",
265		.data = of_at91sam9x5_clk_master_setup,
266	},
267	/* System clocks */
268	{
269		.compatible = "atmel,at91rm9200-clk-system",
270		.data = of_at91rm9200_clk_sys_setup,
271	},
272	/* Peripheral clocks */
273	{
274		.compatible = "atmel,at91rm9200-clk-peripheral",
275		.data = of_at91rm9200_clk_periph_setup,
276	},
277	{
278		.compatible = "atmel,at91sam9x5-clk-peripheral",
279		.data = of_at91sam9x5_clk_periph_setup,
280	},
281	/* Programmable clocks */
282	{
283		.compatible = "atmel,at91rm9200-clk-programmable",
284		.data = of_at91rm9200_clk_prog_setup,
285	},
286	{
287		.compatible = "atmel,at91sam9g45-clk-programmable",
288		.data = of_at91sam9g45_clk_prog_setup,
289	},
290	{
291		.compatible = "atmel,at91sam9x5-clk-programmable",
292		.data = of_at91sam9x5_clk_prog_setup,
293	},
294	/* UTMI clock */
295#if defined(CONFIG_HAVE_AT91_UTMI)
296	{
297		.compatible = "atmel,at91sam9x5-clk-utmi",
298		.data = of_at91sam9x5_clk_utmi_setup,
299	},
300#endif
301	/* USB clock */
302#if defined(CONFIG_HAVE_AT91_USB_CLK)
303	{
304		.compatible = "atmel,at91rm9200-clk-usb",
305		.data = of_at91rm9200_clk_usb_setup,
306	},
307	{
308		.compatible = "atmel,at91sam9x5-clk-usb",
309		.data = of_at91sam9x5_clk_usb_setup,
310	},
311	{
312		.compatible = "atmel,at91sam9n12-clk-usb",
313		.data = of_at91sam9n12_clk_usb_setup,
314	},
315#endif
316	/* SMD clock */
317#if defined(CONFIG_HAVE_AT91_SMD)
318	{
319		.compatible = "atmel,at91sam9x5-clk-smd",
320		.data = of_at91sam9x5_clk_smd_setup,
321	},
322#endif
323	{ /*sentinel*/ }
324};
325
326static void __init of_at91_pmc_setup(struct device_node *np,
327				     const struct at91_pmc_caps *caps)
328{
329	struct at91_pmc *pmc;
330	struct device_node *childnp;
331	void (*clk_setup)(struct device_node *, struct at91_pmc *);
332	const struct of_device_id *clk_id;
333	void __iomem *regbase = of_iomap(np, 0);
334	int virq;
335
336	if (!regbase)
337		return;
338
339	virq = irq_of_parse_and_map(np, 0);
340	if (!virq)
341		return;
342
343	pmc = at91_pmc_init(np, regbase, virq, caps);
344	if (!pmc)
345		return;
346	for_each_child_of_node(np, childnp) {
347		clk_id = of_match_node(pmc_clk_ids, childnp);
348		if (!clk_id)
349			continue;
350		clk_setup = clk_id->data;
351		clk_setup(childnp, pmc);
352	}
353}
354
355static void __init of_at91rm9200_pmc_setup(struct device_node *np)
356{
357	of_at91_pmc_setup(np, &at91rm9200_caps);
358}
359CLK_OF_DECLARE(at91rm9200_clk_pmc, "atmel,at91rm9200-pmc",
360	       of_at91rm9200_pmc_setup);
361
362static void __init of_at91sam9260_pmc_setup(struct device_node *np)
363{
364	of_at91_pmc_setup(np, &at91sam9260_caps);
365}
366CLK_OF_DECLARE(at91sam9260_clk_pmc, "atmel,at91sam9260-pmc",
367	       of_at91sam9260_pmc_setup);
368
369static void __init of_at91sam9g45_pmc_setup(struct device_node *np)
370{
371	of_at91_pmc_setup(np, &at91sam9g45_caps);
372}
373CLK_OF_DECLARE(at91sam9g45_clk_pmc, "atmel,at91sam9g45-pmc",
374	       of_at91sam9g45_pmc_setup);
375
376static void __init of_at91sam9n12_pmc_setup(struct device_node *np)
377{
378	of_at91_pmc_setup(np, &at91sam9n12_caps);
379}
380CLK_OF_DECLARE(at91sam9n12_clk_pmc, "atmel,at91sam9n12-pmc",
381	       of_at91sam9n12_pmc_setup);
382
383static void __init of_at91sam9x5_pmc_setup(struct device_node *np)
384{
385	of_at91_pmc_setup(np, &at91sam9x5_caps);
386}
387CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc",
388	       of_at91sam9x5_pmc_setup);
389
390static void __init of_sama5d3_pmc_setup(struct device_node *np)
391{
392	of_at91_pmc_setup(np, &sama5d3_caps);
393}
394CLK_OF_DECLARE(sama5d3_clk_pmc, "atmel,sama5d3-pmc",
395	       of_sama5d3_pmc_setup);