// SPDX-License-Identifier: GPL-2.0
/*
* SAMA7G5 PMC code.
*
* Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
*
* Author: Claudiu Beznea <claudiu.beznea@microchip.com>
*
*/
#include <linux/clk.h>
#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(pmc_pll_lock);
static DEFINE_SPINLOCK(pmc_mck0_lock);
static DEFINE_SPINLOCK(pmc_mckX_lock);
/*
* PLL clocks identifiers
* @PLL_ID_CPU: CPU PLL identifier
* @PLL_ID_SYS: System PLL identifier
* @PLL_ID_DDR: DDR PLL identifier
* @PLL_ID_IMG: Image subsystem PLL identifier
* @PLL_ID_BAUD: Baud PLL identifier
* @PLL_ID_AUDIO: Audio PLL identifier
* @PLL_ID_ETH: Ethernet PLL identifier
*/
enum pll_ids {
PLL_ID_CPU,
PLL_ID_SYS,
PLL_ID_DDR,
PLL_ID_IMG,
PLL_ID_BAUD,
PLL_ID_AUDIO,
PLL_ID_ETH,
PLL_ID_MAX,
};
/*
* PLL component identifier
* @PLL_COMPID_FRAC: Fractional PLL component identifier
* @PLL_COMPID_DIV0: 1st PLL divider component identifier
* @PLL_COMPID_DIV1: 2nd PLL divider component identifier
*/
enum pll_component_id {
PLL_COMPID_FRAC,
PLL_COMPID_DIV0,
PLL_COMPID_DIV1,
PLL_COMPID_MAX,
};
/*
* PLL type identifiers
* @PLL_TYPE_FRAC: fractional PLL identifier
* @PLL_TYPE_DIV: divider PLL identifier
*/
enum pll_type {
PLL_TYPE_FRAC,
PLL_TYPE_DIV,
};
/* Layout for fractional PLLs. */
static const struct clk_pll_layout pll_layout_frac = {
.mul_mask = GENMASK(31, 24),
.frac_mask = GENMASK(21, 0),
.mul_shift = 24,
.frac_shift = 0,
};
/* Layout for DIVPMC dividers. */
static const struct clk_pll_layout pll_layout_divpmc = {
.div_mask = GENMASK(7, 0),
.endiv_mask = BIT(29),
.div_shift = 0,
.endiv_shift = 29,
};
/* Layout for DIVIO dividers. */
static const struct clk_pll_layout pll_layout_divio = {
.div_mask = GENMASK(19, 12),
.endiv_mask = BIT(30),
.div_shift = 12,
.endiv_shift = 30,
};
/*
* CPU PLL output range.
* Notice: The upper limit has been setup to 1000000002 due to hardware
* block which cannot output exactly 1GHz.
*/
static const struct clk_range cpu_pll_outputs[] = {
{ .min = 2343750, .max = 1000000002 },
};
/* PLL output range. */
static const struct clk_range pll_outputs[] = {
{ .min = 2343750, .max = 1200000000 },
};
/* Fractional PLL core output range. */
static const struct clk_range core_outputs[] = {
{ .min = 600000000, .max = 1200000000 },
};
/* CPU PLL characteristics. */
static const struct clk_pll_characteristics cpu_pll_characteristics = {
.input = { .min = 12000000, .max = 50000000 },
.num_output = ARRAY_SIZE(cpu_pll_outputs),
.output = cpu_pll_outputs,
.core_output = core_outputs,
};
/* PLL characteristics. */
static const struct clk_pll_characteristics pll_characteristics = {
.input = { .min = 12000000, .max = 50000000 },
.num_output = ARRAY_SIZE(pll_outputs),
.output = pll_outputs,
.core_output = core_outputs,
};
/*
* SAMA7G5 PLL possible parents
* @SAMA7G5_PLL_PARENT_MAINCK: MAINCK is PLL a parent
* @SAMA7G5_PLL_PARENT_MAIN_XTAL: MAIN XTAL is a PLL parent
* @SAMA7G5_PLL_PARENT_FRACCK: Frac PLL is a PLL parent (for PLL dividers)
*/
enum sama7g5_pll_parent {
SAMA7G5_PLL_PARENT_MAINCK,
SAMA7G5_PLL_PARENT_MAIN_XTAL,
SAMA7G5_PLL_PARENT_FRACCK,
};
/*
* PLL clocks description
* @n: clock name
* @l: clock layout
* @c: clock characteristics
* @hw: pointer to clk_hw
* @t: clock type
* @f: clock flags
* @p: clock parent
* @eid: export index in sama7g5->chws[] array
* @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
* notification
*/
static struct sama7g5_pll {
const char *n;
const struct clk_pll_layout *l;
const struct clk_pll_characteristics *c;
struct clk_hw *hw;
unsigned long f;
enum sama7g5_pll_parent p;
u8 t;
u8 eid;
u8 safe_div;
} sama7g5_plls[][PLL_COMPID_MAX]