summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/512x/clock-commonclk.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/512x/clock-commonclk.c')
-rw-r--r--arch/powerpc/platforms/512x/clock-commonclk.c339
1 files changed, 295 insertions, 44 deletions
diff --git a/arch/powerpc/platforms/512x/clock-commonclk.c b/arch/powerpc/platforms/512x/clock-commonclk.c
index 16569376b259..6eb614a271fb 100644
--- a/arch/powerpc/platforms/512x/clock-commonclk.c
+++ b/arch/powerpc/platforms/512x/clock-commonclk.c
@@ -36,7 +36,8 @@ enum {
#define NR_PSCS 12
#define NR_MSCANS 4
#define NR_SPDIFS 1
-#define NR_MCLKS (NR_PSCS + NR_MSCANS + NR_SPDIFS)
+#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 {
@@ -46,11 +47,11 @@ enum {
MPC512x_CLK_DDR,
MPC512x_CLK_MEM,
MPC512x_CLK_IIM,
- MPC512x_CLK_SDHC_2,
/* 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,
@@ -76,6 +77,144 @@ static struct clk_onecell_data clk_data;
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;
+ return true;
+}
+
+static bool soc_has_sata(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return false;
+ return true;
+}
+
+static bool soc_has_pci(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return false;
+ return true;
+}
+
+static bool soc_has_fec2(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return true;
+ return false;
+}
+
+static int soc_max_pscnum(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return 10;
+ return 12;
+}
+
+static bool soc_has_sdhc2(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return true;
+ return false;
+}
+
+static bool soc_has_nfc_5125(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return true;
+ return false;
+}
+
+static bool soc_has_outclk(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return true;
+ return false;
+}
+
+static bool soc_has_cpmf_0_bypass(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return true;
+ return false;
+}
+
+static bool soc_has_mclk_mux0_canin(void)
+{
+ if (soc == MPC512x_SOC_MPC5125)
+ return true;
+ return false;
+}
+
+/* }}} SoC variants */
/* common clk API wrappers {{{ */
/* convenience wrappers around the common clk API */
@@ -196,12 +335,23 @@ static int get_sys_div_x2(void)
*/
static int get_cpmf_mult_x2(void)
{
- static int cpmf_to_mult[] = {
+ static int cpmf_to_mult_x36[] = {
+ /* 0b000 is "times 36" */
72, 2, 2, 3, 4, 5, 6, 7,
};
+ static int cpmf_to_mult_0by[] = {
+ /* 0b000 is "bypass" */
+ 2, 2, 2, 3, 4, 5, 6, 7,
+ };
+
+ int *cpmf_to_mult;
int cpmf;
cpmf = get_bit_field(&clkregs->spmr, 16, 4);
+ if (soc_has_cpmf_0_bypass())
+ cpmf_to_mult = cpmf_to_mult_0by;
+ else
+ cpmf_to_mult = cpmf_to_mult_x36;
return cpmf_to_mult[cpmf];
}
@@ -347,14 +497,19 @@ static void mpc512x_clk_setup_ref_clock(struct device_node *np, int bus_freq,
* it's the very data type dictated by <linux/clk-provider.h>,
* "fixing" this warning will break compilation
*/
-static const char *parent_names_mux0[] = {
+static const char *parent_names_mux0_spdif[] = {
"sys", "ref", "psc-mclk-in", "spdif-tx",
};
+static const char *parent_names_mux0_canin[] = {
+ "sys", "ref", "psc-mclk-in", "can-clk-in",
+};
+
enum mclk_type {
MCLK_TYPE_PSC,
MCLK_TYPE_MSCAN,
MCLK_TYPE_SPDIF,
+ MCLK_TYPE_OUTCLK,
};
struct mclk_setup_data {
@@ -394,6 +549,15 @@ struct mclk_setup_data {
"spdif_mclk", \
}
+#define MCLK_SETUP_DATA_OUTCLK(id) { \
+ MCLK_TYPE_OUTCLK, 0, \
+ "out" #id "-mux0", \
+ "out" #id "-en0", \
+ "out" #id "_mclk_div", \
+ { "out" #id "_mclk_div", "dummy", }, \
+ "out" #id "_clk", \
+}
+
static struct mclk_setup_data mclk_psc_data[] = {
MCLK_SETUP_DATA_PSC(0),
MCLK_SETUP_DATA_PSC(1),
@@ -420,6 +584,13 @@ static struct mclk_setup_data mclk_spdif_data[] = {
MCLK_SETUP_DATA_SPDIF,
};
+static struct mclk_setup_data mclk_outclk_data[] = {
+ MCLK_SETUP_DATA_OUTCLK(0),
+ MCLK_SETUP_DATA_OUTCLK(1),
+ MCLK_SETUP_DATA_OUTCLK(2),
+ MCLK_SETUP_DATA_OUTCLK(3),
+};
+
/* setup the MCLK clock subtree of an individual PSC/MSCAN/SPDIF */
static void mpc512x_clk_setup_mclk(struct mclk_setup_data *entry, size_t idx)
{
@@ -447,6 +618,13 @@ static void mpc512x_clk_setup_mclk(struct mclk_setup_data *entry, size_t idx)
+ (NR_PSCS + NR_MSCANS) * MCLK_MAX_IDX;
mccr_reg = &clkregs->spccr;
break;
+ case MCLK_TYPE_OUTCLK:
+ clks_idx_pub = MPC512x_CLK_OUT0_CLK + idx;
+ clks_idx_int = MPC512x_CLK_MCLKS_FIRST
+ + (NR_PSCS + NR_MSCANS + NR_SPDIFS + idx)
+ * MCLK_MAX_IDX;
+ mccr_reg = &clkregs->out_ccr[idx];
+ break;
default:
return;
}
@@ -495,7 +673,10 @@ static void mpc512x_clk_setup_mclk(struct mclk_setup_data *entry, size_t idx)
*/
clks[clks_idx_int + MCLK_IDX_MUX0] = mpc512x_clk_muxed(
entry->name_mux0,
- &parent_names_mux0[0], ARRAY_SIZE(parent_names_mux0),
+ soc_has_mclk_mux0_canin()
+ ? &parent_names_mux0_canin[0]
+ : &parent_names_mux0_spdif[0],
+ ARRAY_SIZE(parent_names_mux0_spdif),
mccr_reg, 14, 2);
clks[clks_idx_int + MCLK_IDX_EN0] = mpc512x_clk_gated(
entry->name_en0, entry->name_mux0,
@@ -576,6 +757,12 @@ static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq)
clks[MPC512x_CLK_SDHC_UG] = mpc512x_clk_divider("sdhc-ug", "sdhc-x4", 0,
&clkregs->scfr2, 1, 7,
CLK_DIVIDER_ONE_BASED);
+ if (soc_has_sdhc2()) {
+ clks[MPC512x_CLK_SDHC2_UG] = mpc512x_clk_divider(
+ "sdhc2-ug", "sdhc-x4", 0, &clkregs->scfr2,
+ 9, 7, CLK_DIVIDER_ONE_BASED);
+ }
+
clks[MPC512x_CLK_DIU_x4] = mpc512x_clk_factor("diu-x4", "csb", 4, 1);
clks[MPC512x_CLK_DIU_UG] = mpc512x_clk_divider("diu-ug", "diu-x4", 0,
&clkregs->scfr1, 0, 8,
@@ -592,19 +779,32 @@ static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq)
div = 2; /* compensate for the fractional factor */
clks[MPC512x_CLK_E300] = mpc512x_clk_factor("e300", "csb", mul, div);
- clks[MPC512x_CLK_MBX_BUS_UG] = mpc512x_clk_factor("mbx-bus-ug", "csb",
- 1, 2);
- clks[MPC512x_CLK_MBX_UG] = mpc512x_clk_divtable("mbx-ug", "mbx-bus-ug",
- &clkregs->scfr1, 14, 3,
- divtab_1234);
- clks[MPC512x_CLK_MBX_3D_UG] = mpc512x_clk_factor("mbx-3d-ug", "mbx-ug",
- 1, 1);
- clks[MPC512x_CLK_PCI_UG] = mpc512x_clk_divtable("pci-ug", "csb",
- &clkregs->scfr1, 20, 3,
- divtab_2346);
- clks[MPC512x_CLK_NFC_UG] = mpc512x_clk_divtable("nfc-ug", "ips",
- &clkregs->scfr1, 8, 3,
- divtab_1234);
+ if (soc_has_mbx()) {
+ clks[MPC512x_CLK_MBX_BUS_UG] = mpc512x_clk_factor(
+ "mbx-bus-ug", "csb", 1, 2);
+ clks[MPC512x_CLK_MBX_UG] = mpc512x_clk_divtable(
+ "mbx-ug", "mbx-bus-ug", &clkregs->scfr1,
+ 14, 3, divtab_1234);
+ clks[MPC512x_CLK_MBX_3D_UG] = mpc512x_clk_factor(
+ "mbx-3d-ug", "mbx-ug", 1, 1);
+ }
+ if (soc_has_pci()) {
+ clks[MPC512x_CLK_PCI_UG] = mpc512x_clk_divtable(
+ "pci-ug", "csb", &clkregs->scfr1,
+ 20, 3, divtab_2346);
+ }
+ if (soc_has_nfc_5125()) {
+ /*
+ * XXX TODO implement 5125 NFC clock setup logic,
+ * with high/low period counters in clkregs->scfr3,
+ * currently there are no users so it's ENOIMPL
+ */
+ clks[MPC512x_CLK_NFC_UG] = ERR_PTR(-ENOTSUPP);
+ } else {
+ clks[MPC512x_CLK_NFC_UG] = mpc512x_clk_divtable(
+ "nfc-ug", "ips", &clkregs->scfr1,
+ 8, 3, divtab_1234);
+ }
clks[MPC512x_CLK_LPC_UG] = mpc512x_clk_divtable("lpc-ug", "ips",
&clkregs->scfr1, 11, 3,
divtab_1234);
@@ -613,10 +813,12 @@ static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq)
&clkregs->sccr1, 30);
clks[MPC512x_CLK_NFC] = mpc512x_clk_gated("nfc", "nfc-ug",
&clkregs->sccr1, 29);
- clks[MPC512x_CLK_PATA] = mpc512x_clk_gated("pata", "ips",
- &clkregs->sccr1, 28);
+ if (soc_has_pata()) {
+ clks[MPC512x_CLK_PATA] = mpc512x_clk_gated(
+ "pata", "ips", &clkregs->sccr1, 28);
+ }
/* for PSCs there is a "registers" gate and a bitrate MCLK subtree */
- for (mclk_idx = 0; mclk_idx < ARRAY_SIZE(mclk_psc_data); mclk_idx++) {
+ for (mclk_idx = 0; mclk_idx < soc_max_pscnum(); mclk_idx++) {
char name[12];
snprintf(name, sizeof(name), "psc%d", mclk_idx);
clks[MPC512x_CLK_PSC0 + mclk_idx] = mpc512x_clk_gated(
@@ -625,19 +827,29 @@ static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq)
}
clks[MPC512x_CLK_PSC_FIFO] = mpc512x_clk_gated("psc-fifo", "ips",
&clkregs->sccr1, 15);
- clks[MPC512x_CLK_SATA] = mpc512x_clk_gated("sata", "ips",
- &clkregs->sccr1, 14);
+ if (soc_has_sata()) {
+ clks[MPC512x_CLK_SATA] = mpc512x_clk_gated(
+ "sata", "ips", &clkregs->sccr1, 14);
+ }
clks[MPC512x_CLK_FEC] = mpc512x_clk_gated("fec", "ips",
&clkregs->sccr1, 13);
- clks[MPC512x_CLK_PCI] = mpc512x_clk_gated("pci", "pci-ug",
- &clkregs->sccr1, 11);
+ if (soc_has_pci()) {
+ clks[MPC512x_CLK_PCI] = mpc512x_clk_gated(
+ "pci", "pci-ug", &clkregs->sccr1, 11);
+ }
clks[MPC512x_CLK_DDR] = mpc512x_clk_gated("ddr", "ddr-ug",
&clkregs->sccr1, 10);
+ if (soc_has_fec2()) {
+ clks[MPC512x_CLK_FEC2] = mpc512x_clk_gated(
+ "fec2", "ips", &clkregs->sccr1, 9);
+ }
clks[MPC512x_CLK_DIU] = mpc512x_clk_gated("diu", "diu-ug",
&clkregs->sccr2, 31);
- clks[MPC512x_CLK_AXE] = mpc512x_clk_gated("axe", "csb",
- &clkregs->sccr2, 30);
+ if (soc_has_axe()) {
+ clks[MPC512x_CLK_AXE] = mpc512x_clk_gated(
+ "axe", "csb", &clkregs->sccr2, 30);
+ }
clks[MPC512x_CLK_MEM] = mpc512x_clk_gated("mem", "ips",
&clkregs->sccr2, 29);
clks[MPC512x_CLK_USB1] = mpc512x_clk_gated("usb1", "csb",
@@ -654,21 +866,35 @@ static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq)
clks[MPC512x_CLK_SDHC] = mpc512x_clk_gated("sdhc", "sdhc-ug",
&clkregs->sccr2, 24);
/* there is only one SPDIF component, which shares MCLK support code */
- clks[MPC512x_CLK_SPDIF] = mpc512x_clk_gated("spdif", "ips",
- &clkregs->sccr2, 23);
- mpc512x_clk_setup_mclk(&mclk_spdif_data[0], 0);
- clks[MPC512x_CLK_MBX_BUS] = mpc512x_clk_gated("mbx-bus", "mbx-bus-ug",
- &clkregs->sccr2, 22);
- clks[MPC512x_CLK_MBX] = mpc512x_clk_gated("mbx", "mbx-ug",
- &clkregs->sccr2, 21);
- clks[MPC512x_CLK_MBX_3D] = mpc512x_clk_gated("mbx-3d", "mbx-3d-ug",
- &clkregs->sccr2, 20);
+ if (soc_has_spdif()) {
+ clks[MPC512x_CLK_SPDIF] = mpc512x_clk_gated(
+ "spdif", "ips", &clkregs->sccr2, 23);
+ mpc512x_clk_setup_mclk(&mclk_spdif_data[0], 0);
+ }
+ if (soc_has_mbx()) {
+ clks[MPC512x_CLK_MBX_BUS] = mpc512x_clk_gated(
+ "mbx-bus", "mbx-bus-ug", &clkregs->sccr2, 22);
+ clks[MPC512x_CLK_MBX] = mpc512x_clk_gated(
+ "mbx", "mbx-ug", &clkregs->sccr2, 21);
+ clks[MPC512x_CLK_MBX_3D] = mpc512x_clk_gated(
+ "mbx-3d", "mbx-3d-ug", &clkregs->sccr2, 20);
+ }
clks[MPC512x_CLK_IIM] = mpc512x_clk_gated("iim", "csb",
&clkregs->sccr2, 19);
- clks[MPC512x_CLK_VIU] = mpc512x_clk_gated("viu", "csb",
- &clkregs->sccr2, 18);
- clks[MPC512x_CLK_SDHC_2] = mpc512x_clk_gated("sdhc-2", "sdhc-ug",
- &clkregs->sccr2, 17);
+ if (soc_has_viu()) {
+ clks[MPC512x_CLK_VIU] = mpc512x_clk_gated(
+ "viu", "csb", &clkregs->sccr2, 18);
+ }
+ if (soc_has_sdhc2()) {
+ clks[MPC512x_CLK_SDHC2] = mpc512x_clk_gated(
+ "sdhc-2", "sdhc2-ug", &clkregs->sccr2, 17);
+ }
+
+ if (soc_has_outclk()) {
+ size_t idx; /* used as mclk_idx, just to trim line length */
+ for (idx = 0; idx < ARRAY_SIZE(mclk_outclk_data); idx++)
+ mpc512x_clk_setup_mclk(&mclk_outclk_data[idx], idx);
+ }
/*
* externally provided clocks (when implemented in hardware,
@@ -678,10 +904,18 @@ static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq)
if (!freq)
freq = 25000000;
clks[MPC512x_CLK_PSC_MCLK_IN] = mpc512x_clk_fixed("psc_mclk_in", freq);
- freq = get_freq_from_dt("spdif_tx_in");
- clks[MPC512x_CLK_SPDIF_TX_IN] = mpc512x_clk_fixed("spdif_tx_in", freq);
- freq = get_freq_from_dt("spdif_rx_in");
- clks[MPC512x_CLK_SPDIF_TX_IN] = mpc512x_clk_fixed("spdif_rx_in", freq);
+ if (soc_has_mclk_mux0_canin()) {
+ freq = get_freq_from_dt("can_clk_in");
+ clks[MPC512x_CLK_CAN_CLK_IN] = mpc512x_clk_fixed(
+ "can_clk_in", freq);
+ } else {
+ freq = get_freq_from_dt("spdif_tx_in");
+ clks[MPC512x_CLK_SPDIF_TX_IN] = mpc512x_clk_fixed(
+ "spdif_tx_in", freq);
+ freq = get_freq_from_dt("spdif_rx_in");
+ clks[MPC512x_CLK_SPDIF_TX_IN] = mpc512x_clk_fixed(
+ "spdif_rx_in", freq);
+ }
/* fixed frequency for AC97, always 24.567MHz */
clks[MPC512x_CLK_AC97] = mpc512x_clk_fixed("ac97", 24567000);
@@ -883,6 +1117,20 @@ static void mpc5121_clk_provide_backwards_compat(void)
NODE_PREP;
NODE_CHK("per", clks[MPC512x_CLK_FEC], 0, FEC);
}
+ /*
+ * MPC5125 has two FECs: FEC1 at 0x2800, FEC2 at 0x4800;
+ * the clock items don't "form an array" since FEC2 was
+ * added only later and was not allowed to shift all other
+ * clock item indices, so the numbers aren't adjacent
+ */
+ FOR_NODES("fsl,mpc5125-fec") {
+ NODE_PREP;
+ if (res.start & 0x4000)
+ idx = MPC512x_CLK_FEC2;
+ else
+ idx = MPC512x_CLK_FEC;
+ NODE_CHK("per", clks[idx], 0, FEC);
+ }
FOR_NODES("fsl,mpc5121-usb2-dr") {
NODE_PREP;
@@ -932,6 +1180,9 @@ int __init mpc5121_clk_init(void)
clkregs = of_iomap(clk_np, 0);
WARN_ON(!clkregs);
+ /* determine the SoC variant we run on */
+ mpc512x_clk_determine_soc();
+
/* invalidate all not yet registered clock slots */
mpc512x_clk_preset_data();