// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013 Freescale Semiconductor, Inc.
* Copyright 2021 NXP
*
* clock driver for Freescale QorIQ SoCs.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <dt-bindings/clock/fsl,qoriq-clockgen.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/fsl/guts.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/slab.h>
#define PLL_DIV1 0
#define PLL_DIV2 1
#define PLL_DIV3 2
#define PLL_DIV4 3
#define PLATFORM_PLL 0
#define CGA_PLL1 1
#define CGA_PLL2 2
#define CGA_PLL3 3
#define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */
#define CGB_PLL1 4
#define CGB_PLL2 5
#define MAX_PLL_DIV 32
struct clockgen_pll_div {
struct clk *clk;
char name[32];
};
struct clockgen_pll {
struct clockgen_pll_div div[MAX_PLL_DIV];
};
#define CLKSEL_VALID 1
#define CLKSEL_80PCT 2 /* Only allowed if PLL <= 80% of max cpu freq */
struct clockgen_sourceinfo {
u32 flags; /* CLKSEL_xxx */
int pll; /* CGx_PLLn */
int div; /* PLL_DIVn */
};
#define NUM_MUX_PARENTS 16
struct clockgen_muxinfo {
struct clockgen_sourceinfo clksel[NUM_MUX_PARENTS];
};
#define NUM_HWACCEL 5
#define NUM_CMUX 8
struct clockgen;
/*
* cmux freq must be >= platform pll.
* If not set, cmux freq must be >= platform pll/2
*/
#define CG_CMUX_GE_PLAT 1
#define CG_PLL_8BIT 2 /* PLLCnGSR[CFG] is 8 bits, not 6 */
#define CG_VER3 4 /* version 3 cg: reg layout different */
#define CG_LITTLE_ENDIAN 8
struct clockgen_chipinfo {
const char *compat, *guts_compat;
const struct clockgen_muxinfo *cmux_groups[2];
const struct clockgen_muxinfo *hwaccel[NUM_HWACCEL];
void (*init_periph)(struct clockgen *cg);
int cmux_to_group[NUM_CMUX + 1]; /* array should be -1 terminated */
u32 pll_mask; /* 1 << n bit set if PLL n is valid */
u32 flags; /* CG_xxx */
};
struct clockgen {
struct device_node *node;
void __iomem *regs;
struct clockgen_chipinfo info; /* mutable copy */
struct clk *sysclk, *coreclk;
struct clockgen_pll pll[6];
struct clk *cmux[NUM_CMUX];
struct clk *hwaccel[NUM_HWACCEL];
struct clk *fman[2];
struct ccsr_guts __iomem *guts;
};
static struct clockgen clockgen;
static bool add_cpufreq_dev __initdata;
static void cg_out(struct clockgen *cg, u32 val, u32 __iomem *reg)
{
if (cg->info.flags & CG_LITTLE_ENDIAN)
iowrite32(val, reg);
else
iowrite32be(val, reg);
}
static u32 cg_in(struct clockgen *cg, u32 __iomem *reg)
{
u32 val;
if (cg->info.flags & CG_LITTLE_ENDIAN)
val = ioread32(reg);
else
val = ioread32be(reg);
return val;
}
static const struct clockgen_muxinfo p2041_cmux_grp1 = {
{
[0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
[1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
[4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
}
};
static const struct clockgen_muxinfo p2041_cmux_grp2 = {
{
[0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
[4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
[5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
}
};
static const struct clockgen_muxinfo p5020_cmux_grp1 = {
{
[0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
[1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
[4] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV1 },
}
};
static const struct clockgen_muxinfo p5020_cmux_grp2 = {
{
[0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 },
[4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
[5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
}
};
static const struct clockgen_muxinfo p5040_cmux_grp1 = {
{
[0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
[1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
[4] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV1 },
[5] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL2, PLL_DIV2 },
}
};
static const struct clockgen_muxinfo p5040_cmux_grp2 = {
{
[0] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV1 },
[1] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL1, PLL_DIV2 },
[4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
[5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
}
};
static const struct clockgen_muxinfo p4080_cmux_grp1 = {
{
[0] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
[1] = { CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
[4] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
[5] = { CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
[8] = { CLKSEL_VALID | CLKSEL_80PCT, CGA_PLL3, PLL_DIV1 },
}
};
static const struct clockgen_muxinfo p4080_cmux_grp2 = {
{
[0] = { CL
|