From 4cc5e1ca547738578182f7ae9ec5ff8d2a9c86c8 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 30 Nov 2013 23:51:22 +0100 Subject: dts: mpc512x: introduce dt-bindings/clock/ header introduce a dt-bindings/ header file for MPC512x clocks, providing symbolic identifiers for those SoC clocks which clients will reference from their device tree nodes Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Reviewed-by: Mike Turquette Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- include/dt-bindings/clock/mpc512x-clock.h | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 include/dt-bindings/clock/mpc512x-clock.h diff --git a/include/dt-bindings/clock/mpc512x-clock.h b/include/dt-bindings/clock/mpc512x-clock.h new file mode 100644 index 000000000000..9e81b3b99a32 --- /dev/null +++ b/include/dt-bindings/clock/mpc512x-clock.h @@ -0,0 +1,69 @@ +/* + * This header provides constants for MPC512x clock specs in DT bindings. + */ + +#ifndef _DT_BINDINGS_CLOCK_MPC512x_CLOCK_H +#define _DT_BINDINGS_CLOCK_MPC512x_CLOCK_H + +#define MPC512x_CLK_DUMMY 0 +#define MPC512x_CLK_REF 1 +#define MPC512x_CLK_SYS 2 +#define MPC512x_CLK_DIU 3 +#define MPC512x_CLK_VIU 4 +#define MPC512x_CLK_CSB 5 +#define MPC512x_CLK_E300 6 +#define MPC512x_CLK_IPS 7 +#define MPC512x_CLK_FEC 8 +#define MPC512x_CLK_SATA 9 +#define MPC512x_CLK_PATA 10 +#define MPC512x_CLK_NFC 11 +#define MPC512x_CLK_LPC 12 +#define MPC512x_CLK_MBX_BUS 13 +#define MPC512x_CLK_MBX 14 +#define MPC512x_CLK_MBX_3D 15 +#define MPC512x_CLK_AXE 16 +#define MPC512x_CLK_USB1 17 +#define MPC512x_CLK_USB2 18 +#define MPC512x_CLK_I2C 19 +#define MPC512x_CLK_MSCAN0_MCLK 20 +#define MPC512x_CLK_MSCAN1_MCLK 21 +#define MPC512x_CLK_MSCAN2_MCLK 22 +#define MPC512x_CLK_MSCAN3_MCLK 23 +#define MPC512x_CLK_BDLC 24 +#define MPC512x_CLK_SDHC 25 +#define MPC512x_CLK_PCI 26 +#define MPC512x_CLK_PSC_MCLK_IN 27 +#define MPC512x_CLK_SPDIF_TX 28 +#define MPC512x_CLK_SPDIF_RX 29 +#define MPC512x_CLK_SPDIF_MCLK 30 +#define MPC512x_CLK_SPDIF 31 +#define MPC512x_CLK_AC97 32 +#define MPC512x_CLK_PSC0_MCLK 33 +#define MPC512x_CLK_PSC1_MCLK 34 +#define MPC512x_CLK_PSC2_MCLK 35 +#define MPC512x_CLK_PSC3_MCLK 36 +#define MPC512x_CLK_PSC4_MCLK 37 +#define MPC512x_CLK_PSC5_MCLK 38 +#define MPC512x_CLK_PSC6_MCLK 39 +#define MPC512x_CLK_PSC7_MCLK 40 +#define MPC512x_CLK_PSC8_MCLK 41 +#define MPC512x_CLK_PSC9_MCLK 42 +#define MPC512x_CLK_PSC10_MCLK 43 +#define MPC512x_CLK_PSC11_MCLK 44 +#define MPC512x_CLK_PSC_FIFO 45 +#define MPC512x_CLK_PSC0 46 +#define MPC512x_CLK_PSC1 47 +#define MPC512x_CLK_PSC2 48 +#define MPC512x_CLK_PSC3 49 +#define MPC512x_CLK_PSC4 50 +#define MPC512x_CLK_PSC5 51 +#define MPC512x_CLK_PSC6 52 +#define MPC512x_CLK_PSC7 53 +#define MPC512x_CLK_PSC8 54 +#define MPC512x_CLK_PSC9 55 +#define MPC512x_CLK_PSC10 56 +#define MPC512x_CLK_PSC11 57 + +#define MPC512x_CLK_LAST_PUBLIC 57 + +#endif -- cgit v1.2.3 From f87ccd2edcdbe86ffb7cf7286e1c7aa84d5e5af9 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 30 Nov 2013 23:51:23 +0100 Subject: dts: mpc512x: add clock related device tree specs this addresses the clock driver aka provider's side of clocks - introduce a 'clocks' subtree with an 'osc' node for the crystal or oscillator SoC input (fixed frequency) - the 'clock@f00' clock-control-module node references the 'osc' for its input, and is another provider for all the clocks which the CCM component manages - prepare for future references to clocks from peripheral nodes by means of the <&clks ID> syntax and symbolic ID names which a header file provides - provide default values with 33MHz oscillator frequency in the common include (the 66MHz IPS bus already was there), and add override values for the ifm AC14xx board which deviates from the reference design (25MHz xtal, 80MHz IPS bus) Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree@vger.kernel.org Reviewed-by: Mike Turquette Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/ac14xx.dts | 7 +++++++ arch/powerpc/boot/dts/mpc5121.dtsi | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/boot/dts/ac14xx.dts b/arch/powerpc/boot/dts/ac14xx.dts index a543c4088cba..a1b883730b31 100644 --- a/arch/powerpc/boot/dts/ac14xx.dts +++ b/arch/powerpc/boot/dts/ac14xx.dts @@ -139,7 +139,14 @@ }; }; + clocks { + osc { + clock-frequency = <25000000>; + }; + }; + soc@80000000 { + bus-frequency = <80000000>; /* 80 MHz ips bus */ clock@f00 { compatible = "fsl,mpc5121rev2-clock", "fsl,mpc5121-clock"; diff --git a/arch/powerpc/boot/dts/mpc5121.dtsi b/arch/powerpc/boot/dts/mpc5121.dtsi index 2d7cb04ac962..1d533e083e3c 100644 --- a/arch/powerpc/boot/dts/mpc5121.dtsi +++ b/arch/powerpc/boot/dts/mpc5121.dtsi @@ -9,6 +9,8 @@ * option) any later version. */ +#include + /dts-v1/; / { @@ -73,6 +75,17 @@ ranges = <0x0 0x0 0xfc000000 0x04000000>; }; + clocks { + #address-cells = <1>; + #size-cells = <0>; + + osc: osc { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <33000000>; + }; + }; + soc@80000000 { compatible = "fsl,mpc5121-immr"; #address-cells = <1>; @@ -117,9 +130,12 @@ }; /* Clock control */ - clock@f00 { + clks: clock@f00 { compatible = "fsl,mpc5121-clock"; reg = <0xf00 0x100>; + #clock-cells = <1>; + clocks = <&osc>; + clock-names = "osc"; }; /* Power Management Controller */ -- cgit v1.2.3 From 6d8cdb68249d2a5d2504a7bc79a8cfb517e07020 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 30 Nov 2013 23:51:24 +0100 Subject: clk: mpc512x: introduce COMMON_CLK for MPC512x (disabled) this change implements a clock driver for the MPC512x PowerPC platform which follows the COMMON_CLK approach and uses common clock drivers shared with other platforms this driver implements the publicly announced set of clocks (those listed in the dt-bindings header file), as well as generates additional 'struct clk' items where the SoC hardware cannot easily get mapped to the common primitives (shared code) of the clock API, or requires "intermediate clock nodes" to represent clocks that have both gates and dividers the previous PPC_CLOCK implementation is kept in place and remains active for the moment, the newly introduced CCF clock driver will receive additional support for backwards compatibility in a subsequent patch before it gets enabled and will replace the PPC_CLOCK approach some of the clock items get pre-enabled in the clock driver to not have them automatically disabled by the underlying clock subsystem because of their being unused -- this approach is desirable because - some of the clocks are useful to have for diagnostics and information despite their not getting claimed by any drivers (CPU, internal and external RAM, internal busses, boot media) - some of the clocks aren't claimed by their peripheral drivers yet, either because of missing driver support or because device tree specs aren't available yet (but the workarounds will get removed as the drivers get adjusted and the device tree provides the clock specs) clkdev registration provides "alias names" for few clock items - to not break those peripheral drivers which encode their component index into the name that is used for clock lookup (UART, SPI, USB) - to not break those drivers which use names for the clock lookup which were encoded in the previous PPC_CLOCK implementation (NFC, VIU, CAN) this workaround will get removed as these drivers get adjusted after device tree based clock lookup has become available the COMMON_CLK implementation copes with device trees which lack an oscillator node (backwards compat), the REF clock is then derived from the IPS bus frequency and multiplier values fetched from hardware Cc: Mike Turquette Cc: Anatolij Gustschin Cc: linux-arm-kernel@lists.infradead.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/512x/Makefile | 4 +- arch/powerpc/platforms/512x/clock-commonclk.c | 798 ++++++++++++++++++++++++++ include/linux/clk-provider.h | 16 + 3 files changed, 817 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/platforms/512x/clock-commonclk.c diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile index 72fb9340e09f..1e05f9def8a4 100644 --- a/arch/powerpc/platforms/512x/Makefile +++ b/arch/powerpc/platforms/512x/Makefile @@ -1,7 +1,9 @@ # # Makefile for the Freescale PowerPC 512x linux kernel. # -obj-y += clock.o mpc512x_shared.o +obj-$(CONFIG_PPC_CLOCK) += clock.o +obj-$(CONFIG_COMMON_CLK) += clock-commonclk.o +obj-y += mpc512x_shared.o obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o mpc5121_ads_cpld.o obj-$(CONFIG_MPC512x_GENERIC) += mpc512x_generic.o obj-$(CONFIG_PDM360NG) += pdm360ng.o diff --git a/arch/powerpc/platforms/512x/clock-commonclk.c b/arch/powerpc/platforms/512x/clock-commonclk.c new file mode 100644 index 000000000000..818927248392 --- /dev/null +++ b/arch/powerpc/platforms/512x/clock-commonclk.c @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2013 DENX Software Engineering + * + * Gerhard Sittig, + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#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_MCLKS (NR_PSCS + NR_MSCANS + NR_SPDIFS) + +/* 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, + 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_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); + +/* convenience wrappers around the common clk API */ +static inline struct clk *mpc512x_clk_fixed(const char *name, int rate) +{ + return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); +} + +static inline struct clk *mpc512x_clk_factor( + const char *name, const char *parent_name, + int mul, int div) +{ + int clkflags; + + clkflags = CLK_SET_RATE_PARENT; + return clk_register_fixed_factor(NULL, name, parent_name, clkflags, + mul, div); +} + +static inline struct clk *mpc512x_clk_divider( + const char *name, const char *parent_name, u8 clkflags, + u32 __iomem *reg, u8 pos, u8 len, int divflags) +{ + return clk_register_divider(NULL, name, parent_name, clkflags, + reg, pos, len, divflags, &clklock); +} + +static inline struct clk *mpc512x_clk_divtable( + const char *name, const char *parent_name, + u32 __iomem *reg, u8 pos, u8 len, + const struct clk_div_table *divtab) +{ + u8 divflags; + + divflags = 0; + return clk_register_divider_table(NULL, name, parent_name, 0, + reg, pos, len, divflags, + divtab, &clklock); +} + +static inline struct clk *mpc512x_clk_gated( + const char *name, const char *parent_name, + u32 __iomem *reg, u8 pos) +{ + int clkflags; + + clkflags = CLK_SET_RATE_PARENT; + return clk_register_gate(NULL, name, parent_name, clkflags, + reg, pos, 0, &clklock); +} + +static inline struct clk *mpc512x_clk_muxed(const char *name, + const char **parent_names, int parent_count, + u32 __iomem *reg, u8 pos, u8 len) +{ + int clkflags; + u8 muxflags; + + clkflags = CLK_SET_RATE_PARENT; + muxflags = 0; + return clk_register_mux(NULL, name, + parent_names, parent_count, clkflags, + reg, pos, len, muxflags, &clklock); +} + +/* helper to isolate a bit field from a register */ +static inline int get_bit_field(uint32_t __iomem *reg, uint8_t pos, uint8_t len) +{ + uint32_t val; + + val = in_be32(reg); + val >>= pos; + val &= (1 << len) - 1; + return val; +} + +/* get the SPMF and translate it into the "sys pll" multiplier */ +static int get_spmf_mult(void) +{ + static int spmf_to_mult[] = { + 68, 1, 12, 16, 20, 24, 28, 32, + 36, 40, 44, 48, 52, 56, 60, 64, + }; + int spmf; + + spmf = get_bit_field(&clkregs->spmr, 24, 4); + return spmf_to_mult[spmf]; +} + +/* + * get the SYS_DIV value and translate it into a divide factor + * + * values returned from here are a multiple of the real factor since the + * divide ratio is fractional + */ +static int get_sys_div_x2(void) +{ + static int sysdiv_code_to_x2[] = { + 4, 5, 6, 7, 8, 9, 10, 14, + 12, 16, 18, 22, 20, 24, 26, 30, + 28, 32, 34, 38, 36, 40, 42, 46, + 44, 48, 50, 54, 52, 56, 58, 62, + 60, 64, 66, + }; + int divcode; + + divcode = get_bit_field(&clkregs->scfr2, 26, 6); + return sysdiv_code_to_x2[divcode]; +} + +/* + * get the CPMF value and translate it into a multiplier factor + * + * values returned from here are a multiple of the real factor since the + * multiplier ratio is fractional + */ +static int get_cpmf_mult_x2(void) +{ + static int cpmf_to_mult[] = { + 72, 2, 2, 3, 4, 5, 6, 7, + }; + int cpmf; + + cpmf = get_bit_field(&clkregs->spmr, 16, 4); + return cpmf_to_mult[cpmf]; +} + +/* + * some of the clock dividers do scale in a linear way, yet not all of + * their bit combinations are legal; use a divider table to get a + * resulting set of applicable divider values + */ + +/* applies to the IPS_DIV, and PCI_DIV values */ +static struct clk_div_table divtab_2346[] = { + { .val = 2, .div = 2, }, + { .val = 3, .div = 3, }, + { .val = 4, .div = 4, }, + { .val = 6, .div = 6, }, + { .div = 0, }, +}; + +/* applies to the MBX_DIV, LPC_DIV, and NFC_DIV values */ +static struct clk_div_table divtab_1234[] = { + { .val = 1, .div = 1, }, + { .val = 2, .div = 2, }, + { .val = 3, .div = 3, }, + { .val = 4, .div = 4, }, + { .div = 0, }, +}; + +static int get_freq_from_dt(char *propname) +{ + struct device_node *np; + const unsigned int *prop; + int val; + + val = 0; + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-immr"); + if (np) { + prop = of_get_property(np, propname, NULL); + if (prop) + val = *prop; + of_node_put(np); + } + return val; +} + +static void mpc512x_clk_preset_data(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(clks); i++) + clks[i] = ERR_PTR(-ENODEV); +} + +/* + * - receives the "bus frequency" from the caller (that's the IPS clock + * rate, the historical source of clock information) + * - fetches the system PLL multiplier and divider values as well as the + * IPS divider value from hardware + * - determines the REF clock rate either from the XTAL/OSC spec (if + * there is a device tree node describing the oscillator) or from the + * IPS bus clock (supported for backwards compatibility, such that + * setups without XTAL/OSC specs keep working) + * - creates the "ref" clock item in the clock tree, such that + * subsequent code can create the remainder of the hierarchy (REF -> + * SYS -> CSB -> IPS) from the REF clock rate and the returned mul/div + * values + */ +static void mpc512x_clk_setup_ref_clock(struct device_node *np, int bus_freq, + int *sys_mul, int *sys_div, + int *ips_div) +{ + struct clk *osc_clk; + int calc_freq; + + /* fetch mul/div factors from the hardware */ + *sys_mul = get_spmf_mult(); + *sys_mul *= 2; /* compensate for the fractional divider */ + *sys_div = get_sys_div_x2(); + *ips_div = get_bit_field(&clkregs->scfr1, 23, 3); + + /* lookup the oscillator clock for its rate */ + osc_clk = of_clk_get_by_name(np, "osc"); + + /* + * either descend from OSC to REF (and in bypassing verify the + * IPS rate), or backtrack from IPS and multiplier values that + * were fetched from hardware to REF and thus to the OSC value + * + * in either case the REF clock gets created here and the + * remainder of the clock tree can get spanned from there + */ + if (!IS_ERR(osc_clk)) { + clks[MPC512x_CLK_REF] = mpc512x_clk_factor("ref", "osc", 1, 1); + calc_freq = clk_get_rate(clks[MPC512x_CLK_REF]); + calc_freq *= *sys_mul; + calc_freq /= *sys_div; + calc_freq /= 2; + calc_freq /= *ips_div; + if (bus_freq && calc_freq != bus_freq) + pr_warn("calc rate %d != OF spec %d\n", + calc_freq, bus_freq); + } else { + calc_freq = bus_freq; /* start with IPS */ + calc_freq *= *ips_div; /* IPS -> CSB */ + calc_freq *= 2; /* CSB -> SYS */ + calc_freq *= *sys_div; /* SYS -> PLL out */ + calc_freq /= *sys_mul; /* PLL out -> REF == OSC */ + clks[MPC512x_CLK_REF] = mpc512x_clk_fixed("ref", calc_freq); + } +} + +/* + * helper code for the MCLK subtree setup + * + * the overview in section 5.2.4 of the MPC5121e Reference Manual rev4 + * suggests that all instances of the "PSC clock generation" are equal, + * and that one might re-use the PSC setup for MSCAN clock generation + * (section 5.2.5) as well, at least the logic if not the data for + * description + * + * the details (starting at page 5-20) show differences in the specific + * inputs of the first mux stage ("can clk in", "spdif tx"), and the + * factual non-availability of the second mux stage (it's present yet + * only one input is valid) + * + * the MSCAN clock related registers (starting at page 5-35) all + * reference "spdif clk" at the first mux stage and don't mention any + * "can clk" at all, which somehow is unexpected + * + * TODO re-check the document, and clarify whether the RM is correct in + * the overview or in the details, and whether the difference is a + * clipboard induced error or results from chip revisions + * + * it turns out that the RM rev4 as of 2012-06 talks about "can" for the + * PSCs while RM rev3 as of 2008-10 talks about "spdif", so I guess that + * first a doc update is required which better reflects reality in the + * SoC before the implementation should follow while no questions remain + */ + +/* + * note that this declaration raises a checkpatch warning, but + * it's the very data type which expects, + * making this declaration pass checkpatch will break compilation + */ +static const char *parent_names_mux0[] = { + "sys", "ref", "psc-mclk-in", "spdif-tx", +}; + +enum mclk_type { + MCLK_TYPE_PSC, + MCLK_TYPE_MSCAN, + MCLK_TYPE_SPDIF, +}; + +struct mclk_setup_data { + enum mclk_type type; + bool has_mclk1; + const char *name_mux0; + const char *name_en0; + const char *name_div0; + const char *parent_names_mux1[2]; + const char *name_mclk; +}; + +#define MCLK_SETUP_DATA_PSC(id) { \ + MCLK_TYPE_PSC, 0, \ + "psc" #id "-mux0", \ + "psc" #id "-en0", \ + "psc" #id "_mclk_div", \ + { "psc" #id "_mclk_div", "dummy", }, \ + "psc" #id "_mclk", \ +} + +#define MCLK_SETUP_DATA_MSCAN(id) { \ + MCLK_TYPE_MSCAN, 0, \ + "mscan" #id "-mux0", \ + "mscan" #id "-en0", \ + "mscan" #id "_mclk_div", \ + { "mscan" #id "_mclk_div", "dummy", }, \ + "mscan" #id "_mclk", \ +} + +#define MCLK_SETUP_DATA_SPDIF { \ + MCLK_TYPE_SPDIF, 1, \ + "spdif-mux0", \ + "spdif-en0", \ + "spdif_mclk_div", \ + { "spdif_mclk_div", "spdif-rx", }, \ + "spdif_mclk", \ +} + +static struct mclk_setup_data mclk_psc_data[] = { + MCLK_SETUP_DATA_PSC(0), + MCLK_SETUP_DATA_PSC(1), + MCLK_SETUP_DATA_PSC(2), + MCLK_SETUP_DATA_PSC(3), + MCLK_SETUP_DATA_PSC(4), + MCLK_SETUP_DATA_PSC(5), + MCLK_SETUP_DATA_PSC(6), + MCLK_SETUP_DATA_PSC(7), + MCLK_SETUP_DATA_PSC(8), + MCLK_SETUP_DATA_PSC(9), + MCLK_SETUP_DATA_PSC(10), + MCLK_SETUP_DATA_PSC(11), +}; + +static struct mclk_setup_data mclk_mscan_data[] = { + MCLK_SETUP_DATA_MSCAN(0), + MCLK_SETUP_DATA_MSCAN(1), + MCLK_SETUP_DATA_MSCAN(2), + MCLK_SETUP_DATA_MSCAN(3), +}; + +static struct mclk_setup_data mclk_spdif_data[] = { + MCLK_SETUP_DATA_SPDIF, +}; + +/* 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) +{ + size_t clks_idx_pub, clks_idx_int; + u32 __iomem *mccr_reg; /* MCLK control register (mux, en, div) */ + int div; + + /* derive a few parameters from the component type and index */ + switch (entry->type) { + case MCLK_TYPE_PSC: + clks_idx_pub = MPC512x_CLK_PSC0_MCLK + idx; + clks_idx_int = MPC512x_CLK_MCLKS_FIRST + + (idx) * MCLK_MAX_IDX; + mccr_reg = &clkregs->psc_ccr[idx]; + break; + case MCLK_TYPE_MSCAN: + clks_idx_pub = MPC512x_CLK_MSCAN0_MCLK + idx; + clks_idx_int = MPC512x_CLK_MCLKS_FIRST + + (NR_PSCS + idx) * MCLK_MAX_IDX; + mccr_reg = &clkregs->mscan_ccr[idx]; + break; + case MCLK_TYPE_SPDIF: + clks_idx_pub = MPC512x_CLK_SPDIF_MCLK; + clks_idx_int = MPC512x_CLK_MCLKS_FIRST + + (NR_PSCS + NR_MSCANS) * MCLK_MAX_IDX; + mccr_reg = &clkregs->spccr; + break; + default: + return; + } + + /* + * this was grabbed from the PPC_CLOCK implementation, which + * enforced a specific MCLK divider while the clock was gated + * during setup (that's a documented hardware requirement) + * + * the PPC_CLOCK implementation might even have violated the + * "MCLK <= IPS" constraint, the fixed divider value of 1 + * results in a divider of 2 and thus MCLK = SYS/2 which equals + * CSB which is greater than IPS; the serial port setup may have + * adjusted the divider which the clock setup might have left in + * an undesirable state + * + * initial setup is: + * - MCLK 0 from SYS + * - MCLK DIV such to not exceed the IPS clock + * - MCLK 0 enabled + * - MCLK 1 from MCLK DIV + */ + div = clk_get_rate(clks[MPC512x_CLK_SYS]); + div /= clk_get_rate(clks[MPC512x_CLK_IPS]); + out_be32(mccr_reg, (0 << 16)); + out_be32(mccr_reg, (0 << 16) | ((div - 1) << 17)); + out_be32(mccr_reg, (1 << 16) | ((div - 1) << 17)); + + /* + * create the 'struct clk' items of the MCLK's clock subtree + * + * note that by design we always create all nodes and won't take + * shortcuts here, because + * - the "internal" MCLK_DIV and MCLK_OUT signal in turn are + * selectable inputs to the CFM while those who "actually use" + * the PSC/MSCAN/SPDIF (serial drivers et al) need the MCLK + * for their bitrate + * - in the absence of "aliases" for clocks we need to create + * individial 'struct clk' items for whatever might get + * referenced or looked up, even if several of those items are + * identical from the logical POV (their rate value) + * - for easier future maintenance and for better reflection of + * the SoC's documentation, it appears appropriate to generate + * clock items even for those muxers which actually are NOPs + * (those with two inputs of which one is reserved) + */ + clks[clks_idx_int + MCLK_IDX_MUX0] = mpc512x_clk_muxed( + entry->name_mux0, + &parent_names_mux0[0], ARRAY_SIZE(parent_names_mux0), + mccr_reg, 14, 2); + clks[clks_idx_int + MCLK_IDX_EN0] = mpc512x_clk_gated( + entry->name_en0, entry->name_mux0, + mccr_reg, 16); + clks[clks_idx_int + MCLK_IDX_DIV0] = mpc512x_clk_divider( + entry->name_div0, + entry->name_en0, CLK_SET_RATE_GATE, + mccr_reg, 17, 15, 0); + if (entry->has_mclk1) { + clks[clks_idx_pub] = mpc512x_clk_muxed( + entry->name_mclk, + &entry->parent_names_mux1[0], + ARRAY_SIZE(entry->parent_names_mux1), + mccr_reg, 7, 1); + } else { + clks[clks_idx_pub] = mpc512x_clk_factor( + entry->name_mclk, + entry->parent_names_mux1[0], + 1, 1); + } +} + +static void mpc512x_clk_setup_clock_tree(struct device_node *np, int busfreq) +{ + int sys_mul, sys_div, ips_div; + int mul, div; + size_t mclk_idx; + int freq; + + /* + * developer's notes: + * - consider whether to handle clocks which have both gates and + * dividers via intermediates or by means of composites + * - fractional dividers appear to not map well to composites + * since they can be seen as a fixed multiplier and an + * adjustable divider, while composites can only combine at + * most one of a mux, div, and gate each into one 'struct clk' + * item + * - PSC/MSCAN/SPDIF clock generation OTOH already is very + * specific and cannot get mapped to componsites (at least not + * a single one, maybe two of them, but then some of these + * intermediate clock signals get referenced elsewhere (e.g. + * in the clock frequency measurement, CFM) and thus need + * publicly available names + * - the current source layout appropriately reflects the + * hardware setup, and it works, so it's questionable whether + * further changes will result in big enough a benefit + */ + + /* regardless of whether XTAL/OSC exists, have REF created */ + mpc512x_clk_setup_ref_clock(np, busfreq, &sys_mul, &sys_div, &ips_div); + + /* now setup the REF -> SYS -> CSB -> IPS hierarchy */ + clks[MPC512x_CLK_SYS] = mpc512x_clk_factor("sys", "ref", + sys_mul, sys_div); + clks[MPC512x_CLK_CSB] = mpc512x_clk_factor("csb", "sys", 1, 2); + clks[MPC512x_CLK_IPS] = mpc512x_clk_divtable("ips", "csb", + &clkregs->scfr1, 23, 3, + divtab_2346); + + /* now setup anything below SYS and CSB and IPS */ + clks[MPC512x_CLK_DDR_UG] = mpc512x_clk_factor("ddr-ug", "sys", 1, 2); + clks[MPC512x_CLK_SDHC_x4] = mpc512x_clk_factor("sdhc-x4", "csb", 4, 1); + clks[MPC512x_CLK_SDHC_UG] = mpc512x_clk_divider("sdhc-ug", "sdhc-x4", 0, + &clkregs->scfr2, 0, 8, + 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, + CLK_DIVIDER_ONE_BASED); + + /* + * the "power architecture PLL" was setup from data which was + * sampled from the reset config word, at this point in time the + * configuration can be considered fixed and read only (i.e. no + * longer adjustable, or no longer in need of adjustment), which + * is why we don't register a PLL here but assume fixed factors + */ + mul = get_cpmf_mult_x2(); + 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); + clks[MPC512x_CLK_LPC_UG] = mpc512x_clk_divtable("lpc-ug", "ips", + &clkregs->scfr1, 11, 3, + divtab_1234); + + clks[MPC512x_CLK_LPC] = mpc512x_clk_gated("lpc", "lpc-ug", + &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); + /* 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++) { + char name[12]; + snprintf(name, sizeof(name), "psc%d", mclk_idx); + clks[MPC512x_CLK_PSC0 + mclk_idx] = mpc512x_clk_gated( + name, "ips", &clkregs->sccr1, 27 - mclk_idx); + mpc512x_clk_setup_mclk(&mclk_psc_data[mclk_idx], mclk_idx); + } + 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); + 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); + clks[MPC512x_CLK_DDR] = mpc512x_clk_gated("ddr", "ddr-ug", + &clkregs->sccr1, 10); + + 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); + clks[MPC512x_CLK_MEM] = mpc512x_clk_gated("mem", "ips", + &clkregs->sccr2, 29); + clks[MPC512x_CLK_USB1] = mpc512x_clk_gated("usb1", "csb", + &clkregs->sccr2, 28); + clks[MPC512x_CLK_USB2] = mpc512x_clk_gated("usb2", "csb", + &clkregs->sccr2, 27); + clks[MPC512x_CLK_I2C] = mpc512x_clk_gated("i2c", "ips", + &clkregs->sccr2, 26); + /* MSCAN differs from PSC with just one gate for multiple components */ + clks[MPC512x_CLK_BDLC] = mpc512x_clk_gated("bdlc", "ips", + &clkregs->sccr2, 25); + for (mclk_idx = 0; mclk_idx < ARRAY_SIZE(mclk_mscan_data); mclk_idx++) + mpc512x_clk_setup_mclk(&mclk_mscan_data[mclk_idx], mclk_idx); + 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); + 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); + + /* + * externally provided clocks (when implemented in hardware, + * device tree may specify values which otherwise were unknown) + */ + freq = get_freq_from_dt("psc_mclk_in"); + 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); + + /* fixed frequency for AC97, always 24.567MHz */ + clks[MPC512x_CLK_AC97] = mpc512x_clk_fixed("ac97", 24567000); + + /* + * pre-enable those "internal" clock items which never get + * claimed by any peripheral driver, to not have the clock + * subsystem disable them late at startup + */ + clk_prepare_enable(clks[MPC512x_CLK_DUMMY]); + clk_prepare_enable(clks[MPC512x_CLK_E300]); /* PowerPC CPU */ + clk_prepare_enable(clks[MPC512x_CLK_DDR]); /* DRAM */ + clk_prepare_enable(clks[MPC512x_CLK_MEM]); /* SRAM */ + clk_prepare_enable(clks[MPC512x_CLK_IPS]); /* SoC periph */ + clk_prepare_enable(clks[MPC512x_CLK_LPC]); /* boot media */ +} + +/* + * registers the set of public clocks (those listed in the dt-bindings/ + * header file) for OF lookups, keeps the intermediates private to us + */ +static void mpc5121_clk_register_of_provider(struct device_node *np) +{ + clk_data.clks = clks; + clk_data.clk_num = MPC512x_CLK_LAST_PUBLIC + 1; /* _not_ ARRAY_SIZE() */ + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +/* + * temporary support for the period of time between introduction of CCF + * support and the adjustment of peripheral drivers to OF based lookups + */ +static void mpc5121_clk_provide_migration_support(void) +{ + int idx; + char name[32]; + + /* + * provide "pre-CCF" alias clock names for peripheral drivers + * which have not yet been adjusted to do OF based clock lookups + */ + clk_register_clkdev(clks[MPC512x_CLK_REF], "ref_clk", NULL); + clk_register_clkdev(clks[MPC512x_CLK_SYS], "sys_clk", NULL); + clk_register_clkdev(clks[MPC512x_CLK_VIU], "viu_clk", NULL); + clk_register_clkdev(clks[MPC512x_CLK_NFC], "nfc_clk", NULL); + clk_register_clkdev(clks[MPC512x_CLK_USB1], "usb1_clk", NULL); + clk_register_clkdev(clks[MPC512x_CLK_USB2], "usb2_clk", NULL); + for (idx = 0; idx < NR_PSCS; idx++) { + snprintf(name, sizeof(name), "psc%d_mclk", idx); + clk_register_clkdev(clks[MPC512x_CLK_PSC0_MCLK + idx], + name, NULL); + } + for (idx = 0; idx < NR_MSCANS; idx++) { + snprintf(name, sizeof(name), "mscan%d_mclk", idx); + clk_register_clkdev(clks[MPC512x_CLK_MSCAN0_MCLK + idx], + name, NULL); + } + clk_register_clkdev(clks[MPC512x_CLK_SPDIF_MCLK], "spdif_mclk", NULL); + + /* + * pre-enable those clock items which are not yet appropriately + * acquired by their peripheral driver + */ + clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]); + clk_prepare_enable(clks[MPC512x_CLK_PSC3_MCLK]);/* serial console */ + clk_prepare_enable(clks[MPC512x_CLK_FEC]); /* network, NFS */ + clk_prepare_enable(clks[MPC512x_CLK_DIU]); /* display */ + clk_prepare_enable(clks[MPC512x_CLK_I2C]); /* I2C */ + for (idx = 0; idx < NR_PSCS; idx++) /* PSC ipg */ + clk_prepare_enable(clks[MPC512x_CLK_PSC0 + idx]); + clk_prepare_enable(clks[MPC512x_CLK_BDLC]); /* MSCAN ipg */ + for (idx = 0; idx < NR_MSCANS; idx++) /* MSCAN mclk */ + clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK + idx]); + clk_prepare_enable(clks[MPC512x_CLK_PCI]); /* PCI */ +} + +/* + * register source code provided fallback results for clock lookups, + * these get consulted when OF based clock lookup fails (that is in the + * case of not yet adjusted device tree data, where clock related specs + * are missing) + */ +static void mpc5121_clk_provide_backwards_compat(void) +{ + /* TODO */ +} + +int __init mpc5121_clk_init(void) +{ + struct device_node *clk_np; + int busfreq; + + /* map the clock control registers */ + clk_np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock"); + if (!clk_np) + return -ENODEV; + clkregs = of_iomap(clk_np, 0); + WARN_ON(!clkregs); + + /* invalidate all not yet registered clock slots */ + mpc512x_clk_preset_data(); + + /* + * have the device tree scanned for "fixed-clock" nodes (which + * includes the oscillator node if the board's DT provides one) + */ + of_clk_init(NULL); + + /* + * add a dummy clock for those situations where a clock spec is + * required yet no real clock is involved + */ + clks[MPC512x_CLK_DUMMY] = mpc512x_clk_fixed("dummy", 0); + + /* + * have all the real nodes in the clock tree populated from REF + * down to all leaves, either starting from the OSC node or from + * a REF root that was created from the IPS bus clock input + */ + busfreq = get_freq_from_dt("bus-frequency"); + mpc512x_clk_setup_clock_tree(clk_np, busfreq); + + /* register as an OF clock provider */ + mpc5121_clk_register_of_provider(clk_np); + + /* + * unbreak not yet adjusted peripheral drivers during migration + * towards fully operational common clock support, and allow + * operation in the absence of clock related device tree specs + */ + mpc5121_clk_provide_migration_support(); + mpc5121_clk_provide_backwards_compat(); + + return 0; +} diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7e59253b8603..534dc618f2d7 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -512,6 +512,20 @@ static inline const char *of_clk_get_parent_name(struct device_node *np, * for improved portability across platforms */ +#if IS_ENABLED(CONFIG_PPC) + +static inline u32 clk_readl(u32 __iomem *reg) +{ + return ioread32be(reg); +} + +static inline void clk_writel(u32 val, u32 __iomem *reg) +{ + iowrite32be(val, reg); +} + +#else /* platform dependent I/O accessors */ + static inline u32 clk_readl(u32 __iomem *reg) { return readl(reg); @@ -522,5 +536,7 @@ static inline void clk_writel(u32 val, u32 __iomem *reg) writel(val, reg); } +#endif /* platform dependent I/O accessors */ + #endif /* CONFIG_COMMON_CLK */ #endif /* CLK_PROVIDER_H */ -- cgit v1.2.3 From 01f25c371658d3e8769210be038941691b158985 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 30 Nov 2013 23:51:25 +0100 Subject: clk: mpc512x: add backwards compat to the CCF code extend the recently added COMMON_CLK platform support for MPC512x such that it works with incomplete device tree data which lacks clock specs Cc: Mike Turquette Cc: Anatolij Gustschin Cc: linux-arm-kernel@lists.infradead.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Gerhard Sittig [agust@denx.de: moved node macro definitions out of the function body] Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/512x/clock-commonclk.c | 172 +++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/512x/clock-commonclk.c b/arch/powerpc/platforms/512x/clock-commonclk.c index 818927248392..389c0adde328 100644 --- a/arch/powerpc/platforms/512x/clock-commonclk.c +++ b/arch/powerpc/platforms/512x/clock-commonclk.c @@ -11,6 +11,7 @@ * (at your option) any later version. */ +#include #include #include #include @@ -737,6 +738,35 @@ static void mpc5121_clk_provide_migration_support(void) clk_prepare_enable(clks[MPC512x_CLK_PCI]); /* PCI */ } +/* + * those macros are not exactly pretty, but they encapsulate a lot + * of copy'n'paste heavy code which is even more ugly, and reduce + * the potential for inconsistencies in those many code copies + */ +#define FOR_NODES(compatname) \ + for_each_compatible_node(np, NULL, compatname) + +#define NODE_PREP do { \ + of_address_to_resource(np, 0, &res); \ + snprintf(devname, sizeof(devname), "%08x.%s", res.start, np->name); \ +} while (0) + +#define NODE_CHK(clkname, clkitem, regnode, regflag) do { \ + struct clk *clk; \ + clk = of_clk_get_by_name(np, clkname); \ + if (IS_ERR(clk)) { \ + clk = clkitem; \ + clk_register_clkdev(clk, clkname, devname); \ + if (regnode) \ + clk_register_clkdev(clk, clkname, np->name); \ + did_register |= DID_REG_ ## regflag; \ + pr_debug("clock alias name '%s' for dev '%s' pointer %p\n", \ + clkname, devname, clk); \ + } else { \ + clk_put(clk); \ + } \ +} while (0) + /* * register source code provided fallback results for clock lookups, * these get consulted when OF based clock lookup fails (that is in the @@ -745,7 +775,147 @@ static void mpc5121_clk_provide_migration_support(void) */ static void mpc5121_clk_provide_backwards_compat(void) { - /* TODO */ + enum did_reg_flags { + DID_REG_PSC = BIT(0), + DID_REG_PSCFIFO = BIT(1), + DID_REG_NFC = BIT(2), + DID_REG_CAN = BIT(3), + DID_REG_I2C = BIT(4), + DID_REG_DIU = BIT(5), + DID_REG_VIU = BIT(6), + DID_REG_FEC = BIT(7), + DID_REG_USB = BIT(8), + DID_REG_PATA = BIT(9), + }; + + int did_register; + struct device_node *np; + struct resource res; + int idx; + char devname[32]; + + did_register = 0; + + FOR_NODES(mpc512x_select_psc_compat()) { + NODE_PREP; + idx = (res.start >> 8) & 0xf; + NODE_CHK("ipg", clks[MPC512x_CLK_PSC0 + idx], 0, PSC); + NODE_CHK("mclk", clks[MPC512x_CLK_PSC0_MCLK + idx], 0, PSC); + } + + FOR_NODES("fsl,mpc5121-psc-fifo") { + NODE_PREP; + NODE_CHK("ipg", clks[MPC512x_CLK_PSC_FIFO], 1, PSCFIFO); + } + + FOR_NODES("fsl,mpc5121-nfc") { + NODE_PREP; + NODE_CHK("ipg", clks[MPC512x_CLK_NFC], 0, NFC); + } + + FOR_NODES("fsl,mpc5121-mscan") { + NODE_PREP; + idx = 0; + idx += (res.start & 0x2000) ? 2 : 0; + idx += (res.start & 0x0080) ? 1 : 0; + NODE_CHK("ipg", clks[MPC512x_CLK_BDLC], 0, CAN); + NODE_CHK("mclk", clks[MPC512x_CLK_MSCAN0_MCLK + idx], 0, CAN); + } + + /* + * do register the 'ips', 'sys', and 'ref' names globally + * instead of inside each individual CAN node, as there is no + * potential for a name conflict (in contrast to 'ipg' and 'mclk') + */ + if (did_register & DID_REG_CAN) { + clk_register_clkdev(clks[MPC512x_CLK_IPS], "ips", NULL); + clk_register_clkdev(clks[MPC512x_CLK_SYS], "sys", NULL); + clk_register_clkdev(clks[MPC512x_CLK_REF], "ref", NULL); + } + + FOR_NODES("fsl,mpc5121-i2c") { + NODE_PREP; + NODE_CHK("ipg", clks[MPC512x_CLK_I2C], 0, I2C); + } + + /* + * workaround for the fact that the I2C driver does an "anonymous" + * lookup (NULL name spec, which yields the first clock spec) for + * which we cannot register an alias -- a _global_ 'ipg' alias that + * is not bound to any device name and returns the I2C clock item + * is not a good idea + * + * so we have the lookup in the peripheral driver fail, which is + * silent and non-fatal, and pre-enable the clock item here such + * that register access is possible + * + * see commit b3bfce2b "i2c: mpc: cleanup clock API use" for + * details, adjusting s/NULL/"ipg"/ in i2c-mpc.c would make this + * workaround obsolete + */ + if (did_register & DID_REG_I2C) + clk_prepare_enable(clks[MPC512x_CLK_I2C]); + + FOR_NODES("fsl,mpc5121-diu") { + NODE_PREP; + NODE_CHK("ipg", clks[MPC512x_CLK_DIU], 1, DIU); + } + + FOR_NODES("fsl,mpc5121-viu") { + NODE_PREP; + NODE_CHK("ipg", clks[MPC512x_CLK_VIU], 0, VIU); + } + + /* + * note that 2771399a "fs_enet: cleanup clock API use" did use the + * "per" string for the clock lookup in contrast to the "ipg" name + * which most other nodes are using -- this is not a fatal thing + * but just something to keep in mind when doing compatibility + * registration, it's a non-issue with up-to-date device tree data + */ + FOR_NODES("fsl,mpc5121-fec") { + NODE_PREP; + NODE_CHK("per", clks[MPC512x_CLK_FEC], 0, FEC); + } + FOR_NODES("fsl,mpc5121-fec-mdio") { + NODE_PREP; + NODE_CHK("per", clks[MPC512x_CLK_FEC], 0, FEC); + } + + FOR_NODES("fsl,mpc5121-usb2-dr") { + NODE_PREP; + idx = (res.start & 0x4000) ? 1 : 0; + NODE_CHK("ipg", clks[MPC512x_CLK_USB1 + idx], 0, USB); + } + + FOR_NODES("fsl,mpc5121-pata") { + NODE_PREP; + NODE_CHK("ipg", clks[MPC512x_CLK_PATA], 0, PATA); + } + + /* + * try to collapse diagnostics into a single line of output yet + * provide a full list of what is missing, to avoid noise in the + * absence of up-to-date device tree data -- backwards + * compatibility to old DTBs is a requirement, updates may be + * desirable or preferrable but are not at all mandatory + */ + if (did_register) { + pr_notice("device tree lacks clock specs, adding fallbacks (0x%x,%s%s%s%s%s%s%s%s%s%s)\n", + did_register, + (did_register & DID_REG_PSC) ? " PSC" : "", + (did_register & DID_REG_PSCFIFO) ? " PSCFIFO" : "", + (did_register & DID_REG_NFC) ? " NFC" : "", + (did_register & DID_REG_CAN) ? " CAN" : "", + (did_register & DID_REG_I2C) ? " I2C" : "", + (did_register & DID_REG_DIU) ? " DIU" : "", + (did_register & DID_REG_VIU) ? " VIU" : "", + (did_register & DID_REG_FEC) ? " FEC" : "", + (did_register & DID_REG_USB) ? " USB" : "", + (did_register & DID_REG_PATA) ? " PATA" : ""); + } else { + pr_debug("device tree has clock specs, no fallbacks added\n"); + } } int __init mpc5121_clk_init(void) -- cgit v1.2.3 From 124fe7c561ecba872f67b686e976e5c557fb52d5 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 30 Nov 2013 23:51:26 +0100 Subject: dts: mpc512x: add clock specs for client lookups this addresses the client side of device tree based clock lookups add clock specifiers to the mbx, nfc, mscan, sdhc, i2c, axe, diu, viu, mdio, fec, usb, pata, psc, psc fifo, and pci nodes in the shared mpc5121.dtsi include Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Stephen Warren Cc: Ian Campbell Cc: devicetree@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Reviewed-by: Mike Turquette Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/mpc5121.dtsi | 95 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/arch/powerpc/boot/dts/mpc5121.dtsi b/arch/powerpc/boot/dts/mpc5121.dtsi index 1d533e083e3c..2c0e1552d20b 100644 --- a/arch/powerpc/boot/dts/mpc5121.dtsi +++ b/arch/powerpc/boot/dts/mpc5121.dtsi @@ -51,6 +51,10 @@ compatible = "fsl,mpc5121-mbx"; reg = <0x20000000 0x4000>; interrupts = <66 0x8>; + clocks = <&clks MPC512x_CLK_MBX_BUS>, + <&clks MPC512x_CLK_MBX_3D>, + <&clks MPC512x_CLK_MBX>; + clock-names = "mbx-bus", "mbx-3d", "mbx"; }; sram@30000000 { @@ -64,6 +68,8 @@ interrupts = <6 8>; #address-cells = <1>; #size-cells = <1>; + clocks = <&clks MPC512x_CLK_NFC>; + clock-names = "ipg"; }; localbus@80000020 { @@ -155,12 +161,24 @@ compatible = "fsl,mpc5121-mscan"; reg = <0x1300 0x80>; interrupts = <12 0x8>; + clocks = <&clks MPC512x_CLK_BDLC>, + <&clks MPC512x_CLK_IPS>, + <&clks MPC512x_CLK_SYS>, + <&clks MPC512x_CLK_REF>, + <&clks MPC512x_CLK_MSCAN0_MCLK>; + clock-names = "ipg", "ips", "sys", "ref", "mclk"; }; can@1380 { compatible = "fsl,mpc5121-mscan"; reg = <0x1380 0x80>; interrupts = <13 0x8>; + clocks = <&clks MPC512x_CLK_BDLC>, + <&clks MPC512x_CLK_IPS>, + <&clks MPC512x_CLK_SYS>, + <&clks MPC512x_CLK_REF>, + <&clks MPC512x_CLK_MSCAN1_MCLK>; + clock-names = "ipg", "ips", "sys", "ref", "mclk"; }; sdhc@1500 { @@ -169,6 +187,9 @@ interrupts = <8 0x8>; dmas = <&dma0 30>; dma-names = "rx-tx"; + clocks = <&clks MPC512x_CLK_IPS>, + <&clks MPC512x_CLK_SDHC>; + clock-names = "ipg", "per"; }; i2c@1700 { @@ -177,6 +198,8 @@ compatible = "fsl,mpc5121-i2c", "fsl-i2c"; reg = <0x1700 0x20>; interrupts = <9 0x8>; + clocks = <&clks MPC512x_CLK_I2C>; + clock-names = "ipg"; }; i2c@1720 { @@ -185,6 +208,8 @@ compatible = "fsl,mpc5121-i2c", "fsl-i2c"; reg = <0x1720 0x20>; interrupts = <10 0x8>; + clocks = <&clks MPC512x_CLK_I2C>; + clock-names = "ipg"; }; i2c@1740 { @@ -193,6 +218,8 @@ compatible = "fsl,mpc5121-i2c", "fsl-i2c"; reg = <0x1740 0x20>; interrupts = <11 0x8>; + clocks = <&clks MPC512x_CLK_I2C>; + clock-names = "ipg"; }; i2ccontrol@1760 { @@ -204,30 +231,48 @@ compatible = "fsl,mpc5121-axe"; reg = <0x2000 0x100>; interrupts = <42 0x8>; + clocks = <&clks MPC512x_CLK_AXE>; + clock-names = "ipg"; }; display@2100 { compatible = "fsl,mpc5121-diu"; reg = <0x2100 0x100>; interrupts = <64 0x8>; + clocks = <&clks MPC512x_CLK_DIU>; + clock-names = "ipg"; }; can@2300 { compatible = "fsl,mpc5121-mscan"; reg = <0x2300 0x80>; interrupts = <90 0x8>; + clocks = <&clks MPC512x_CLK_BDLC>, + <&clks MPC512x_CLK_IPS>, + <&clks MPC512x_CLK_SYS>, + <&clks MPC512x_CLK_REF>, + <&clks MPC512x_CLK_MSCAN2_MCLK>; + clock-names = "ipg", "ips", "sys", "ref", "mclk"; }; can@2380 { compatible = "fsl,mpc5121-mscan"; reg = <0x2380 0x80>; interrupts = <91 0x8>; + clocks = <&clks MPC512x_CLK_BDLC>, + <&clks MPC512x_CLK_IPS>, + <&clks MPC512x_CLK_SYS>, + <&clks MPC512x_CLK_REF>, + <&clks MPC512x_CLK_MSCAN3_MCLK>; + clock-names = "ipg", "ips", "sys", "ref", "mclk"; }; viu@2400 { compatible = "fsl,mpc5121-viu"; reg = <0x2400 0x400>; interrupts = <67 0x8>; + clocks = <&clks MPC512x_CLK_VIU>; + clock-names = "ipg"; }; mdio@2800 { @@ -235,6 +280,8 @@ reg = <0x2800 0x800>; #address-cells = <1>; #size-cells = <0>; + clocks = <&clks MPC512x_CLK_FEC>; + clock-names = "per"; }; eth0: ethernet@2800 { @@ -243,6 +290,8 @@ reg = <0x2800 0x800>; local-mac-address = [ 00 00 00 00 00 00 ]; interrupts = <4 0x8>; + clocks = <&clks MPC512x_CLK_FEC>; + clock-names = "per"; }; /* USB1 using external ULPI PHY */ @@ -254,6 +303,8 @@ interrupts = <43 0x8>; dr_mode = "otg"; phy_type = "ulpi"; + clocks = <&clks MPC512x_CLK_USB1>; + clock-names = "ipg"; }; /* USB0 using internal UTMI PHY */ @@ -265,6 +316,8 @@ interrupts = <44 0x8>; dr_mode = "otg"; phy_type = "utmi_wide"; + clocks = <&clks MPC512x_CLK_USB2>; + clock-names = "ipg"; }; /* IO control */ @@ -283,6 +336,8 @@ compatible = "fsl,mpc5121-pata"; reg = <0x10200 0x100>; interrupts = <5 0x8>; + clocks = <&clks MPC512x_CLK_PATA>; + clock-names = "ipg"; }; /* 512x PSCs are not 52xx PSC compatible */ @@ -294,6 +349,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC0>, + <&clks MPC512x_CLK_PSC0_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC1 */ @@ -303,6 +361,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC1>, + <&clks MPC512x_CLK_PSC1_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC2 */ @@ -312,6 +373,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC2>, + <&clks MPC512x_CLK_PSC2_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC3 */ @@ -321,6 +385,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC3>, + <&clks MPC512x_CLK_PSC3_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC4 */ @@ -330,6 +397,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC4>, + <&clks MPC512x_CLK_PSC4_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC5 */ @@ -339,6 +409,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC5>, + <&clks MPC512x_CLK_PSC5_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC6 */ @@ -348,6 +421,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC6>, + <&clks MPC512x_CLK_PSC6_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC7 */ @@ -357,6 +433,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC7>, + <&clks MPC512x_CLK_PSC7_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC8 */ @@ -366,6 +445,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC8>, + <&clks MPC512x_CLK_PSC8_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC9 */ @@ -375,6 +457,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC9>, + <&clks MPC512x_CLK_PSC9_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC10 */ @@ -384,6 +469,9 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC10>, + <&clks MPC512x_CLK_PSC10_MCLK>; + clock-names = "ipg", "mclk"; }; /* PSC11 */ @@ -393,12 +481,17 @@ interrupts = <40 0x8>; fsl,rx-fifo-size = <16>; fsl,tx-fifo-size = <16>; + clocks = <&clks MPC512x_CLK_PSC11>, + <&clks MPC512x_CLK_PSC11_MCLK>; + clock-names = "ipg", "mclk"; }; pscfifo@11f00 { compatible = "fsl,mpc5121-psc-fifo"; reg = <0x11f00 0x100>; interrupts = <40 0x8>; + clocks = <&clks MPC512x_CLK_PSC_FIFO>; + clock-names = "ipg"; }; dma0: dma@14000 { @@ -416,6 +509,8 @@ #address-cells = <3>; #size-cells = <2>; #interrupt-cells = <1>; + clocks = <&clks MPC512x_CLK_PCI>; + clock-names = "ipg"; reg = <0x80008500 0x100 /* internal registers */ 0x80008300 0x8>; /* config space access registers */ -- cgit v1.2.3 From 7d71d5b2e80623242b0c0823ce6cb635d089c8b2 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Sat, 30 Nov 2013 23:51:27 +0100 Subject: clk: mpc5xxx: switch to COMMON_CLK, retire PPC_CLOCK the setup before the change was - arch/powerpc/Kconfig had the PPC_CLOCK option, off by default - depending on the PPC_CLOCK option the arch/powerpc/kernel/clock.c file was built, which implements the clk.h API but always returns -ENOSYS unless a platform registers specific callbacks - the MPC52xx platform selected PPC_CLOCK but did not register any callbacks, thus all clk.h API calls keep resulting in -ENOSYS errors (which is OK, all peripheral drivers deal with the situation) - the MPC512x platform selected PPC_CLOCK and registered specific callbacks implemented in arch/powerpc/platforms/512x/clock.c, thus provided real support for the clock API - no other powerpc platform did select PPC_CLOCK the situation after the change is - the MPC512x platform implements the COMMON_CLK interface, and thus the PPC_CLOCK approach in arch/powerpc/platforms/512x/clock.c has become obsolete - the MPC52xx platform still lacks genuine support for the clk.h API while this is not a change against the previous situation (the error code returned from COMMON_CLK stubs differs but every call still results in an error) - with all references gone, the arch/powerpc/kernel/clock.c wrapper and the PPC_CLOCK option have become obsolete, as did the clk_interface.h header file the switch from PPC_CLOCK to COMMON_CLK is done for all platforms within the same commit such that multiplatform kernels (the combination of 512x and 52xx within one executable) keep working Cc: Mike Turquette Cc: Anatolij Gustschin Cc: linux-arm-kernel@lists.infradead.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- arch/powerpc/Kconfig | 5 - arch/powerpc/include/asm/clk_interface.h | 20 - arch/powerpc/kernel/Makefile | 1 - arch/powerpc/kernel/clock.c | 82 ---- arch/powerpc/platforms/512x/Kconfig | 2 +- arch/powerpc/platforms/512x/Makefile | 1 - arch/powerpc/platforms/512x/clock.c | 754 ------------------------------- arch/powerpc/platforms/52xx/Kconfig | 2 +- 8 files changed, 2 insertions(+), 865 deletions(-) delete mode 100644 arch/powerpc/include/asm/clk_interface.h delete mode 100644 arch/powerpc/kernel/clock.c delete mode 100644 arch/powerpc/platforms/512x/clock.c diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index f0a893142cee..b131b26ca45a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -1040,11 +1040,6 @@ config KEYS_COMPAT source "crypto/Kconfig" -config PPC_CLOCK - bool - default n - select HAVE_CLK - config PPC_LIB_RHEAP bool diff --git a/arch/powerpc/include/asm/clk_interface.h b/arch/powerpc/include/asm/clk_interface.h deleted file mode 100644 index ab1882c1e176..000000000000 --- a/arch/powerpc/include/asm/clk_interface.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ASM_POWERPC_CLK_INTERFACE_H -#define __ASM_POWERPC_CLK_INTERFACE_H - -#include - -struct clk_interface { - struct clk* (*clk_get) (struct device *dev, const char *id); - int (*clk_enable) (struct clk *clk); - void (*clk_disable) (struct clk *clk); - unsigned long (*clk_get_rate) (struct clk *clk); - void (*clk_put) (struct clk *clk); - long (*clk_round_rate) (struct clk *clk, unsigned long rate); - int (*clk_set_rate) (struct clk *clk, unsigned long rate); - int (*clk_set_parent) (struct clk *clk, struct clk *parent); - struct clk* (*clk_get_parent) (struct clk *clk); -}; - -extern struct clk_interface clk_functions; - -#endif /* __ASM_POWERPC_CLK_INTERFACE_H */ diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 904d713366ff..fcc9a89a4695 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -48,7 +48,6 @@ obj-$(CONFIG_ALTIVEC) += vecemu.o obj-$(CONFIG_PPC_970_NAP) += idle_power4.o obj-$(CONFIG_PPC_P7_NAP) += idle_power7.o obj-$(CONFIG_PPC_OF) += of_platform.o prom_parse.o -obj-$(CONFIG_PPC_CLOCK) += clock.o procfs-y := proc_powerpc.o obj-$(CONFIG_PROC_FS) += $(procfs-y) rtaspci-$(CONFIG_PPC64)-$(CONFIG_PCI) := rtas_pci.o diff --git a/arch/powerpc/kernel/clock.c b/arch/powerpc/kernel/clock.c deleted file mode 100644 index a764b47791e8..000000000000 --- a/arch/powerpc/kernel/clock.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Dummy clk implementations for powerpc. - * These need to be overridden in platform code. - */ - -#include -#include -#include -#include -#include - -struct clk_interface clk_functions; - -struct clk *clk_get(struct device *dev, const char *id) -{ - if (clk_functions.clk_get) - return clk_functions.clk_get(dev, id); - return ERR_PTR(-ENOSYS); -} -EXPORT_SYMBOL(clk_get); - -void clk_put(struct clk *clk) -{ - if (clk_functions.clk_put) - clk_functions.clk_put(clk); -} -EXPORT_SYMBOL(clk_put); - -int clk_enable(struct clk *clk) -{ - if (clk_functions.clk_enable) - return clk_functions.clk_enable(clk); - return -ENOSYS; -} -EXPORT_SYMBOL(clk_enable); - -void clk_disable(struct clk *clk) -{ - if (clk_functions.clk_disable) - clk_functions.clk_disable(clk); -} -EXPORT_SYMBOL(clk_disable); - -unsigned long clk_get_rate(struct clk *clk) -{ - if (clk_functions.clk_get_rate) - return clk_functions.clk_get_rate(clk); - return 0; -} -EXPORT_SYMBOL(clk_get_rate); - -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - if (clk_functions.clk_round_rate) - return clk_functions.clk_round_rate(clk, rate); - return -ENOSYS; -} -EXPORT_SYMBOL(clk_round_rate); - -int clk_set_rate(struct clk *clk, unsigned long rate) -{ - if (clk_functions.clk_set_rate) - return clk_functions.clk_set_rate(clk, rate); - return -ENOSYS; -} -EXPORT_SYMBOL(clk_set_rate); - -struct clk *clk_get_parent(struct clk *clk) -{ - if (clk_functions.clk_get_parent) - return clk_functions.clk_get_parent(clk); - return ERR_PTR(-ENOSYS); -} -EXPORT_SYMBOL(clk_get_parent); - -int clk_set_parent(struct clk *clk, struct clk *parent) -{ - if (clk_functions.clk_set_parent) - return clk_functions.clk_set_parent(clk, parent); - return -ENOSYS; -} -EXPORT_SYMBOL(clk_set_parent); diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig index fc9c1cbfcb1d..5aa3f4b5332c 100644 --- a/arch/powerpc/platforms/512x/Kconfig +++ b/arch/powerpc/platforms/512x/Kconfig @@ -1,9 +1,9 @@ config PPC_MPC512x bool "512x-based boards" depends on 6xx + select COMMON_CLK select FSL_SOC select IPIC - select PPC_CLOCK select PPC_PCI_CHOICE select FSL_PCI if PCI select ARCH_WANT_OPTIONAL_GPIOLIB diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile index 1e05f9def8a4..01693121a2b1 100644 --- a/arch/powerpc/platforms/512x/Makefile +++ b/arch/powerpc/platforms/512x/Makefile @@ -1,7 +1,6 @@ # # Makefile for the Freescale PowerPC 512x linux kernel. # -obj-$(CONFIG_PPC_CLOCK) += clock.o obj-$(CONFIG_COMMON_CLK) += clock-commonclk.o obj-y += mpc512x_shared.o obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o mpc5121_ads_cpld.o diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c deleted file mode 100644 index fd8a37653417..000000000000 --- a/arch/powerpc/platforms/512x/clock.c +++ /dev/null @@ -1,754 +0,0 @@ -/* - * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: John Rigby - * - * Implements the clk api defined in include/linux/clk.h - * - * Original based on linux/arch/arm/mach-integrator/clock.c - * - * Copyright (C) 2004 ARM Limited. - * Written by Deep Blue Solutions Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "mpc512x.h" - -#undef CLK_DEBUG - -static int clocks_initialized; - -#define CLK_HAS_RATE 0x1 /* has rate in MHz */ -#define CLK_HAS_CTRL 0x2 /* has control reg and bit */ - -struct clk { - struct list_head node; - char name[32]; - int flags; - struct device *dev; - unsigned long rate; - struct module *owner; - void (*calc) (struct clk *); - struct clk *parent; - int reg, bit; /* CLK_HAS_CTRL */ - int div_shift; /* only used by generic_div_clk_calc */ -}; - -static LIST_HEAD(clocks); -static DEFINE_MUTEX(clocks_mutex); - -static struct clk *mpc5121_clk_get(struct device *dev, const char *id) -{ - struct clk *p, *clk = ERR_PTR(-ENOENT); - int dev_match; - int id_match; - - if (dev == NULL || id == NULL) - return clk; - - mutex_lock(&clocks_mutex); - list_for_each_entry(p, &clocks, node) { - dev_match = id_match = 0; - - if (dev == p->dev) - dev_match++; - if (strcmp(id, p->name) == 0) - id_match++; - if ((dev_match || id_match) && try_module_get(p->owner)) { - clk = p; - break; - } - } - mutex_unlock(&clocks_mutex); - - return clk; -} - -#ifdef CLK_DEBUG -static void dump_clocks(void) -{ - struct clk *p; - - mutex_lock(&clocks_mutex); - printk(KERN_INFO "CLOCKS:\n"); - list_for_each_entry(p, &clocks, node) { - pr_info(" %s=%ld", p->name, p->rate); - if (p->parent) - pr_cont(" %s=%ld", p->parent->name, - p->parent->rate); - if (p->flags & CLK_HAS_CTRL) - pr_cont(" reg/bit=%d/%d", p->reg, p->bit); - pr_cont("\n"); - } - mutex_unlock(&clocks_mutex); -} -#define DEBUG_CLK_DUMP() dump_clocks() -#else -#define DEBUG_CLK_DUMP() -#endif - - -static void mpc5121_clk_put(struct clk *clk) -{ - module_put(clk->owner); -} - -#define NRPSC 12 - -struct mpc512x_clockctl { - u32 spmr; /* System PLL Mode Reg */ - u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ - u32 scfr1; /* System Clk Freq Reg 1 */ - u32 scfr2; /* System Clk Freq Reg 2 */ - u32 reserved; - u32 bcr; /* Bread Crumb Reg */ - u32 pccr[NRPSC]; /* PSC Clk Ctrl Reg 0-11 */ - u32 spccr; /* SPDIF Clk Ctrl Reg */ - u32 cccr; /* CFM Clk Ctrl Reg */ - u32 dccr; /* DIU Clk Cnfg Reg */ -}; - -static struct mpc512x_clockctl __iomem *clockctl; - -static int mpc5121_clk_enable(struct clk *clk) -{ - unsigned int mask; - - if (clk->flags & CLK_HAS_CTRL) { - mask = in_be32(&clockctl->sccr[clk->reg]); - mask |= 1 << clk->bit; - out_be32(&clockctl->sccr[clk->reg], mask); - } - return 0; -} - -static void mpc5121_clk_disable(struct clk *clk) -{ - unsigned int mask; - - if (clk->flags & CLK_HAS_CTRL) { - mask = in_be32(&clockctl->sccr[clk->reg]); - mask &= ~(1 << clk->bit); - out_be32(&clockctl->sccr[clk->reg], mask); - } -} - -static unsigned long mpc5121_clk_get_rate(struct clk *clk) -{ - if (clk->flags & CLK_HAS_RATE) - return clk->rate; - else - return 0; -} - -static long mpc5121_clk_round_rate(struct clk *clk, unsigned long rate) -{ - return rate; -} - -static int mpc5121_clk_set_rate(struct clk *clk, unsigned long rate) -{ - return 0; -} - -static int clk_register(struct clk *clk) -{ - mutex_lock(&clocks_mutex); - list_add(&clk->node, &clocks); - mutex_unlock(&clocks_mutex); - return 0; -} - -static unsigned long spmf_mult(void) -{ - /* - * Convert