Mark DDR clocks as critical for AT91 devices. These clocks are enabled by bootloader when initializing DDR and needs to stay enabled. Up to this patch the DDR clocks were requested from drivers/memory/atmel-sdramc.c which does only clock request and enable. There is no need to have a separate driver just for this, thus the atmel-sdramc.c will be deleted in a subsequent patch. Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com> Link: https://lore.kernel.org/r/20221208114515.35179-2-claudiu.beznea@microchip.com
190 lines
4.7 KiB
C
190 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <dt-bindings/clock/at91.h>
|
|
|
|
#include "pmc.h"
|
|
|
|
static DEFINE_SPINLOCK(sam9rl_mck_lock);
|
|
|
|
static const struct clk_master_characteristics sam9rl_mck_characteristics = {
|
|
.output = { .min = 0, .max = 94000000 },
|
|
.divisors = { 1, 2, 4, 0 },
|
|
};
|
|
|
|
static u8 sam9rl_plla_out[] = { 0, 2 };
|
|
|
|
static const struct clk_range sam9rl_plla_outputs[] = {
|
|
{ .min = 80000000, .max = 200000000 },
|
|
{ .min = 190000000, .max = 240000000 },
|
|
};
|
|
|
|
static const struct clk_pll_characteristics sam9rl_plla_characteristics = {
|
|
.input = { .min = 1000000, .max = 32000000 },
|
|
.num_output = ARRAY_SIZE(sam9rl_plla_outputs),
|
|
.output = sam9rl_plla_outputs,
|
|
.out = sam9rl_plla_out,
|
|
};
|
|
|
|
static const struct {
|
|
char *n;
|
|
char *p;
|
|
u8 id;
|
|
} at91sam9rl_systemck[] = {
|
|
{ .n = "pck0", .p = "prog0", .id = 8 },
|
|
{ .n = "pck1", .p = "prog1", .id = 9 },
|
|
};
|
|
|
|
static const struct {
|
|
char *n;
|
|
u8 id;
|
|
} at91sam9rl_periphck[] = {
|
|
{ .n = "pioA_clk", .id = 2, },
|
|
{ .n = "pioB_clk", .id = 3, },
|
|
{ .n = "pioC_clk", .id = 4, },
|
|
{ .n = "pioD_clk", .id = 5, },
|
|
{ .n = "usart0_clk", .id = 6, },
|
|
{ .n = "usart1_clk", .id = 7, },
|
|
{ .n = "usart2_clk", .id = 8, },
|
|
{ .n = "usart3_clk", .id = 9, },
|
|
{ .n = "mci0_clk", .id = 10, },
|
|
{ .n = "twi0_clk", .id = 11, },
|
|
{ .n = "twi1_clk", .id = 12, },
|
|
{ .n = "spi0_clk", .id = 13, },
|
|
{ .n = "ssc0_clk", .id = 14, },
|
|
{ .n = "ssc1_clk", .id = 15, },
|
|
{ .n = "tc0_clk", .id = 16, },
|
|
{ .n = "tc1_clk", .id = 17, },
|
|
{ .n = "tc2_clk", .id = 18, },
|
|
{ .n = "pwm_clk", .id = 19, },
|
|
{ .n = "adc_clk", .id = 20, },
|
|
{ .n = "dma0_clk", .id = 21, },
|
|
{ .n = "udphs_clk", .id = 22, },
|
|
{ .n = "lcd_clk", .id = 23, },
|
|
};
|
|
|
|
static void __init at91sam9rl_pmc_setup(struct device_node *np)
|
|
{
|
|
const char *slck_name, *mainxtal_name;
|
|
struct pmc_data *at91sam9rl_pmc;
|
|
const char *parent_names[6];
|
|
struct regmap *regmap;
|
|
struct clk_hw *hw;
|
|
int i;
|
|
|
|
i = of_property_match_string(np, "clock-names", "slow_clk");
|
|
if (i < 0)
|
|
return;
|
|
|
|
slck_name = of_clk_get_parent_name(np, i);
|
|
|
|
i = of_property_match_string(np, "clock-names", "main_xtal");
|
|
if (i < 0)
|
|
return;
|
|
mainxtal_name = of_clk_get_parent_name(np, i);
|
|
|
|
regmap = device_node_to_regmap(np);
|
|
if (IS_ERR(regmap))
|
|
return;
|
|
|
|
at91sam9rl_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
|
nck(at91sam9rl_systemck),
|
|
nck(at91sam9rl_periphck), 0, 2);
|
|
if (!at91sam9rl_pmc)
|
|
return;
|
|
|
|
hw = at91_clk_register_rm9200_main(regmap, "mainck", mainxtal_name);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->chws[PMC_MAIN] = hw;
|
|
|
|
hw = at91_clk_register_pll(regmap, "pllack", "mainck", 0,
|
|
&at91rm9200_pll_layout,
|
|
&sam9rl_plla_characteristics);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->chws[PMC_PLLACK] = hw;
|
|
|
|
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->chws[PMC_UTMI] = hw;
|
|
|
|
parent_names[0] = slck_name;
|
|
parent_names[1] = "mainck";
|
|
parent_names[2] = "pllack";
|
|
parent_names[3] = "utmick";
|
|
hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4,
|
|
parent_names,
|
|
&at91rm9200_master_layout,
|
|
&sam9rl_mck_characteristics,
|
|
&sam9rl_mck_lock);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
hw = at91_clk_register_master_div(regmap, "masterck_div",
|
|
"masterck_pres",
|
|
&at91rm9200_master_layout,
|
|
&sam9rl_mck_characteristics,
|
|
&sam9rl_mck_lock, CLK_SET_RATE_GATE, 0);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->chws[PMC_MCK] = hw;
|
|
|
|
parent_names[0] = slck_name;
|
|
parent_names[1] = "mainck";
|
|
parent_names[2] = "pllack";
|
|
parent_names[3] = "utmick";
|
|
parent_names[4] = "masterck_div";
|
|
for (i = 0; i < 2; i++) {
|
|
char name[6];
|
|
|
|
snprintf(name, sizeof(name), "prog%d", i);
|
|
|
|
hw = at91_clk_register_programmable(regmap, name,
|
|
parent_names, 5, i,
|
|
&at91rm9200_programmable_layout,
|
|
NULL);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->pchws[i] = hw;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) {
|
|
hw = at91_clk_register_system(regmap, at91sam9rl_systemck[i].n,
|
|
at91sam9rl_systemck[i].p,
|
|
at91sam9rl_systemck[i].id, 0);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->shws[at91sam9rl_systemck[i].id] = hw;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(at91sam9rl_periphck); i++) {
|
|
hw = at91_clk_register_peripheral(regmap,
|
|
at91sam9rl_periphck[i].n,
|
|
"masterck_div",
|
|
at91sam9rl_periphck[i].id);
|
|
if (IS_ERR(hw))
|
|
goto err_free;
|
|
|
|
at91sam9rl_pmc->phws[at91sam9rl_periphck[i].id] = hw;
|
|
}
|
|
|
|
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, at91sam9rl_pmc);
|
|
|
|
return;
|
|
|
|
err_free:
|
|
kfree(at91sam9rl_pmc);
|
|
}
|
|
|
|
CLK_OF_DECLARE(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
|