summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/512x
diff options
context:
space:
mode:
authorGerhard Sittig <gsi@denx.de>2013-12-10 17:11:36 +0400
committerAnatolij Gustschin <agust@denx.de>2014-01-12 21:59:36 +0400
commit319bbe0ef513e33ecf1a3f40099b5b369122afbd (patch)
tree07d7cbef2353a81ec2edefb20d6da56a0e80d342 /arch/powerpc/platforms/512x
parent76922ebb02fb531142285682352ccdd7091a2bfb (diff)
downloadlinux-319bbe0ef513e33ecf1a3f40099b5b369122afbd.tar.xz
powerpc/512x: clk: support MPC5121/5123/5125 SoC variants
improve the common clock support code for MPC512x - expand the CCM register set declaration with MPC5125 related registers (which reside in the previously "reserved" area) - tell the MPC5121, MPC5123, and MPC5125 SoC variants apart, and derive the availability of components and their clocks from the detected SoC (MBX, AXE, VIU, SPDIF, PATA, SATA, PCI, second FEC, second SDHC, number of PSC components, type of NAND flash controller, interpretation of the CPMF bitfield, PSC/CAN mux0 stage input clocks, output clocks on SoC pins) - add backwards compatibility (allow operation against a device tree which lacks clock related specs) for MPC5125 FECs, too telling SoC variants apart and adjusting the clock tree's generation occurs at runtime, a common generic binary supports all of the chips the MPC5125 approach to the NFC clock (one register with two counters for the high and low periods of the clock) is not implemented, as there are no users and there is no common implementation which supports this kind of clock -- the new implementation would be unused and could not get verified, so it shall wait until there is demand Signed-off-by: Gerhard Sittig <gsi@denx.de> Acked-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Anatolij Gustschin <agust@denx.de>
Diffstat (limited to 'arch/powerpc/platforms/512x')
-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();