/*
* Copyright (C) 2013 DENX Software Engineering
*
* Gerhard Sittig, <gsi@denx.de>
*
* common clock driver support for the MPC512x platform
*
* This 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/bitops.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mpc5121.h>
#include <dt-bindings/clock/mpc512x-clock.h>
#include "mpc512x.h" /* our public mpc5121_clk_init() API */
/* helpers to keep the MCLK intermediates "somewhere" in our table */
enum {
MCLK_IDX_MUX0,
MCLK_IDX_EN0,
MCLK_IDX_DIV0,
MCLK_MAX_IDX,
};
#define NR_PSCS 12
#define NR_MSCANS 4
#define NR_SPDIFS 1
#define NR_OUTCLK 4
#define NR_MCLKS (NR_PSCS + NR_MSCANS + NR_SPDIFS + NR_OUTCLK)
/* extend the public set of clocks by adding internal slots for management */
enum {
/* arrange for adjacent numbers after the public set */
MPC512x_CLK_START_PRIVATE = MPC512x_CLK_LAST_PUBLIC,
/* clocks which aren't announced to the public */
MPC512x_CLK_DDR,
MPC512x_CLK_MEM,
MPC512x_CLK_IIM,
/* intermediates in div+gate combos or fractional dividers */
MPC512x_CLK_DDR_UG,
MPC512x_CLK_SDHC_x4,
MPC512x_CLK_SDHC_UG,
MPC512x_CLK_SDHC2_UG,
MPC512x_CLK_DIU_x4,
MPC512x_CLK_DIU_UG,
MPC512x_CLK_MBX_BUS_UG,
MPC512x_CLK_MBX_UG,
MPC512x_CLK_MBX_3D_UG,
MPC512x_CLK_PCI_UG,
MPC512x_CLK_NFC_UG,
MPC512x_CLK_LPC_UG,
MPC512x_CLK_SPDIF_TX_IN,
/* intermediates for the mux+gate+div+mux MCLK generation */
MPC512x_CLK_MCLKS_FIRST,
MPC512x_CLK_MCLKS_LAST = MPC512x_CLK_MCLKS_FIRST
+ NR_MCLKS * MCLK_MAX_IDX,
/* internal, symbolic spec for the number of slots */
MPC512x_CLK_LAST_PRIVATE,
};
/* data required for the OF clock provider registration */
static struct clk *clks[MPC512x_CLK_LAST_PRIVATE];
static struct clk_onecell_data clk_data;
/* CCM register access */
static struct mpc512x_ccm __iomem *clkregs;
static DEFINE_SPINLOCK(clklock);
/* SoC variants {{{ */
/*
* tell SoC variants apart as they are rather similar yet not identical,
* cache the result in an enum to not repeatedly run the expensive OF test
*
* MPC5123 is an MPC5121 without the MBX graphics accelerator
*
* MPC5125 has many more differences: no MBX, no AXE, no VIU, no SPDIF,
* no PATA, no SATA, no PCI, two FECs (of different compatibility name),
* only 10 PSCs (of different compatibility name), two SDHCs, different
* NFC IP block, output clocks, system PLL status query, different CPMF
* interpretation, no CFM, different fourth PSC/CAN mux0 input -- yet
* those differences can get folded into this clock provider support
* code and don't warrant a separate highly redundant implementation
*/
static enum soc_type {
MPC512x_SOC_MPC5121,
MPC512x_SOC_MPC5123,
MPC512x_SOC_MPC5125,
} soc;
static void mpc512x_clk_determine_soc(void)
{
if (of_machine_is_compatible("fsl,mpc5121")) {
soc = MPC512x_SOC_MPC5121;
return;
}
if (of_machine_is_compatible("fsl,mpc5123")) {
soc = MPC512x_SOC_MPC5123;
return;
}
if (of_machine_is_compatible("fsl,mpc5125")) {
soc = MPC512x_SOC_MPC5125;
return;
}
}
static bool soc_has_mbx(void)
{
if (soc == MPC512x_SOC_MPC5121)
return true;
return false;
}
static bool soc_has_axe(void)
{
if (soc == MPC512x_SOC_MPC5125)
return false;
return true;
}
static bool soc_has_viu(void)
{
if (soc == MPC512x_SOC_MPC5125)
return false;
return true;
}
static bool soc_has_spdif(void)
{
if (soc == MPC512x_SOC_MPC5125)
return false;
return true;
}
static bool soc_has_pata(void)
{
if (soc == MPC512x_SOC_MPC5125)
return false;