From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:35
reword the clock control module's registers declaration such that the
MCLK related registers form an array and get indexed by PSC number
this change is in preparation to COMMON_CLK support for the MPC512x
platform, the changed declaration remains neutral to existing code since
the PSC and MSCAN CCR fields declared here aren't referenced anywhere
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/include/asm/mpc5121.h | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:36
the common clock drivers were motivated/initiated by ARM development
and apparently assume little endian peripherals
wrap register/peripherals access in the common code (div, gate, mux)
in preparation of adding COMMON_CLK support for other platforms
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/clk/clk-divider.c | 6 +++---
drivers/clk/clk-gate.c | 6 +++---
drivers/clk/clk-mux.c | 6 +++---
include/linux/clk-provider.h | 17 +++++++++++++++++
4 files changed, 26 insertions(+), 9 deletions(-)
@@ -89,7 +89,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw)u32reg;structclk_gate*gate=to_clk_gate(hw);-reg=readl(gate->reg);+reg=clk_readl(gate->reg);/* if a set bit disables this clk, flip it before masking */if(gate->flags&CLK_GATE_SET_TO_DISABLE)
@@ -42,7 +42,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)*OTOH,pmd_trace_clk_mux_ckusesaseparatebitforeachclock,so*val=0x4reallymeans"bit 2, index starts at bit 0"*/-val=readl(mux->reg)>>mux->shift;+val=clk_readl(mux->reg)>>mux->shift;val&=mux->mask;if(mux->table){
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:37
prepare C preprocessor support when processing MPC512x DTS files
- switch from DTS syntax to CPP syntax for include specs
- create a symlink such that DTS processing can reference includes
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/boot/dts/ac14xx.dts | 2 +-
arch/powerpc/boot/dts/include/dt-bindings | 1 +
arch/powerpc/boot/dts/mpc5121ads.dts | 2 +-
arch/powerpc/boot/dts/pdm360ng.dts | 2 +-
4 files changed, 4 insertions(+), 3 deletions(-)
create mode 120000 arch/powerpc/boot/dts/include/dt-bindings
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:39
this addresses the clock driver aka provider's side of clocks
- prepare for future '<&clks ID>' phandle references for device tree
based clock lookup in client drivers
- introduce a 'clocks' subtree with an 'osc' node for the crystal
or oscillator SoC input (fixed frequency)
- provide default values with 33MHz oscillator frequency in the
common include (the 66MHz IPS bus already was there), add
override values for the ifm AC14xx board which deviates from
the reference design (25MHz xtal, 80MHz IPS bus)
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/boot/dts/ac14xx.dts | 7 +++++++
arch/powerpc/boot/dts/mpc5121.dtsi | 15 ++++++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:40
this change introduces a clock infrastructure implementation 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 (which can
get referenced by means of symbolic identifiers from 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 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 resides in
parallel to the common clock implementation for test and comparison
during migration, a compile time option picks one of the two
alternatives (Kconfig switch, common clock used by default)
since not all drivers for peripherals were adjusted yet to properly
allocate and release their clock items, this platform clock driver
implementation pre-enables some of the clock items to not break
peripheral drivers during migration -- these clock pre-enable
workarounds will get removed as peripheral drivers get adjusted
to provide clock names which the serial communication drivers are using
and where the PSC index number is encoded into, clkdev registration is
done to not break these peripheral drivers -- this workaround will get
removed as these drivers get adjusted after device tree based clock
lookup has become available
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/Kconfig | 14 +-
arch/powerpc/platforms/512x/Makefile | 4 +-
arch/powerpc/platforms/512x/clock-commonclk.c | 778 +++++++++++++++++++++++++
include/linux/clk-provider.h | 16 +
4 files changed, 810 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/512x/clock-commonclk.c
@@ -1,7 +1,9 @@## Makefile for the Freescale PowerPC 512x linux kernel.#-obj-y+=clock.ompc512x_shared.o+obj-$(CONFIG_PPC_CLOCK)+=clock.o+obj-$(CONFIG_COMMON_CLK)+=clock-commonclk.o+obj-y+=mpc512x_shared.oobj-$(CONFIG_MPC5121_ADS)+=mpc5121_ads.ompc5121_ads_cpld.oobj-$(CONFIG_MPC512x_GENERIC)+=mpc512x_generic.oobj-$(CONFIG_PDM360NG)+=pdm360ng.o
@@ -0,0 +1,778 @@+/*+*Copyright(C)2013DENXSoftwareEngineering+*+*GerhardSittig,<gsi@denx.de>+*+*commonclockdriversupportfortheMPC512xplatform+*+*Thisisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<linux/clk-provider.h>+#include<linux/clkdev.h>+#include<linux/device.h>+#include<linux/errno.h>+#include<linux/io.h>+#include<linux/of.h>++#include<asm/mpc5121.h>+#include<dt-bindings/clock/mpc512x-clock.h>++#include"mpc512x.h" /* our public mpc5121_clk_init() API */++/* helpers to keep the MCLK intermediates "somewhere" in our table */+enum{+MCLK_IDX_MUX0,+MCLK_IDX_EN0,+MCLK_IDX_DIV0,+MCLK_IDX_MUX1,+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 */+staticstructclk*clks[MPC512x_CLK_LAST_PRIVATE];+staticstructclk_onecell_dataclk_data;++/* CCM register access */+staticstructmpc512x_ccm__iomem*clkregs;+staticDEFINE_SPINLOCK(clklock);++/* convenience wrappers around the common clk API */+staticinlinestructclk*mpc512x_clk_fixed(constchar*name,intrate)+{+returnclk_register_fixed_rate(NULL,name,NULL,CLK_IS_ROOT,rate);+}++staticinlinestructclk*mpc512x_clk_factor(+constchar*name,constchar*parent_name,+intmul,intdiv)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+returnclk_register_fixed_factor(NULL,name,parent_name,clkflags,+mul,div);+}++staticinlinestructclk*mpc512x_clk_divider(+constchar*name,constchar*parent_name,u8clkflags,+u32__iomem*reg,u8pos,u8len,intdivflags)+{+returnclk_register_divider(NULL,name,parent_name,clkflags,+reg,pos,len,divflags,&clklock);+}++staticinlinestructclk*mpc512x_clk_divtable(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos,u8len,+conststructclk_div_table*divtab)+{+u8divflags;++divflags=0;+returnclk_register_divider_table(NULL,name,parent_name,0,+reg,pos,len,divflags,+divtab,&clklock);+}++staticinlinestructclk*mpc512x_clk_gated(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+returnclk_register_gate(NULL,name,parent_name,clkflags,+reg,pos,0,&clklock);+}++staticinlinestructclk*mpc512x_clk_muxed(constchar*name,+constchar**parent_names,intparent_count,+u32__iomem*reg,u8pos,u8len)+{+intclkflags;+u8muxflags;++clkflags=CLK_SET_RATE_PARENT;+muxflags=0;+returnclk_register_mux(NULL,name,+parent_names,parent_count,clkflags,+reg,pos,len,muxflags,&clklock);+}++/* helper to isolate a bit field from a register */+staticinlineintget_bit_field(uint32_t__iomem*reg,uint8_tpos,uint8_tlen)+{+uint32_tval;++val=in_be32(reg);+val>>=pos;+val&=(1<<len)-1;+returnval;+}++/* get the SPMF and translate it into the "sys pll" multiplier */+staticintget_spmf_mult(void)+{+staticintspmf_to_mult[]={+68,1,12,16,20,24,28,32,+36,40,44,48,52,56,60,64,+};+intspmf;++spmf=get_bit_field(&clkregs->spmr,24,4);+returnspmf_to_mult[spmf];+}++/*+*gettheSYS_DIVvalueandtranslateitintoadividefactor+*+*valuesreturnedfromhereareamultipleoftherealfactorsincethe+*divideratioisfractional+*/+staticintget_sys_div_x2(void)+{+staticintsysdiv_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,+};+intdivcode;++divcode=get_bit_field(&clkregs->scfr2,26,6);+returnsysdiv_code_to_x2[divcode];+}++/*+*gettheCPMFvalueandtranslateitintoamultiplierfactor+*+*valuesreturnedfromhereareamultipleoftherealfactorsincethe+*multiplierratioisfractional+*/+staticintget_cpmf_mult_x2(void)+{+staticintcpmf_to_mult[]={+72,2,2,3,4,5,6,7,+};+intcpmf;++cpmf=get_bit_field(&clkregs->spmr,16,4);+returncpmf_to_mult[cpmf];+}++/*+*someoftheclockdividersdoscaleinalinearway,yetnotallof+*theirbitcombinationsarelegal;useadividertabletogeta+*resultingsetofapplicabledividervalues+*/++/* applies to the IPS_DIV, and PCI_DIV values */+staticstructclk_div_tabledivtab_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 */+staticstructclk_div_tabledivtab_1234[]={+{.val=1,.div=1,},+{.val=2,.div=2,},+{.val=3,.div=3,},+{.val=4,.div=4,},+{.div=0,},+};++staticintget_freq_from_dt(char*propname)+{+structdevice_node*np;+constunsignedint*prop;+intval;++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);+}+returnval;+}++staticvoidmpc512x_clk_preset_data(void)+{+size_ti;++for(i=0;i<ARRAY_SIZE(clks);i++)+clks[i]=ERR_PTR(-ENODEV);+}++/*+*-receivesthe"bus frequency"fromthecaller(that'stheIPSclock+*rate,thehistoricalsourceofclockinformation)+*-fetchesthesystemPLLmultiplieranddividervaluesaswellasthe+*IPSdividervaluefromhardware+*-determinestheREFclockrateeitherfromtheXTAL/OSCspec(if+*thereisadevicetreenodedescribingtheoscillator)orfromthe+*IPSbusclock(supportedforbackwardscompatibility,suchthat+*setupswithoutXTAL/OSCspecskeepworking)+*-createsthe"ref"clockitemintheclocktree,suchthat+*subsequentcodecancreatetheremainderofthehierarchy(REF->+*SYS->CSB->IPS)fromtheREFclockrateandthereturnedmul/div+*values+*/+staticvoidmpc512x_clk_setup_ref_clock(intbus_freq,+int*sys_mul,int*sys_div,int*ips_div)+{+structclk*osc_clk;+intcalc_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 node */+osc_clk=clk_get(NULL,"osc");+if(osc_clk){+/* descend REF directly from OSC, verify the IPS rate */+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{+/* calculate OSC rate and create REF from the freq value */+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);+}+}++/*+*helpercodefortheMCLKsubtreesetup+*+*theoverviewinsection5.2.4oftheMPC5121eReferenceManualrev4+*suggeststhatallinstancesofthe"PSC clock generation"areequal,+*andthatonemightre-usethePSCsetupforMSCANclockgeneration+*(section5.2.5)aswell,atleastthelogicifnotthedatafor+*description+*+*thedetails(startingatpage5-20)showdifferencesinthespecific+*inputsofthefirstmuxstage("can clk in","spdif tx"),andthe+*factualnon-availabilityofthesecondmuxstage(it'spresentyet+*onlyoneinputisvalid)+*+*theMSCANclockrelatedregisters(startingatpage5-35)all+*reference"spdif clk"atthefirstmuxstageanddon'tmentionany+*"can clk"atall,whichsomehowisunexpected+*+*TODOre-checkthedocument,andclarifywhethertheRMiscorrectin+*theovervieworinthedetails,andwhetherthedifferenceisa+*clipboardinducederrororresultsfromchiprevisions+*+*itturnsoutthattheRMrev4asof2012-06talksabout"can"forthe+*PSCswhileRMrev3asof2008-10talksabout"spdif",soIguessthat+*firstadocupdateisrequiredwhichbetterreflectsrealityinthe+*SoCbeforetheimplementationshouldfollowwhilenoquestionsremain+*/++/*+*notethatthisdeclarationraisesacheckpatchwarning,but+*it'stheverydatatypewhich<linux/clk-provider.h>expects,+*makingthisdeclarationpasscheckpatchwillbreakcompilation+*/+staticconstchar*parent_names_mux0[]={+"sys","ref","psc-mclk-in","spdif-tx",+};++enummclk_type{+MCLK_TYPE_PSC,+MCLK_TYPE_MSCAN,+MCLK_TYPE_SPDIF,+};++structmclk_setup_data{+enummclk_typetype;+intcomp_idx;+boolhas_mclk1;+intbit_sccr1,bit_sccr2;+constchar*name_mux0;+constchar*name_en0;+constchar*name_div0;+constchar*parent_names_mux1[2];+constchar*name_mux1;+constchar*name_mclk;+};++#define MCLK_SETUP_DATA_PSC(id) { \+MCLK_TYPE_PSC,id,\+0,27-id,-1,\+"psc"#id"-mux0",\+"psc"#id"-en0",\+"psc"#id"_mclk_div",\+{"psc"#id"_mclk_div","dummy",},\+"psc"#id"_mclk_out",\+"psc"#id"_mclk",\+}++#define MCLK_SETUP_DATA_MSCAN(id) { \+MCLK_TYPE_MSCAN,id,\+0,-1,25,\+"mscan"#id"-mux0",\+"mscan"#id"-en0",\+"mscan"#id"_mclk_div",\+{"mscan"#id"_mclk_div","dummy",},\+"mscan"#id"_mclk_out",\+"mscan"#id"_mclk",\+}++#define MCLK_SETUP_DATA_SPDIF { \+MCLK_TYPE_SPDIF,0,\+1,-1,23,\+"spdif-mux0",\+"spdif-en0",\+"spdif_mclk_div",\+{"spdif_mclk_div","spdif-rx",},\+"spdif_mclk_out",\+"spdif_mclk",\+}++staticstructmclk_setup_datamclk_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),+};++staticstructmclk_setup_datamclk_mscan_data[]={+MCLK_SETUP_DATA_MSCAN(0),+MCLK_SETUP_DATA_MSCAN(1),+MCLK_SETUP_DATA_MSCAN(2),+MCLK_SETUP_DATA_MSCAN(3),+};++staticstructmclk_setup_datamclk_spdif_data[]={+MCLK_SETUP_DATA_SPDIF,+};++/* setup the MCLK clock subtree of an individual PSC/MSCAN/SPDIF */+staticvoidmpc512x_clk_setup_mclk(structmclk_setup_data*entry)+{+size_tclks_idx_pub,clks_idx_int;+u32__iomem*mccr_reg;/* MCLK control register (mux, en, div) */+u32__iomem*sccr_reg;/* system clock control register (enable) */+intsccr_bit;+intdiv;++/* derive a few parameters from the component type and index */+switch(entry->type){+caseMCLK_TYPE_PSC:+clks_idx_pub=MPC512x_CLK_PSC0_MCLK+entry->comp_idx;+clks_idx_int=MPC512x_CLK_MCLKS_FIRST++(entry->comp_idx)*MCLK_MAX_IDX;+mccr_reg=&clkregs->psc_ccr[entry->comp_idx];+break;+caseMCLK_TYPE_MSCAN:+clks_idx_pub=MPC512x_CLK_MSCAN0_MCLK+entry->comp_idx;+clks_idx_int=MPC512x_CLK_MCLKS_FIRST++(NR_PSCS+entry->comp_idx)*MCLK_MAX_IDX;+mccr_reg=&clkregs->mscan_ccr[entry->comp_idx];+break;+caseMCLK_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;+}+if(entry->bit_sccr1>=0){+sccr_reg=&clkregs->sccr1;+sccr_bit=entry->bit_sccr1;+}elseif(entry->bit_sccr2>=0){+sccr_reg=&clkregs->sccr2;+sccr_bit=entry->bit_sccr2;+}else{+sccr_reg=NULL;+}++/*+*thiswasgrabbedfromthePPC_CLOCKimplementation,which+*enforcedaspecificMCLKdividerwhiletheclockwasgated+*duringsetup(that'sadocumentedhardwarerequirement)+*+*thePPC_CLOCKimplementationmightevenhaveviolatedthe+*"MCLK <= IPS"constraint,thefixeddividervalueof1+*resultsinadividerof2andthusMCLK=SYS/2whichequals+*CSBwhichisgreaterthanIPS;theserialportsetupmayhave+*adjustedthedividerwhichtheclocksetupmighthaveleftin+*anundesirablestate+*+*initialsetupis:+*-MCLK0fromSYS+*-MCLKDIVsuchtonotexceedtheIPSclock+*-MCLK0enabled+*-MCLK1fromMCLKDIV+*/+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));++/*+*createthe'structclk'itemsoftheMCLK'sclocksubtree+*+*notethatbydesignwealwayscreateallnodesandwon'ttake+*shortcutshere,because+*-the"internal"MCLK_DIVandMCLK_OUTsignalinturnare+*selectableinputstotheCFMwhilethosewho"actually use"+*thePSC/MSCAN/SPDIF(serialdriversetal)needtheMCLK+*fortheirbitrate+*-intheabsenceof"aliases"forclocksweneedtocreate+*individial'structclk'itemsforwhatevermightget+*referencedorlookedup,evenifseveralofthoseitemsare+*identicalfromthelogicalPOV(theirratevalue)+*-foreasierfuturemaintenanceandforbetterreflectionof+*theSoC'sdocumentation,itappearsappropriatetogenerate+*clockitemsevenforthosemuxerswhichactuallyareNOPs+*(thosewithtwoinputsofwhichoneisreserved)+*/+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_int+MCLK_IDX_MUX1]=mpc512x_clk_muxed(+entry->name_mux1,+&entry->parent_names_mux1[0],+ARRAY_SIZE(entry->parent_names_mux1),+mccr_reg,7,1);+}else{+clks[clks_idx_int+MCLK_IDX_MUX1]=mpc512x_clk_factor(+entry->name_mux1,entry->parent_names_mux1[0],+1,1);+}+if(sccr_reg){+clks[clks_idx_pub]=mpc512x_clk_gated(+entry->name_mclk,+entry->name_mux1,sccr_reg,sccr_bit);+}else{+clks[clks_idx_pub]=mpc512x_clk_factor(+entry->name_mclk,+entry->name_mux1,1,1);+}++/*+*withoutthis"clock device"registration,"simple"lookupsin+*theSPImasterinitializationandserialportsetupwillfail+*+*thosedriversneedtogetadjustedtolookuptheirrequired+*clocksfromdevicetreespecs,anddevicetreenodesneedto+*providetheclockspecs,beforethisclkdevregistration+*becomesobsolete+*/+clk_register_clkdev(clks[clks_idx_pub],entry->name_mclk,NULL);+}++staticvoidmpc512x_clk_setup_mclks(structmclk_setup_data*table,size_tcount)+{+while(count-->0)+mpc512x_clk_setup_mclk(table++);+}++staticvoidmpc512x_clk_setup_clock_tree(intbusfreq)+{+intsys_mul,sys_div,ips_div;+intmul,div;+intfreq;++/*+*TODO+*-considerwhethertohandleclockswhichhavebothgatesand+*dividersviaintermediatesorbymeansofcomposites+*-fractionaldividersappeartonotmapwelltocomposites+*sincetheycanbeseenasafixedmultiplierandan+*adjustabledivider,whilecompositescanonlycombineat+*mostoneofamux,div,andgateeachintoone'structclk'+*item+*-PSC/MSCAN/SPDIFclockgenerationOTOHalreadyisvery+*specificandcannotgetmappedtocomponsites(atleastnot+*asingleone,maybetwoofthem,butseethecommentabout+*"intermediates are referenced from elsewhere, too")+*-trimthelistofauto-enabledclocksafterdriversacquire+*themcorrectlyasneeded+*/++/* regardless of whether XTAL/OSC exists, have REF created */+mpc512x_clk_setup_ref_clock(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"wassetupfromdatawhichwas+*sampledfromtheresetconfigword,atthispointintimethe+*configurationcanbeconsideredfixedandreadonly(i.e.no+*longeradjustable,ornolongerinneedofadjustment),which+*iswhywedon'tregisteraPLLherebutassumefixedfactors+*/+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);+mpc512x_clk_setup_mclks(mclk_psc_data,ARRAY_SIZE(mclk_psc_data));+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);+mpc512x_clk_setup_mclks(mclk_mscan_data,ARRAY_SIZE(mclk_mscan_data));+clks[MPC512x_CLK_SDHC]=mpc512x_clk_gated("sdhc","sdhc-ug",+&clkregs->sccr2,24);+mpc512x_clk_setup_mclks(mclk_spdif_data,ARRAY_SIZE(mclk_spdif_data));+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);++/*+*externallyprovidedclocks(whenimplementedinhardware,+*devicetreemayspecifyvalueswhichotherwisewereunknown)+*/+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);++pr_debug("clock tree setup complete\n");+freq=clk_get_rate(clks[MPC512x_CLK_E300]);+pr_debug("derived PPC freq [%d]\n",freq);+freq=clk_get_rate(clks[MPC512x_CLK_IPS]);+pr_debug("derived IPS freq [%d]\n",freq);+freq=clk_get_rate(clks[MPC512x_CLK_LPC]);+pr_debug("derived LPC freq [%d]\n",freq);++/* enable some of the clocks here unconditionally because ... */+pr_debug("automatically enabling some clocks\n");+/* some are essential yet never get claimed by any driver */+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 */+/* some are required yet no dependencies were declared */+clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);+/* some are not yet acquired by their respective drivers */+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]);+/*+*somehavetheirindividualclocksubtreewithseparateclock+*itemsandtheirindividualenablecounters,yetsharea+*commongate(refertothesameregisterlocation)whilethe+*commonclockdrivercodeisnotawareofthefactandthe+*platform'scodedoesn'tprovidespecificsupporteither+*+*whatmighthappenisthate.g.enablingtwoMSCANclockitems+*anddisablingoneofthemwilldisablethecommongateand+*thusbreaktheotherMSCANclockaswell+*/+clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN1_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN2_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN3_MCLK]);+}++/*+*registersthesetofpublicclocks(thoselistedinthedt-bindings/+*headerfile)forOFlookups,keepstheintermediatesprivatetous+*/+staticvoidmpc5121_clk_register_of_provider(structdevice_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);+}++int__initmpc5121_clk_init(void)+{+structdevice_node*clk_np;+intbusfreq;++/* 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();++/*+*havethedevicetreescannedfor"fixed-clock"nodes(which+*includestheoscillatornodeiftheboard'sDTprovidesone)+*/+of_clk_init(NULL);++/*+*addadummyclockforthosesituationswhereaclockspecis+*requiredyetnorealclockisinvolved+*/+clks[MPC512x_CLK_DUMMY]=mpc512x_clk_fixed("dummy",0);++/*+*havealltherealnodesintheclocktreepopulatedfromREF+*downtoallleaves,eitherstartingfromtheOSCnodeorfrom+*aREFrootthatwascreatedfromtheIPSbusclockinput+*/+busfreq=get_freq_from_dt("bus-frequency");+mpc512x_clk_setup_clock_tree(busfreq);++/* register as an OF clock provider */+mpc5121_clk_register_of_provider(clk_np);++return0;+}
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:41
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
these specs map 'clock-names' encoded in drivers to their respective
'struct clk' items in the platform's clock driver; some drivers
still need to learn how to lookup OF clocks, other drivers will
transparently probe successfully on MPC512x as well (sdhc)
few 'clock-names' (nfc, viu) use strings that are encoded in their
respective drivers to keep up compatibility, an alternative approach
would be to register clkdev items in the platform's clock driver for
the migration period
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/boot/dts/mpc5121.dtsi | 79 ++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:42
after device tree based clock lookup became available, the peripheral
driver need no longer construct clock names which include the PSC index,
remove the "psc%d_mclk" template and unconditionally use "mclk"
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/spi/spi-mpc512x-psc.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:43
after device tree based clock lookup became available, the peripheral
driver need no longer construct clock names which include the PSC index,
remove the "psc%d_mclk" template and unconditionally use "mclk"
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/tty/serial/mpc52xx_uart.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
@@ -623,14 +623,11 @@ static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM];/* called from within the .request_port() callback (allocation) */staticintmpc512x_psc_alloc_clock(structuart_port*port){-intpsc_num;-charclk_name[16];structclk*clk;interr;+intpsc_num;-psc_num=(port->mapbase&0xf00)>>8;-snprintf(clk_name,sizeof(clk_name),"psc%d_mclk",psc_num);-clk=clk_get(port->dev,clk_name);+clk=clk_get(port->dev,"mclk");if(IS_ERR(clk)){dev_err(port->dev,"Failed to get MCLK!\n");returnPTR_ERR(clk);
@@ -641,6 +638,7 @@ static int mpc512x_psc_alloc_clock(struct uart_port *port)clk_put(clk);returnerr;}+psc_num=(port->mapbase&0xf00)>>8;psc_mclk_clk[psc_num]=clk;return0;}
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:44
after the peripheral drivers for UART and SPI mode (both using the PSC
controller) got converted to device tree based clock lookups, the
platform clock driver need no longer provide the "psc%d_mclk" name which
depends on the PSC index number -- remove the clk_register_clkdev() call
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 12 ------------
1 file changed, 12 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-18 17:00:45
prepare and enable the FIFO clock upon PSC FIFO initialization,
disable and unprepare the FIFO clock upon PSC FIFO uninitialization,
remove the pre-enable workaround from the platform's clock driver
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 2 --
drivers/tty/serial/mpc52xx_uart.c | 39 +++++++++++++++++++++----
2 files changed, 33 insertions(+), 8 deletions(-)
@@ -689,8 +689,6 @@ static void mpc512x_clk_setup_clock_tree(int busfreq)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 */-/* some are required yet no dependencies were declared */-clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);/* some are not yet acquired by their respective drivers */clk_prepare_enable(clks[MPC512x_CLK_PSC3_MCLK]);/* serial console */clk_prepare_enable(clks[MPC512x_CLK_FEC]);/* network, NFS */
From: Gerhard Sittig <hidden> Date: 2013-07-18 20:20:49
add a comment about the magic of deriving an MSCAN component index
from the peripheral's physical address / register offset
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
@@ -148,7 +148,10 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,gotoexit_put;}-/* Determine the MSCAN device index from the physical address */+/* Determine the MSCAN device index from the peripheral's+*physicaladdress.Registeraddressoffsetsagainstthe+*IMMRbaseare:0x1300,0x1380,0x2300,0x2380+*/pval=of_get_property(ofdev->dev.of_node,"reg",&plen);BUG_ON(!pval||plen<sizeof(*pval));clockidx=(*pval&0x80)?1:0;
From: Gerhard Sittig <hidden> Date: 2013-07-18 20:20:52
make the MPC I2C driver prepare and enable the peripheral clock
('per' for register access) in the MPC512x setup routine,
make this clock setup non-fatal to allow for a migration period,
remove the pre-enabling hack in the platform's clock driver
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 1 -
drivers/i2c/busses/i2c-mpc.c | 9 +++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
@@ -692,7 +692,6 @@ static void mpc512x_clk_setup_clock_tree(int busfreq)/* some are not yet acquired by their respective drivers */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_I2C]);/**somehavetheirindividualclocksubtreewithseparateclock*itemsandtheirindividualenablecounters,yetsharea
From: Gerhard Sittig <hidden> Date: 2013-07-18 20:20:53
device tree based clock lookup in the MPC512x initialization (lookup
'per' for register access), add error check in the clock setup, must
prepare clocks before they can get enabled, unprepare after disable
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/usb/host/fsl-mph-dr-of.c | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
@@ -260,25 +260,19 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev){structfsl_usb2_platform_data*pdata=pdev->dev.platform_data;structclk*clk;-charclk_name[10];-intbase,clk_num;--base=pdev->resource->start&0xf000;-if(base==0x3000)-clk_num=1;-elseif(base==0x4000)-clk_num=2;-else-return-ENODEV;+interr;-snprintf(clk_name,sizeof(clk_name),"usb%d_clk",clk_num);-clk=clk_get(&pdev->dev,clk_name);+clk=clk_get(&pdev->dev,"per");if(IS_ERR(clk)){dev_err(&pdev->dev,"failed to get clk\n");returnPTR_ERR(clk);}--clk_enable(clk);+err=clk_prepare_enable(clk);+if(err){+dev_err(&pdev->dev,"failed to enable clk\n");+clk_put(clk);+returnerr;+}pdata->clk=clk;if(pdata->phy_mode==FSL_USB2_PHY_UTMI_WIDE){
From: Gerhard Sittig <hidden> Date: 2013-07-18 20:20:54
device tree based clock lookup, must prepare clocks before enabling
them, unprepare after disable, error check in the clock setup, remove
the pre-enable workaround in the MPC512x platform's clock driver
this change implements non-fatal clock lookup since not all platforms
provide device tree specs for clocks, but failure to enable a specified
clock is considered fatal
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 1 -
.../net/ethernet/freescale/fs_enet/fs_enet-main.c | 27 +++++++++++++++++++-
include/linux/fs_enet_pd.h | 3 +++
3 files changed, 29 insertions(+), 2 deletions(-)
@@ -691,7 +691,6 @@ static void mpc512x_clk_setup_clock_tree(int busfreq)clk_prepare_enable(clks[MPC512x_CLK_LPC]);/* boot media *//* some are not yet acquired by their respective drivers */clk_prepare_enable(clks[MPC512x_CLK_PSC3_MCLK]);/* serial console */-clk_prepare_enable(clks[MPC512x_CLK_FEC]);/* network, NFS *//**somehavetheirindividualclocksubtreewithseparateclock*itemsandtheirindividualenablecounters,yetsharea
@@ -1020,6 +1020,21 @@ static int fs_enet_probe(struct platform_device *ofdev)fpi->cp_command=*data;}+/* make clock lookup non-fatal (the driver is shared among platforms),+*butrequireenabletosucceedwhenaclockwasspecified/found+*/+fpi->clk_per=clk_get(&ofdev->dev,"per");+if(IS_ERR(fpi->clk_per))+fpi->clk_per=NULL;+if(fpi->clk_per){+interr;+err=clk_prepare_enable(fpi->clk_per);+if(err){+ret=err;+gotoout_clk_put;+}+}+fpi->rx_ring=32;fpi->tx_ring=32;fpi->rx_copybreak=240;
@@ -1028,7 +1043,7 @@ static int fs_enet_probe(struct platform_device *ofdev)fpi->phy_node=of_parse_phandle(ofdev->dev.of_node,"phy-handle",0);if((!fpi->phy_node)&&(!of_get_property(ofdev->dev.of_node,"fixed-link",NULL)))-gotoout_free_fpi;+gotoout_clk_dis;if(of_device_is_compatible(ofdev->dev.of_node,"fsl,mpc5125-fec")){phy_connection_type=of_get_property(ofdev->dev.of_node,
@@ -142,6 +143,8 @@ struct fs_platform_info {intuse_rmii;/* use RMII mode */inthas_phy;/* if the network is phy container as well...*/++structclk*clk_per;/* 'per' clock for register access */};structfs_mii_fec_platform_info{u32irq[32];
From: Gerhard Sittig <hidden> Date: 2013-07-18 20:20:55
device tree based clock lookup, must prepare clocks before enabling
them, unprepare after disable, error check in the clock setup
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/media/platform/fsl-viu.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
@@ -1582,8 +1582,11 @@ static int viu_of_probe(struct platform_device *op)dev_err(&op->dev,"failed to find the clock module!\n");ret=-ENODEV;gotoerr_clk;-}else{-clk_enable(viu_dev->clk);+}+ret=clk_prepare_enable(viu_dev->clk);+if(ret){+dev_err(&op->dev,"failed to enable the clock!\n");+gotoerr_clk_put;}/* reset VIU module */
@@ -1602,7 +1605,8 @@ static int viu_of_probe(struct platform_device *op)returnret;err_irq:-clk_disable(viu_dev->clk);+clk_disable_unprepare(viu_dev->clk);+err_clk_put:clk_put(viu_dev->clk);err_clk:video_unregister_device(viu_dev->vdev);
@@ -1626,7 +1630,7 @@ static int viu_of_remove(struct platform_device *op)free_irq(dev->irq,(void*)dev);irq_dispose_mapping(dev->irq);-clk_disable(dev->clk);+clk_disable_unprepare(dev->clk);clk_put(dev->clk);video_unregister_device(dev->vdev);
From: Gerhard Sittig <hidden> Date: 2013-07-18 20:20:58
transition to the COMMON_CLK framework has completed for the MPC512x
platform, remove the now obsolete code path of the mpc5xxx mscan
driver which accessed clock control module registers directly
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 136 -----------------------------------
1 file changed, 136 deletions(-)
From: Russell King - ARM Linux <hidden> Date: 2013-07-18 20:33:24
On Thu, Jul 18, 2013 at 10:20:52PM +0200, Gerhard Sittig wrote:
+ /* enable clock for the I2C peripheral (non fatal) */
+ clk = of_clk_get_by_name(node, "per");
+ if (!IS_ERR(clk)) {
+ clk_prepare_enable(clk);
+ clk_put(clk);
+ }
+
This kind of hacked up approach to the clk API is exactly the thing I
really don't like seeing. I don't know what it is... is the clk API
somehow difficult to use or what's the problem with doing stuff correctly?
1. Get the clock in your probe function.
2. Prepare it at the appropriate time.
3. Enable it appropriately. (or if you want to combine 2 and 3, use
clk_prepare_enable().)
4. Ensure that enables/disables and prepares/unprepares are appropriately
balanced.
5. 'put' the clock in your remove function.
Certainly do not get-enable-put a clock. You're supposed to hold on to
the clock all the time that you're actually using it.
Final point - if you want to make it non-fatal, don't play games like:
clk = clk_get(whatever);
if (IS_ERR(clk))
clk = NULL;
...
if (clk)
clk_prepare(clk);
Do this instead:
clk = clk_get(whatever);
...
if (!IS_ERR(clk))
clk_prepare(clk);
etc.
(And on this subject, I'm considering whether to make a change to the
clk API where clk_prepare() and clk_enable() return zero when passed
an error pointer - this means drivers with optional clocks don't have
to burden themselves with these kinds of checks.)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:27
this series
- fixes several drivers that are used in the MPC512x platform (UART,
SPI, ethernet, PCI, USB, CAN, NAND flash, video capture) in how they
handle clocks (appropriately acquire and setup them, hold references
during use, release clocks after use)
- introduces support for the common clock framework (CCF, COMMON_CLK
Kconfig option) in the PowerPC based MPC512x platform, which brings
device tree based clock lookup as well
although the series does touch several subsystems -- tty (serial), spi,
net (can, fs_enet), mtd (nfc), usb, i2c, media (viu), and dts -- all of
the patches are strictly clock related
it appears most appropriate to take this series through either the clk
or the powerpc trees after it has passed review and other subsystem
maintainers ACKed the clock setup related driver modifications
the series passes 'checkpatch.pl --strict' except for one warning which
cannot get resolved, since that either breaks compilation (the data type
is preset by the clk-provider.h API) or requires a cast which shadows
real mismatches:
WARNING: static const char * array should probably be static const char * const
#436: FILE: arch/powerpc/platforms/512x/clock-commonclk.c:335:
+static const char *parent_names_mux0[] = {
total: 0 errors, 1 warnings, 0 checks, 845 lines checked
each step in the series was build and run tested (with a display that is
attached to the DIU as well as SPI, with an SPI attached NOR flash, with
multiple UART ports such that one is not the boot console, with EEPROMs
attached to I2C, with an SD card, booting from network)
changes in v3:
- rebase the series against v3.11-rc2
- re-ordered the series to first address all general clock handling
concerns in existing drivers, before introducing common clock support
in the platform's clock driver
- slightly rework the SPI (01/31), UART (02/31), and PSC FIFO (23/31)
clock handling in comparison to v2 which introduced those fixes
(devm_{get,put}_clk() calls, fewer goto labels in error paths)
- fix and improve clock handling (balance allocation and release of
clocks, check for errors during setup) in all of the other drivers
which this series has touched before in naive ways: USB (03/31), NAND
flash (04/31), video capture (05/31), I2C (06/31), ethernet (08/31),
PCI (09/31), CAN (11/31)
- silence a build warning in the ethernet driver (07/31)
- eliminate all PPC_CLOCK references, use 'per' clock names for NAND
flash (25/31) and VIU (26/31) as well
- unbreak CAN operation for the period between introducing common clock
support in the platform's clock driver and introducing common clock
support in the CAN peripheral driver as well as providing clock specs
in the device tree (provide clkdev aliases for SYS and REF)
- improve common clock support for CAN (devm_{get,put}_clk() calls,
check enable() errors, keep a reference to used clocks, disable and
put clocks after use)
- reworded several commit messages to better reflect the kind of change
and because fixes were applied before adding common infrastructure
support
- point to individual numbered patches of the series in the list of
changes for v2 as well
changes in v2:
- cleanup of the UART (02/24) and SPI (01/24) clock handling before the
introduction of common clock support for the platform, as incomplete
clock handling becomes fatal or more dangerous later (which in turn
changes the context of the "device tree lookup only" followup patch
later)
- reordered the sequence of patches to keep the serial communication
related parts together (UART, SPI, and PSC FIFO changes after common
clock support was introduced, which have become 11-14/24 now)
- updated commit messages for the clock API use cleanup in the serial
communication drivers, updated comments and reworded commit messages
in the core clock driver to expand on the pre-enable workaround and
clkdev registration (09/24)
- keep a reference to the PSC FIFO clock during use instead of looking
up the clock again in the uninit() routine (14/24)
- remove the clkdev.h header file inclusion directive with the removal
of the clkdev registration call (13/24)
Gerhard Sittig (31):
spi: mpc512x: cleanup clock API use
serial: mpc512x: cleanup clock API use
USB: fsl-mph-dr-of: cleanup clock API use
mtd: mpc5121_nfc: cleanup clock API use
[media] fsl-viu: cleanup clock API use
i2c: mpc: cleanup clock API use
fs_enet: silence a build warning (unused variable)
fs_enet: cleanup clock API use
powerpc/fsl-pci: improve clock API use
net: can: mscan: add a comment on reg to idx mapping
net: can: mscan: improve clock API use
powerpc: mpc512x: array decl for MCLK registers in CCM
clk: wrap I/O access for improved portability
dts: mpc512x: prepare for preprocessor support
dts: mpc512x: introduce dt-bindings/clock/ header
dts: mpc512x: add clock related device tree specs
clk: mpc512x: introduce COMMON_CLK for MPC512x
dts: mpc512x: add clock specs for client lookups
clk: mpc512x: don't pre-enable FEC and I2C clocks
spi: mpc512x: remove now obsolete clock lookup name
serial: mpc512x: remove now obsolete clock lookup name
clk: mpc512x: remove clkdev registration (uart, spi)
serial: mpc512x: setup the PSC FIFO clock as well
USB: fsl-mph-dr-of: remove now obsolete clock lookup name
mtd: mpc5121_nfc: remove now obsolete clock lookup name
[media] fsl-viu: remove now obsolete clock lookup name
net: can: mscan: add common clock support for mpc512x
powerpc/mpc512x: improve DIU related clock setup
clk: mpc512x: switch to COMMON_CLK, remove PPC_CLOCK
net: can: mscan: remove non-common_clock code for MPC512x
clk: mpc512x: remove clkdev registration (sys/ref, header)
arch/powerpc/boot/dts/ac14xx.dts | 9 +-
arch/powerpc/boot/dts/include/dt-bindings | 1 +
arch/powerpc/boot/dts/mpc5121.dtsi | 94 ++-
arch/powerpc/boot/dts/mpc5121ads.dts | 2 +-
arch/powerpc/boot/dts/pdm360ng.dts | 2 +-
arch/powerpc/include/asm/mpc5121.h | 18 +-
arch/powerpc/platforms/512x/Kconfig | 2 +-
arch/powerpc/platforms/512x/Makefile | 3 +-
arch/powerpc/platforms/512x/clock-commonclk.c | 761 ++++++++++++++++++++
arch/powerpc/platforms/512x/clock.c | 753 -------------------
arch/powerpc/platforms/512x/mpc512x_shared.c | 165 +++--
arch/powerpc/sysdev/fsl_pci.c | 23 +
drivers/clk/clk-divider.c | 6 +-
drivers/clk/clk-gate.c | 6 +-
drivers/clk/clk-mux.c | 6 +-
drivers/i2c/busses/i2c-mpc.c | 28 +
drivers/media/platform/fsl-viu.c | 26 +-
drivers/mtd/nand/mpc5121_nfc.c | 20 +-
drivers/net/can/mscan/mpc5xxx_can.c | 271 ++++---
drivers/net/can/mscan/mscan.c | 9 +
drivers/net/can/mscan/mscan.h | 2 +
.../net/ethernet/freescale/fs_enet/fs_enet-main.c | 26 +-
drivers/spi/spi-mpc512x-psc.c | 48 +-
drivers/tty/serial/mpc52xx_uart.c | 144 +++-
drivers/usb/host/fsl-mph-dr-of.c | 26 +-
include/dt-bindings/clock/mpc512x-clock.h | 59 ++
include/linux/clk-provider.h | 33 +
include/linux/fs_enet_pd.h | 3 +
28 files changed, 1493 insertions(+), 1053 deletions(-)
create mode 120000 arch/powerpc/boot/dts/include/dt-bindings
create mode 100644 arch/powerpc/platforms/512x/clock-commonclk.c
delete mode 100644 arch/powerpc/platforms/512x/clock.c
create mode 100644 include/dt-bindings/clock/mpc512x-clock.h
# uname -srm
Linux 3.11.0-rc2-00031-ge72cd55 ppc
# cat /sys/kernel/debug/clk/clk_summary
clock enable_cnt prepare_cnt rate
---------------------------------------------------------------------
ac97 0 0 24567000
spdif_rx_in 0 0 0
spdif_tx_in 0 0 0
psc_mclk_in 0 0 25000000
dummy 1 1 0
osc 1 1 25000000
ref 1 1 25000000
sys 10 10 320000000
spdif-mux0 0 0 320000000
spdif-en0 0 0 320000000
spdif_mclk_div 0 0 80000000
spdif_mclk_out 0 0 80000000
spdif_mclk 0 0 80000000
mscan3-mux0 1 1 320000000
mscan3-en0 1 1 320000000
mscan3_mclk_div 1 1 80000000
mscan3_mclk_out 1 1 80000000
mscan3_mclk 1 1 80000000
mscan2-mux0 1 1 320000000
mscan2-en0 1 1 320000000
mscan2_mclk_div 1 1 80000000
mscan2_mclk_out 1 1 80000000
mscan2_mclk 1 1 80000000
mscan1-mux0 1 1 320000000
mscan1-en0 1 1 320000000
mscan1_mclk_div 1 1 16000000
mscan1_mclk_out 1 1 16000000
mscan1_mclk 1 2 16000000
mscan0-mux0 1 1 320000000
mscan0-en0 1 1 320000000
mscan0_mclk_div 1 1 16000000
mscan0_mclk_out 1 1 16000000
mscan0_mclk 1 2 16000000
psc11-mux0 0 0 320000000
psc11-en0 0 0 320000000
psc11_mclk_div 0 0 80000000
psc11_mclk_out 0 0 80000000
psc11_mclk 0 0 80000000
psc10-mux0 0 0 320000000
psc10-en0 0 0 320000000
psc10_mclk_div 0 0 80000000
psc10_mclk_out 0 0 80000000
psc10_mclk 0 0 80000000
psc9-mux0 0 0 320000000
psc9-en0 0 0 320000000
psc9_mclk_div 0 0 80000000
psc9_mclk_out 0 0 80000000
psc9_mclk 0 0 80000000
psc8-mux0 0 0 320000000
psc8-en0 0 0 320000000
psc8_mclk_div 0 0 80000000
psc8_mclk_out 0 0 80000000
psc8_mclk 0 0 80000000
psc7-mux0 1 1 320000000
psc7-en0 1 1 320000000
psc7_mclk_div 1 1 80000000
psc7_mclk_out 1 1 80000000
psc7_mclk 1 1 80000000
psc6-mux0 0 0 320000000
psc6-en0 0 0 320000000
psc6_mclk_div 0 0 80000000
psc6_mclk_out 0 0 80000000
psc6_mclk 0 0 80000000
psc5-mux0 1 1 320000000
psc5-en0 1 1 320000000
psc5_mclk_div 1 1 80000000
psc5_mclk_out 1 1 80000000
psc5_mclk 1 1 80000000
psc4-mux0 1 1 320000000
psc4-en0 1 1 320000000
psc4_mclk_div 1 1 80000000
psc4_mclk_out 1 1 80000000
psc4_mclk 1 1 80000000
psc3-mux0 1 1 320000000
psc3-en0 1 1 320000000
psc3_mclk_div 1 1 80000000
psc3_mclk_out 1 1 80000000
psc3_mclk 2 2 80000000
psc2-mux0 0 0 320000000
psc2-en0 0 0 320000000
psc2_mclk_div 0 0 80000000
psc2_mclk_out 0 0 80000000
psc2_mclk 0 0 80000000
psc1-mux0 0 0 320000000
psc1-en0 0 0 320000000
psc1_mclk_div 0 0 80000000
psc1_mclk_out 0 0 80000000
psc1_mclk 0 0 80000000
psc0-mux0 0 0 320000000
psc0-en0 0 0 320000000
psc0_mclk_div 0 0 80000000
psc0_mclk_out 0 0 80000000
psc0_mclk 0 0 80000000
ddr-ug 1 1 160000000
ddr 1 1 160000000
csb 4 4 160000000
viu 0 0 160000000
iim 0 0 160000000
usb2 0 0 160000000
usb1 0 0 160000000
axe 0 0 160000000
pci-ug 0 0 26666666
pci 0 0 26666666
mbx-bus-ug 0 0 80000000
mbx-bus 0 0 80000000
mbx-ug 0 0 80000000
mbx 0 0 80000000
mbx-3d-ug 0 0 80000000
mbx-3d 0 0 80000000
e300 1 1 400000000
diu-x4 1 1 640000000
diu-ug 1 1 2509803
diu 2 2 2509803
sdhc-x4 1 1 640000000
sdhc-ug 1 1 53333333
sdhc-2 0 0 53333333
sdhc 1 1 53333333
ips 7 7 80000000
i2c 3 3 80000000
mem 1 1 80000000
fec 1 1 80000000
sata 0 0 80000000
psc-fifo 1 1 80000000
pata 0 0 80000000
lpc-ug 1 1 40000000
lpc 1 1 40000000
nfc-ug 0 0 40000000
nfc 0 0 40000000
--
1.7.10.4
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:28
cleanup the MPC512x SoC's SPI master's use of the clock API
- get, prepare, and enable the MCLK during probe; disable, unprepare and
put the MCLK upon remove; hold a reference to the clock over the
period of use
- fetch MCLK rate (reference) once during probe and slightly reword BCLK
(bitrate) determination to reduce redundancy as well as to not exceed
the maximum text line length
- stick with the PPC_CLOCK 'psc%d_mclk' name for clock lookup, only
switch to a fixed string later after device tree based clock lookup
will have become available
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/spi/spi-mpc512x-psc.c | 52 +++++++++++++++++++++++++++--------------
1 file changed, 34 insertions(+), 18 deletions(-)
@@ -386,19 +388,11 @@ static int mpc512x_psc_spi_port_config(struct spi_master *master,{structmpc52xx_psc__iomem*psc=mps->psc;structmpc512x_psc_fifo__iomem*fifo=mps->fifo;-structclk*spiclk;-intret=0;-charname[32];u32sicr;u32ccr;+intspeed;u16bclkdiv;-sprintf(name,"psc%d_mclk",master->bus_num);-spiclk=clk_get(&master->dev,name);-clk_enable(spiclk);-mps->mclk=clk_get_rate(spiclk);-clk_put(spiclk);-/* Reset the PSC into a known state */out_8(&psc->command,MPC52xx_PSC_RST_RX);out_8(&psc->command,MPC52xx_PSC_RST_TX);
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:29
cleanup the clock API use of the UART driver which is shared among the
MPC512x and the MPC5200 platforms
- get, prepare, and enable the MCLK during port allocation; disable,
unprepare and put the MCLK upon port release; hold a reference to the
clock over the period of use; check for and propagate enable errors
- fix a buffer overflow for clock names with two digit PSC index numbers
- stick with the PPC_CLOCK 'psc%d_mclk' name for clock lookup, only
switch to a fixed string later after device tree based clock lookup
will have become available
to achieve support for MPC512x which is neutral to MPC5200, the
modification was done as follows
- introduce "clock alloc" and "clock release" routines in addition to
the previous "clock enable/disable" routine in the psc_ops struct
- make the clock allocation a part of the port request (resource
allocation), and make clock release a part of the port release, such
that essential resources get allocated early
- just enable/disable the clock from within the .clock() callback
without any allocation or preparation as the former implementation
did, since this routine is called from within the startup and shutdown
callbacks
- all of the above remains a NOP for the MPC5200 platform (no callbacks
are provided on that platform)
- implementation note: the clock gets enabled upon allocation already
just in case the clock is not only required for bitrate generation but
for register access as well
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/tty/serial/mpc52xx_uart.c | 100 ++++++++++++++++++++++++++++++-------
1 file changed, 83 insertions(+), 17 deletions(-)
@@ -616,31 +618,75 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port)returnIRQ_NONE;}-staticintmpc512x_psc_clock(structuart_port*port,intenable)+staticstructclk*psc_mclk_clk[MPC52xx_PSC_MAXNUM];++/* called from within the .request_port() callback (allocation) */+staticintmpc512x_psc_alloc_clock(structuart_port*port){-structclk*psc_clk;intpsc_num;-charclk_name[10];+charclk_name[16];+structclk*clk;+interr;++psc_num=(port->mapbase&0xf00)>>8;+snprintf(clk_name,sizeof(clk_name),"psc%d_mclk",psc_num);+clk=devm_clk_get(port->dev,clk_name);+if(IS_ERR(clk)){+dev_err(port->dev,"Failed to get MCLK!\n");+returnPTR_ERR(clk);+}+err=clk_prepare_enable(clk);+if(err){+dev_err(port->dev,"Failed to enable MCLK!\n");+devm_clk_put(port->dev,clk);+returnerr;+}+psc_mclk_clk[psc_num]=clk;+return0;+}++/* called from within the .release_port() callback (release) */+staticvoidmpc512x_psc_relse_clock(structuart_port*port)+{+intpsc_num;+structclk*clk;++psc_num=(port->mapbase&0xf00)>>8;+clk=psc_mclk_clk[psc_num];+if(clk){+clk_disable_unprepare(clk);+devm_clk_put(port->dev,clk);+psc_mclk_clk[psc_num]=NULL;+}+}++/* implementation of the .clock() callback (enable/disable) */+staticintmpc512x_psc_endis_clock(structuart_port*port,intenable)+{+intpsc_num;+structclk*psc_clk;+intret;if(uart_console(port))return0;psc_num=(port->mapbase&0xf00)>>8;-snprintf(clk_name,sizeof(clk_name),"psc%d_mclk",psc_num);-psc_clk=clk_get(port->dev,clk_name);-if(IS_ERR(psc_clk)){+psc_clk=psc_mclk_clk[psc_num];+if(!psc_clk){dev_err(port->dev,"Failed to get PSC clock entry!\n");return-ENODEV;}-dev_dbg(port->dev,"%s %sable\n",clk_name,enable?"en":"dis");--if(enable)-clk_enable(psc_clk);-else+dev_dbg(port->dev,"mclk %sable\n",enable?"en":"dis");+if(enable){+ret=clk_enable(psc_clk);+if(ret)+dev_err(port->dev,"Failed to enable MCLK!\n");+returnret;+}else{clk_disable(psc_clk);--return0;+return0;+}}staticvoidmpc512x_psc_get_irq(structuart_port*port,structdevice_node*np)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:30
error check in the clock setup, must prepare clocks before they
can get enabled, unprepare after disable, use devm_{get,put}_clk()
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/usb/host/fsl-mph-dr-of.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
@@ -260,6 +260,7 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev){structfsl_usb2_platform_data*pdata=pdev->dev.platform_data;structclk*clk;+interr;charclk_name[10];intbase,clk_num;
@@ -272,13 +273,17 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev)return-ENODEV;snprintf(clk_name,sizeof(clk_name),"usb%d_clk",clk_num);-clk=clk_get(&pdev->dev,clk_name);+clk=devm_clk_get(&pdev->dev,clk_name);if(IS_ERR(clk)){dev_err(&pdev->dev,"failed to get clk\n");returnPTR_ERR(clk);}--clk_enable(clk);+err=clk_prepare_enable(clk);+if(err){+dev_err(&pdev->dev,"failed to enable clk\n");+devm_clk_put(&pdev->dev,clk);+returnerr;+}pdata->clk=clk;if(pdata->phy_mode==FSL_USB2_PHY_UTMI_WIDE){
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:31
prepare before enable isn't optional, do check for and propagate clock
setup errors, adjust error code paths to correctly balance get/put and
prepare/unprepare and enable/disable, use devm_{get,put}_clk()
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/mtd/nand/mpc5121_nfc.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:32
prepare clocks before enabling them, check for and propagate enable
errors, balance get/put and prepare/unprepare and enable/disable,
use devm_{get,put}_clk()
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/media/platform/fsl-viu.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
@@ -1485,6 +1485,7 @@ static int viu_of_probe(struct platform_device *op)structviu_reg__iomem*viu_regs;structi2c_adapter*ad;intret,viu_irq;+structclk*clk;ret=of_address_to_resource(op->dev.of_node,0,&r);if(ret){
@@ -1577,14 +1578,19 @@ static int viu_of_probe(struct platform_device *op)}/* enable VIU clock */-viu_dev->clk=clk_get(&op->dev,"viu_clk");-if(IS_ERR(viu_dev->clk)){-dev_err(&op->dev,"failed to find the clock module!\n");-ret=-ENODEV;+clk=devm_clk_get(&op->dev,"viu_clk");+if(IS_ERR(clk)){+dev_err(&op->dev,"failed to lookup the clock!\n");+ret=PTR_ERR(clk);+gotoerr_clk;+}+ret=clk_prepare_enable(clk);+if(ret){+dev_err(&op->dev,"failed to enable the clock!\n");+devm_clk_put(&op->dev,clk);gotoerr_clk;-}else{-clk_enable(viu_dev->clk);}+viu_dev->clk=clk;/* reset VIU module */viu_reset(viu_dev->vr);
@@ -1602,8 +1608,8 @@ static int viu_of_probe(struct platform_device *op)returnret;err_irq:-clk_disable(viu_dev->clk);-clk_put(viu_dev->clk);+clk_disable_unprepare(viu_dev->clk);+devm_clk_put(&op->dev,viu_dev->clk);err_clk:video_unregister_device(viu_dev->vdev);err_vdev:
@@ -1626,8 +1632,8 @@ static int viu_of_remove(struct platform_device *op)free_irq(dev->irq,(void*)dev);irq_dispose_mapping(dev->irq);-clk_disable(dev->clk);-clk_put(dev->clk);+clk_disable_unprepare(dev->clk);+devm_clk_put(&op->dev,dev->clk);video_unregister_device(dev->vdev);i2c_put_adapter(client->adapter);
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:33
make the MPC I2C driver get, prepare and enable the peripheral clock
('per' for access to the peripheral's registers) during probe;
disable, unprepare and put the clock upon remove(); hold a reference
to the clock over the period of use
clock lookup is non-fatal in this implementation as not all platforms
may provide clock specs in their device tree, but enable errors for
specified clocks are considered fatal
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/i2c/busses/i2c-mpc.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:35
make the Freescale ethernet driver get, prepare and enable the FEC clock
during probe(); disable, unprepare and put the clock upon remove(); hold
a reference to the clock over the period of use; use devm_{get,put}_clk()
clock lookup is non-fatal as not all platforms provide clock specs in
their device tree; failure to enable specified clocks is fatal
Signed-off-by: Gerhard Sittig <redacted>
---
.../net/ethernet/freescale/fs_enet/fs_enet-main.c | 25 ++++++++++++++++++++
include/linux/fs_enet_pd.h | 3 +++
2 files changed, 28 insertions(+)
@@ -999,6 +999,8 @@ static int fs_enet_probe(struct platform_device *ofdev)structfs_enet_private*fep;structfs_platform_info*fpi;constu32*data;+structclk*clk;+interr;constu8*mac_addr;constchar*phy_connection_type;intprivsize,len,ret=-ENODEV;
@@ -1036,6 +1038,21 @@ static int fs_enet_probe(struct platform_device *ofdev)fpi->use_rmii=1;}+/* make clock lookup non-fatal (the driver is shared among platforms),+*butrequireenabletosucceedwhenaclockwasspecified/found,+*keepareferencetotheclockuponsuccessfulacquisition+*/+clk=devm_clk_get(&ofdev->dev,"per");+if(!IS_ERR(clk)){+err=clk_prepare_enable(clk);+if(err){+devm_clk_put(&ofdev->dev,clk);+ret=err;+gotoout_free_fpi;+}+fpi->clk_per=clk;+}+privsize=sizeof(*fep)+sizeof(structsk_buff**)*(fpi->rx_ring+fpi->tx_ring);
@@ -142,6 +143,8 @@ struct fs_platform_info {intuse_rmii;/* use RMII mode */inthas_phy;/* if the network is phy container as well...*/++structclk*clk_per;/* 'per' clock for register access */};structfs_mii_fec_platform_info{u32irq[32];
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:36
make the Freescale PCI driver get, prepare and enable the PCI clock
during probe()
clock lookup is non-fatal as not all platforms may provide clock specs
in their device tree, but failure to enable specified clocks are fatal
the driver appears to not have a remove() routine, so no reference to
the clock is kept during use, and the clock isn't released (the devm
approach will put the clock, but it won't get disabled or unprepared)
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/sysdev/fsl_pci.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:37
add a comment about the magic of deriving an MSCAN component index
from the peripheral's physical address / register offset
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
@@ -148,7 +148,10 @@ static u32 mpc512x_can_get_clock(struct platform_device *ofdev,gotoexit_put;}-/* Determine the MSCAN device index from the physical address */+/* Determine the MSCAN device index from the peripheral's+*physicaladdress.Registeraddressoffsetsagainstthe+*IMMRbaseare:0x1300,0x1380,0x2300,0x2380+*/pval=of_get_property(ofdev->dev.of_node,"reg",&plen);BUG_ON(!pval||plen<sizeof(*pval));clockidx=(*pval&0x80)?1:0;
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:38
the .get_clock() callback is run from probe() and might allocate
resources, introduce a .put_clock() callback that is run from remove()
to undo any allocation activities
use devm_get_clk() upon lookup (for SYS and REF) to have the clocks put
upon driver unload
assume that resources get prepared but not necessarily enabled in the
setup phase, make the open() and close() callbacks of the CAN network
device enable and disable a previously acquired and prepared clock
store pointers to data structures upon successful allocation already
instead of deferral until complete setup, such that subroutines in the
setup sequence may access those data structures as well to track their
resource acquisition
since clock allocation remains optional, the release callback as well as
the enable/disable calls in open/close are optional as well
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 18 ++++++++++++------
drivers/net/can/mscan/mscan.c | 9 +++++++++
drivers/net/can/mscan/mscan.h | 2 ++
3 files changed, 23 insertions(+), 6 deletions(-)
@@ -573,6 +573,12 @@ static int mscan_open(struct net_device *dev)structmscan_priv*priv=netdev_priv(dev);structmscan_regs__iomem*regs=priv->reg_base;+if(priv->clk_can){+ret=clk_enable(priv->clk_can);+if(ret)+returnret;+}+/* common open */ret=open_candev(dev);if(ret)
@@ -621,6 +627,9 @@ static int mscan_close(struct net_device *dev)close_candev(dev);free_irq(dev->irq,dev);+if(priv->clk_can)+clk_disable(priv->clk_can);+return0;}
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:39
reword the clock control module's registers declaration such that the
MCLK related registers form an array and get indexed by PSC number
this change is in preparation to COMMON_CLK support for the MPC512x
platform, the changed declaration remains neutral to existing code since
the PSC and MSCAN CCR fields declared here aren't referenced anywhere
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/include/asm/mpc5121.h | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:40
the common clock drivers were motivated/initiated by ARM development
and apparently assume little endian peripherals
wrap register/peripherals access in the common code (div, gate, mux)
in preparation of adding COMMON_CLK support for other platforms
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/clk/clk-divider.c | 6 +++---
drivers/clk/clk-gate.c | 6 +++---
drivers/clk/clk-mux.c | 6 +++---
include/linux/clk-provider.h | 17 +++++++++++++++++
4 files changed, 26 insertions(+), 9 deletions(-)
@@ -89,7 +89,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw)u32reg;structclk_gate*gate=to_clk_gate(hw);-reg=readl(gate->reg);+reg=clk_readl(gate->reg);/* if a set bit disables this clk, flip it before masking */if(gate->flags&CLK_GATE_SET_TO_DISABLE)
@@ -42,7 +42,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)*OTOH,pmd_trace_clk_mux_ckusesaseparatebitforeachclock,so*val=0x4reallymeans"bit 2, index starts at bit 0"*/-val=readl(mux->reg)>>mux->shift;+val=clk_readl(mux->reg)>>mux->shift;val&=mux->mask;if(mux->table){
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:41
prepare C preprocessor support when processing MPC512x DTS files
- switch from DTS syntax to CPP syntax for include specs
- create a symlink such that DTS processing can reference includes
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/boot/dts/ac14xx.dts | 2 +-
arch/powerpc/boot/dts/include/dt-bindings | 1 +
arch/powerpc/boot/dts/mpc5121ads.dts | 2 +-
arch/powerpc/boot/dts/pdm360ng.dts | 2 +-
4 files changed, 4 insertions(+), 3 deletions(-)
create mode 120000 arch/powerpc/boot/dts/include/dt-bindings
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:42
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
Signed-off-by: Gerhard Sittig <redacted>
---
include/dt-bindings/clock/mpc512x-clock.h | 59 +++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 include/dt-bindings/clock/mpc512x-clock.h
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:43
this addresses the clock driver aka provider's side of clocks
- prepare for future '<&clks ID>' phandle references for device tree
based clock lookup in client drivers
- introduce a 'clocks' subtree with an 'osc' node for the crystal
or oscillator SoC input (fixed frequency)
- provide default values with 33MHz oscillator frequency in the
common include (the 66MHz IPS bus already was there), add
override values for the ifm AC14xx board which deviates from
the reference design (25MHz xtal, 80MHz IPS bus)
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/boot/dts/ac14xx.dts | 7 +++++++
arch/powerpc/boot/dts/mpc5121.dtsi | 15 ++++++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:44
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 (which can
get referenced by means of symbolic identifiers from 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 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 in
parallel to the common clock implementation for test and comparison
during migration, a compile time option picks one of the two
alternatives (Kconfig switch, common clock used by default)
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)
- some help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
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
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/Kconfig | 14 +-
arch/powerpc/platforms/512x/Makefile | 4 +-
arch/powerpc/platforms/512x/clock-commonclk.c | 786 +++++++++++++++++++++++++
include/linux/clk-provider.h | 16 +
4 files changed, 818 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/512x/clock-commonclk.c
@@ -1,7 +1,9 @@## Makefile for the Freescale PowerPC 512x linux kernel.#-obj-y+=clock.ompc512x_shared.o+obj-$(CONFIG_PPC_CLOCK)+=clock.o+obj-$(CONFIG_COMMON_CLK)+=clock-commonclk.o+obj-y+=mpc512x_shared.oobj-$(CONFIG_MPC5121_ADS)+=mpc5121_ads.ompc5121_ads_cpld.oobj-$(CONFIG_MPC512x_GENERIC)+=mpc512x_generic.oobj-$(CONFIG_PDM360NG)+=pdm360ng.o
@@ -0,0 +1,786 @@+/*+*Copyright(C)2013DENXSoftwareEngineering+*+*GerhardSittig,<gsi@denx.de>+*+*commonclockdriversupportfortheMPC512xplatform+*+*Thisisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<linux/clk-provider.h>+#include<linux/clkdev.h>+#include<linux/device.h>+#include<linux/errno.h>+#include<linux/io.h>+#include<linux/of.h>++#include<asm/mpc5121.h>+#include<dt-bindings/clock/mpc512x-clock.h>++#include"mpc512x.h" /* our public mpc5121_clk_init() API */++/* helpers to keep the MCLK intermediates "somewhere" in our table */+enum{+MCLK_IDX_MUX0,+MCLK_IDX_EN0,+MCLK_IDX_DIV0,+MCLK_IDX_MUX1,+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 */+staticstructclk*clks[MPC512x_CLK_LAST_PRIVATE];+staticstructclk_onecell_dataclk_data;++/* CCM register access */+staticstructmpc512x_ccm__iomem*clkregs;+staticDEFINE_SPINLOCK(clklock);++/* convenience wrappers around the common clk API */+staticinlinestructclk*mpc512x_clk_fixed(constchar*name,intrate)+{+returnclk_register_fixed_rate(NULL,name,NULL,CLK_IS_ROOT,rate);+}++staticinlinestructclk*mpc512x_clk_factor(+constchar*name,constchar*parent_name,+intmul,intdiv)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+returnclk_register_fixed_factor(NULL,name,parent_name,clkflags,+mul,div);+}++staticinlinestructclk*mpc512x_clk_divider(+constchar*name,constchar*parent_name,u8clkflags,+u32__iomem*reg,u8pos,u8len,intdivflags)+{+returnclk_register_divider(NULL,name,parent_name,clkflags,+reg,pos,len,divflags,&clklock);+}++staticinlinestructclk*mpc512x_clk_divtable(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos,u8len,+conststructclk_div_table*divtab)+{+u8divflags;++divflags=0;+returnclk_register_divider_table(NULL,name,parent_name,0,+reg,pos,len,divflags,+divtab,&clklock);+}++staticinlinestructclk*mpc512x_clk_gated(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+returnclk_register_gate(NULL,name,parent_name,clkflags,+reg,pos,0,&clklock);+}++staticinlinestructclk*mpc512x_clk_muxed(constchar*name,+constchar**parent_names,intparent_count,+u32__iomem*reg,u8pos,u8len)+{+intclkflags;+u8muxflags;++clkflags=CLK_SET_RATE_PARENT;+muxflags=0;+returnclk_register_mux(NULL,name,+parent_names,parent_count,clkflags,+reg,pos,len,muxflags,&clklock);+}++/* helper to isolate a bit field from a register */+staticinlineintget_bit_field(uint32_t__iomem*reg,uint8_tpos,uint8_tlen)+{+uint32_tval;++val=in_be32(reg);+val>>=pos;+val&=(1<<len)-1;+returnval;+}++/* get the SPMF and translate it into the "sys pll" multiplier */+staticintget_spmf_mult(void)+{+staticintspmf_to_mult[]={+68,1,12,16,20,24,28,32,+36,40,44,48,52,56,60,64,+};+intspmf;++spmf=get_bit_field(&clkregs->spmr,24,4);+returnspmf_to_mult[spmf];+}++/*+*gettheSYS_DIVvalueandtranslateitintoadividefactor+*+*valuesreturnedfromhereareamultipleoftherealfactorsincethe+*divideratioisfractional+*/+staticintget_sys_div_x2(void)+{+staticintsysdiv_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,+};+intdivcode;++divcode=get_bit_field(&clkregs->scfr2,26,6);+returnsysdiv_code_to_x2[divcode];+}++/*+*gettheCPMFvalueandtranslateitintoamultiplierfactor+*+*valuesreturnedfromhereareamultipleoftherealfactorsincethe+*multiplierratioisfractional+*/+staticintget_cpmf_mult_x2(void)+{+staticintcpmf_to_mult[]={+72,2,2,3,4,5,6,7,+};+intcpmf;++cpmf=get_bit_field(&clkregs->spmr,16,4);+returncpmf_to_mult[cpmf];+}++/*+*someoftheclockdividersdoscaleinalinearway,yetnotallof+*theirbitcombinationsarelegal;useadividertabletogeta+*resultingsetofapplicabledividervalues+*/++/* applies to the IPS_DIV, and PCI_DIV values */+staticstructclk_div_tabledivtab_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 */+staticstructclk_div_tabledivtab_1234[]={+{.val=1,.div=1,},+{.val=2,.div=2,},+{.val=3,.div=3,},+{.val=4,.div=4,},+{.div=0,},+};++staticintget_freq_from_dt(char*propname)+{+structdevice_node*np;+constunsignedint*prop;+intval;++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);+}+returnval;+}++staticvoidmpc512x_clk_preset_data(void)+{+size_ti;++for(i=0;i<ARRAY_SIZE(clks);i++)+clks[i]=ERR_PTR(-ENODEV);+}++/*+*-receivesthe"bus frequency"fromthecaller(that'stheIPSclock+*rate,thehistoricalsourceofclockinformation)+*-fetchesthesystemPLLmultiplieranddividervaluesaswellasthe+*IPSdividervaluefromhardware+*-determinestheREFclockrateeitherfromtheXTAL/OSCspec(if+*thereisadevicetreenodedescribingtheoscillator)orfromthe+*IPSbusclock(supportedforbackwardscompatibility,suchthat+*setupswithoutXTAL/OSCspecskeepworking)+*-createsthe"ref"clockitemintheclocktree,suchthat+*subsequentcodecancreatetheremainderofthehierarchy(REF->+*SYS->CSB->IPS)fromtheREFclockrateandthereturnedmul/div+*values+*/+staticvoidmpc512x_clk_setup_ref_clock(intbus_freq,+int*sys_mul,int*sys_div,int*ips_div)+{+structclk*osc_clk;+intcalc_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 node */+osc_clk=clk_get(NULL,"osc");+if(osc_clk){+/* descend REF directly from OSC, verify the IPS rate */+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{+/* calculate OSC rate and create REF from the freq value */+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);+}+}++/*+*helpercodefortheMCLKsubtreesetup+*+*theoverviewinsection5.2.4oftheMPC5121eReferenceManualrev4+*suggeststhatallinstancesofthe"PSC clock generation"areequal,+*andthatonemightre-usethePSCsetupforMSCANclockgeneration+*(section5.2.5)aswell,atleastthelogicifnotthedatafor+*description+*+*thedetails(startingatpage5-20)showdifferencesinthespecific+*inputsofthefirstmuxstage("can clk in","spdif tx"),andthe+*factualnon-availabilityofthesecondmuxstage(it'spresentyet+*onlyoneinputisvalid)+*+*theMSCANclockrelatedregisters(startingatpage5-35)all+*reference"spdif clk"atthefirstmuxstageanddon'tmentionany+*"can clk"atall,whichsomehowisunexpected+*+*TODOre-checkthedocument,andclarifywhethertheRMiscorrectin+*theovervieworinthedetails,andwhetherthedifferenceisa+*clipboardinducederrororresultsfromchiprevisions+*+*itturnsoutthattheRMrev4asof2012-06talksabout"can"forthe+*PSCswhileRMrev3asof2008-10talksabout"spdif",soIguessthat+*firstadocupdateisrequiredwhichbetterreflectsrealityinthe+*SoCbeforetheimplementationshouldfollowwhilenoquestionsremain+*/++/*+*notethatthisdeclarationraisesacheckpatchwarning,but+*it'stheverydatatypewhich<linux/clk-provider.h>expects,+*makingthisdeclarationpasscheckpatchwillbreakcompilation+*/+staticconstchar*parent_names_mux0[]={+"sys","ref","psc-mclk-in","spdif-tx",+};++enummclk_type{+MCLK_TYPE_PSC,+MCLK_TYPE_MSCAN,+MCLK_TYPE_SPDIF,+};++structmclk_setup_data{+enummclk_typetype;+intcomp_idx;+boolhas_mclk1;+intbit_sccr1,bit_sccr2;+constchar*name_mux0;+constchar*name_en0;+constchar*name_div0;+constchar*parent_names_mux1[2];+constchar*name_mux1;+constchar*name_mclk;+};++#define MCLK_SETUP_DATA_PSC(id) { \+MCLK_TYPE_PSC,id,\+0,27-id,-1,\+"psc"#id"-mux0",\+"psc"#id"-en0",\+"psc"#id"_mclk_div",\+{"psc"#id"_mclk_div","dummy",},\+"psc"#id"_mclk_out",\+"psc"#id"_mclk",\+}++#define MCLK_SETUP_DATA_MSCAN(id) { \+MCLK_TYPE_MSCAN,id,\+0,-1,25,\+"mscan"#id"-mux0",\+"mscan"#id"-en0",\+"mscan"#id"_mclk_div",\+{"mscan"#id"_mclk_div","dummy",},\+"mscan"#id"_mclk_out",\+"mscan"#id"_mclk",\+}++#define MCLK_SETUP_DATA_SPDIF { \+MCLK_TYPE_SPDIF,0,\+1,-1,23,\+"spdif-mux0",\+"spdif-en0",\+"spdif_mclk_div",\+{"spdif_mclk_div","spdif-rx",},\+"spdif_mclk_out",\+"spdif_mclk",\+}++staticstructmclk_setup_datamclk_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),+};++staticstructmclk_setup_datamclk_mscan_data[]={+MCLK_SETUP_DATA_MSCAN(0),+MCLK_SETUP_DATA_MSCAN(1),+MCLK_SETUP_DATA_MSCAN(2),+MCLK_SETUP_DATA_MSCAN(3),+};++staticstructmclk_setup_datamclk_spdif_data[]={+MCLK_SETUP_DATA_SPDIF,+};++/* setup the MCLK clock subtree of an individual PSC/MSCAN/SPDIF */+staticvoidmpc512x_clk_setup_mclk(structmclk_setup_data*entry)+{+size_tclks_idx_pub,clks_idx_int;+u32__iomem*mccr_reg;/* MCLK control register (mux, en, div) */+u32__iomem*sccr_reg;/* system clock control register (enable) */+intsccr_bit;+intdiv;++/* derive a few parameters from the component type and index */+switch(entry->type){+caseMCLK_TYPE_PSC:+clks_idx_pub=MPC512x_CLK_PSC0_MCLK+entry->comp_idx;+clks_idx_int=MPC512x_CLK_MCLKS_FIRST++(entry->comp_idx)*MCLK_MAX_IDX;+mccr_reg=&clkregs->psc_ccr[entry->comp_idx];+break;+caseMCLK_TYPE_MSCAN:+clks_idx_pub=MPC512x_CLK_MSCAN0_MCLK+entry->comp_idx;+clks_idx_int=MPC512x_CLK_MCLKS_FIRST++(NR_PSCS+entry->comp_idx)*MCLK_MAX_IDX;+mccr_reg=&clkregs->mscan_ccr[entry->comp_idx];+break;+caseMCLK_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;+}+if(entry->bit_sccr1>=0){+sccr_reg=&clkregs->sccr1;+sccr_bit=entry->bit_sccr1;+}elseif(entry->bit_sccr2>=0){+sccr_reg=&clkregs->sccr2;+sccr_bit=entry->bit_sccr2;+}else{+sccr_reg=NULL;+}++/*+*thiswasgrabbedfromthePPC_CLOCKimplementation,which+*enforcedaspecificMCLKdividerwhiletheclockwasgated+*duringsetup(that'sadocumentedhardwarerequirement)+*+*thePPC_CLOCKimplementationmightevenhaveviolatedthe+*"MCLK <= IPS"constraint,thefixeddividervalueof1+*resultsinadividerof2andthusMCLK=SYS/2whichequals+*CSBwhichisgreaterthanIPS;theserialportsetupmayhave+*adjustedthedividerwhichtheclocksetupmighthaveleftin+*anundesirablestate+*+*initialsetupis:+*-MCLK0fromSYS+*-MCLKDIVsuchtonotexceedtheIPSclock+*-MCLK0enabled+*-MCLK1fromMCLKDIV+*/+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));++/*+*createthe'structclk'itemsoftheMCLK'sclocksubtree+*+*notethatbydesignwealwayscreateallnodesandwon'ttake+*shortcutshere,because+*-the"internal"MCLK_DIVandMCLK_OUTsignalinturnare+*selectableinputstotheCFMwhilethosewho"actually use"+*thePSC/MSCAN/SPDIF(serialdriversetal)needtheMCLK+*fortheirbitrate+*-intheabsenceof"aliases"forclocksweneedtocreate+*individial'structclk'itemsforwhatevermightget+*referencedorlookedup,evenifseveralofthoseitemsare+*identicalfromthelogicalPOV(theirratevalue)+*-foreasierfuturemaintenanceandforbetterreflectionof+*theSoC'sdocumentation,itappearsappropriatetogenerate+*clockitemsevenforthosemuxerswhichactuallyareNOPs+*(thosewithtwoinputsofwhichoneisreserved)+*/+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_int+MCLK_IDX_MUX1]=mpc512x_clk_muxed(+entry->name_mux1,+&entry->parent_names_mux1[0],+ARRAY_SIZE(entry->parent_names_mux1),+mccr_reg,7,1);+}else{+clks[clks_idx_int+MCLK_IDX_MUX1]=mpc512x_clk_factor(+entry->name_mux1,entry->parent_names_mux1[0],+1,1);+}+if(sccr_reg){+clks[clks_idx_pub]=mpc512x_clk_gated(+entry->name_mclk,+entry->name_mux1,sccr_reg,sccr_bit);+}else{+clks[clks_idx_pub]=mpc512x_clk_factor(+entry->name_mclk,+entry->name_mux1,1,1);+}++/*+*withoutthis"clock device"registration,"simple"lookupsin+*theSPImasterinitializationandserialportsetupwillfail+*+*thosedriversneedtogetadjustedtolookuptheirrequired+*clocksfromdevicetreespecs,anddevicetreenodesneedto+*providetheclockspecs,beforethisclkdevregistration+*becomesobsolete+*/+clk_register_clkdev(clks[clks_idx_pub],entry->name_mclk,NULL);+}++staticvoidmpc512x_clk_setup_mclks(structmclk_setup_data*table,size_tcount)+{+while(count-->0)+mpc512x_clk_setup_mclk(table++);+}++staticvoidmpc512x_clk_setup_clock_tree(intbusfreq)+{+intsys_mul,sys_div,ips_div;+intmul,div;+intfreq;++/*+*TODO+*-considerwhethertohandleclockswhichhavebothgatesand+*dividersviaintermediatesorbymeansofcomposites+*-fractionaldividersappeartonotmapwelltocomposites+*sincetheycanbeseenasafixedmultiplierandan+*adjustabledivider,whilecompositescanonlycombineat+*mostoneofamux,div,andgateeachintoone'structclk'+*item+*-PSC/MSCAN/SPDIFclockgenerationOTOHalreadyisvery+*specificandcannotgetmappedtocomponsites(atleastnot+*asingleone,maybetwoofthem,butseethecommentabout+*"intermediates are referenced from elsewhere, too")+*-trimthelistofauto-enabledclocksafterdriversacquire+*themcorrectlyasneeded+*/++/* regardless of whether XTAL/OSC exists, have REF created */+mpc512x_clk_setup_ref_clock(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"wassetupfromdatawhichwas+*sampledfromtheresetconfigword,atthispointintimethe+*configurationcanbeconsideredfixedandreadonly(i.e.no+*longeradjustable,ornolongerinneedofadjustment),which+*iswhywedon'tregisteraPLLherebutassumefixedfactors+*/+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);+mpc512x_clk_setup_mclks(mclk_psc_data,ARRAY_SIZE(mclk_psc_data));+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);+mpc512x_clk_setup_mclks(mclk_mscan_data,ARRAY_SIZE(mclk_mscan_data));+clks[MPC512x_CLK_SDHC]=mpc512x_clk_gated("sdhc","sdhc-ug",+&clkregs->sccr2,24);+mpc512x_clk_setup_mclks(mclk_spdif_data,ARRAY_SIZE(mclk_spdif_data));+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);++/*+*externallyprovidedclocks(whenimplementedinhardware,+*devicetreemayspecifyvalueswhichotherwisewereunknown)+*/+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);++/* clkdev registration for compatibility reasons */+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);++pr_debug("clock tree setup complete\n");+freq=clk_get_rate(clks[MPC512x_CLK_E300]);+pr_debug("derived PPC freq [%d]\n",freq);+freq=clk_get_rate(clks[MPC512x_CLK_IPS]);+pr_debug("derived IPS freq [%d]\n",freq);+freq=clk_get_rate(clks[MPC512x_CLK_LPC]);+pr_debug("derived LPC freq [%d]\n",freq);++/* enable some of the clocks here unconditionally because ... */+pr_debug("automatically enabling some clocks\n");+/* some are essential yet never get claimed by any driver */+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 */+/* some are required yet no dependencies were declared */+clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);+/* some are not yet acquired by their respective drivers */+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]);+/*+*somehavetheirindividualclocksubtreewithseparateclock+*itemsandtheirindividualenablecounters,yetsharea+*commongate(refertothesameregisterlocation)whilethe+*commonclockdrivercodeisnotawareofthefactandthe+*platform'scodedoesn'tprovidespecificsupporteither+*+*whatmighthappenisthate.g.enablingtwoMSCANclockitems+*anddisablingoneofthemwilldisablethecommongateand+*thusbreaktheotherMSCANclockaswell+*/+clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN1_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN2_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN3_MCLK]);+}++/*+*registersthesetofpublicclocks(thoselistedinthedt-bindings/+*headerfile)forOFlookups,keepstheintermediatesprivatetous+*/+staticvoidmpc5121_clk_register_of_provider(structdevice_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);+}++int__initmpc5121_clk_init(void)+{+structdevice_node*clk_np;+intbusfreq;++/* 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();++/*+*havethedevicetreescannedfor"fixed-clock"nodes(which+*includestheoscillatornodeiftheboard'sDTprovidesone)+*/+of_clk_init(NULL);++/*+*addadummyclockforthosesituationswhereaclockspecis+*requiredyetnorealclockisinvolved+*/+clks[MPC512x_CLK_DUMMY]=mpc512x_clk_fixed("dummy",0);++/*+*havealltherealnodesintheclocktreepopulatedfromREF+*downtoallleaves,eitherstartingfromtheOSCnodeorfrom+*aREFrootthatwascreatedfromtheIPSbusclockinput+*/+busfreq=get_freq_from_dt("bus-frequency");+mpc512x_clk_setup_clock_tree(busfreq);++/* register as an OF clock provider */+mpc5121_clk_register_of_provider(clk_np);++return0;+}
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:45
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
these specs map 'clock-names' encoded in drivers to their respective
'struct clk' items in the platform's clock driver
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/boot/dts/mpc5121.dtsi | 79 ++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:46
after the device tree nodes provide clock specs for client side lookups,
peripheral drivers can attach to their clocks and the clock driver need
no longer pre-enable those clock items
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 2 --
1 file changed, 2 deletions(-)
@@ -713,9 +713,7 @@ static void mpc512x_clk_setup_clock_tree(int busfreq)clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);/* some are not yet acquired by their respective drivers */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]);/**somehavetheirindividualclocksubtreewithseparateclock*itemsandtheirindividualenablecounters,yetsharea
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:47
after device tree based clock lookup became available, the peripheral
driver need no longer construct clock names which include the PSC index,
remove the "psc%d_mclk" template and unconditionally use "mclk"
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/spi/spi-mpc512x-psc.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:48
after device tree based clock lookup became available, the peripheral
driver need no longer construct clock names which include the PSC index,
remove the "psc%d_mclk" template and unconditionally use "mclk"
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/tty/serial/mpc52xx_uart.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
@@ -623,14 +623,11 @@ static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM];/* called from within the .request_port() callback (allocation) */staticintmpc512x_psc_alloc_clock(structuart_port*port){-intpsc_num;-charclk_name[16];structclk*clk;interr;+intpsc_num;-psc_num=(port->mapbase&0xf00)>>8;-snprintf(clk_name,sizeof(clk_name),"psc%d_mclk",psc_num);-clk=devm_clk_get(port->dev,clk_name);+clk=devm_clk_get(port->dev,"mclk");if(IS_ERR(clk)){dev_err(port->dev,"Failed to get MCLK!\n");returnPTR_ERR(clk);
@@ -641,6 +638,7 @@ static int mpc512x_psc_alloc_clock(struct uart_port *port)devm_clk_put(port->dev,clk);returnerr;}+psc_num=(port->mapbase&0xf00)>>8;psc_mclk_clk[psc_num]=clk;return0;}
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:49
after the UART and SPI peripheral drivers have switched to device tree
based clock lookup and no longer construct clock names from their PSC
component index, the "psc%d_mclk" alias names have become obsolete --
remove the corresponding clk_register_clkdev() calls
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 11 -----------
1 file changed, 11 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:50
prepare and enable the FIFO clock upon PSC FIFO initialization,
check for and propagage errors when enabling the PSC FIFO clock,
disable and unprepare the FIFO clock upon PSC FIFO uninitialization,
remove the pre-enable workaround from the platform's clock driver
devm_{get,put}_clk() doesn't apply here, as the SoC provides a
single FIFO component which is shared among several PSC components,
thus the FIFO isn't associated with a device (while the PSCs are)
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 2 --
drivers/tty/serial/mpc52xx_uart.c | 46 +++++++++++++++++++++----
2 files changed, 40 insertions(+), 8 deletions(-)
@@ -698,8 +698,6 @@ static void mpc512x_clk_setup_clock_tree(int busfreq)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 */-/* some are required yet no dependencies were declared */-clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);/* some are not yet acquired by their respective drivers */clk_prepare_enable(clks[MPC512x_CLK_PSC3_MCLK]);/* serial console */clk_prepare_enable(clks[MPC512x_CLK_DIU]);/* display */
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:51
after device tree based clock lookup became available, the peripheral
driver need no longer construct clock names which include the component
index -- remove the "usb%d_clk" template and unconditionally use "per",
remove the clock driver's clkdev registration
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 2 --
drivers/usb/host/fsl-mph-dr-of.c | 13 +------------
2 files changed, 1 insertion(+), 14 deletions(-)
@@ -261,19 +261,8 @@ int fsl_usb2_mpc5121_init(struct platform_device *pdev)structfsl_usb2_platform_data*pdata=pdev->dev.platform_data;structclk*clk;interr;-charclk_name[10];-intbase,clk_num;--base=pdev->resource->start&0xf000;-if(base==0x3000)-clk_num=1;-elseif(base==0x4000)-clk_num=2;-else-return-ENODEV;-snprintf(clk_name,sizeof(clk_name),"usb%d_clk",clk_num);-clk=devm_clk_get(&pdev->dev,clk_name);+clk=devm_clk_get(&pdev->dev,"per");if(IS_ERR(clk)){dev_err(&pdev->dev,"failed to get clk\n");returnPTR_ERR(clk);
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:52
after device tree based clock lookup became available, the NAND flash
driver need no longer use the previous "nfc_clk" name but can switch to
the fixed "per" clock name -- adjust the peripheral driver and remove
the clock driver's clkdev registration
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 1 -
drivers/mtd/nand/mpc5121_nfc.c | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:53
after device tree based clock lookup became available, the VIU driver
need no longer use the previous "viu_clk" name but can switch to the
fixed "per" clock name -- adjust the peripheral driver and remove the
clock driver's clkdev registration
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 1 -
drivers/media/platform/fsl-viu.c | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:54
implement a .get_clock() callback for the MPC512x platform which uses
the common clock infrastructure (eliminating direct access to the clock
control registers from within the CAN network driver), and provide the
corresponding .put_clock() callback to release resources after use
keep the previous implementation of MPC512x support in place during
migration, since common clock support is optional
this change is neutral to the MPC5200 platform
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 169 +++++++++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
@@ -109,6 +109,167 @@ static u32 mpc52xx_can_get_clock(struct platform_device *ofdev,#endif /* CONFIG_PPC_MPC52xx */#ifdef CONFIG_PPC_MPC512x++#if IS_ENABLED(CONFIG_COMMON_CLK)++staticu32mpc512x_can_get_clock(structplatform_device*ofdev,+constchar*clock_source,int*mscan_clksrc)+{+structdevice_node*np;+u32clockdiv;+enum{+CLK_FROM_AUTO,+CLK_FROM_IPS,+CLK_FROM_SYS,+CLK_FROM_REF,+}clk_from;+structclk*clk_in,*clk_can;+unsignedlongfreq_calc;+structmscan_priv*priv;++/* the caller passed in the clock source spec that was read from+*thedevicetree,gettheoptionalclockdivideraswell+*/+np=ofdev->dev.of_node;+clockdiv=1;+of_property_read_u32(np,"fsl,mscan-clock-divider",&clockdiv);+dev_dbg(&ofdev->dev,"device tree specs: clk src[%s] div[%d]\n",+clock_source?clock_source:"<NULL>",clockdiv);++/* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to+*getset,andthe'ips'clockistheinputtotheMSCAN+*component+*+*forclock-sourcevaluesof'ref'or'sys'theCANCTL1[CLKSRC]+*bitneedstogetcleared,anoptionalclock-dividermayhave+*beenspecified(thedefaultvalueis1),theappropriate+*MSCANrelatedMCLKistheinputtotheMSCANcomponent+*+*intheabsenceofaclock-sourcespec,firstanoptimalclock+*getsdeterminedbasedonthe'sys'clock,ifthatfailsthe+*'ref'clockisused+*/+clk_from=CLK_FROM_AUTO;+if(clock_source){+/* interpret the device tree's spec for the clock source */+if(!strcmp(clock_source,"ip"))+clk_from=CLK_FROM_IPS;+elseif(!strcmp(clock_source,"sys"))+clk_from=CLK_FROM_SYS;+elseif(!strcmp(clock_source,"ref"))+clk_from=CLK_FROM_REF;+else+gotoerr_invalid;+dev_dbg(&ofdev->dev,"got a clk source spec[%d]\n",clk_from);+}+if(clk_from==CLK_FROM_AUTO){+/* no spec so far, try the 'sys' clock; round to the+*nextMHzandseeifwecangetamultipleof16MHz+*/+dev_dbg(&ofdev->dev,"no clk source spec, trying SYS\n");+clk_in=devm_clk_get(&ofdev->dev,"sys");+if(IS_ERR(clk_in))+gotoerr_notavail;+freq_calc=clk_get_rate(clk_in);+freq_calc+=499999;+freq_calc/=1000000;+freq_calc*=1000000;+if((freq_calc%16000000)==0){+clk_from=CLK_FROM_SYS;+clockdiv=freq_calc/16000000;+dev_dbg(&ofdev->dev,+"clk fit, sys[%lu] div[%d] freq[%lu]\n",+freq_calc,clockdiv,freq_calc/clockdiv);+}+}+if(clk_from==CLK_FROM_AUTO){+/* no spec so far, use the 'ref' clock */+dev_dbg(&ofdev->dev,"no clk source spec, trying REF\n");+clk_in=devm_clk_get(&ofdev->dev,"ref");+if(IS_ERR(clk_in))+gotoerr_notavail;+clk_from=CLK_FROM_REF;+freq_calc=clk_get_rate(clk_in);+dev_dbg(&ofdev->dev,+"clk fit, ref[%lu] (no div) freq[%lu]\n",+freq_calc,freq_calc);+}++/* select IPS or MCLK as the MSCAN input (returned to the caller),+*setuptheMCLKmuxsourceandrateifapplicable,applythe+*optionallyspecifiedorderivedabovedivider,anddetermine+*theactualresultingclockratetoreturntothecaller+*/+switch(clk_from){+caseCLK_FROM_IPS:+clk_can=devm_clk_get(&ofdev->dev,"ips");+if(IS_ERR(clk_can))+gotoerr_notavail;+if(clk_prepare(clk_can)){+devm_clk_put(&ofdev->dev,clk_can);+gotoerr_notavail;+}+priv=netdev_priv(dev_get_drvdata(&ofdev->dev));+priv->clk_can=clk_can;+freq_calc=clk_get_rate(clk_can);+*mscan_clksrc=MSCAN_CLKSRC_IPS;+dev_dbg(&ofdev->dev,"clk from IPS, clksrc[%d] freq[%lu]\n",+*mscan_clksrc,freq_calc);+break;+caseCLK_FROM_SYS:+caseCLK_FROM_REF:+clk_can=devm_clk_get(&ofdev->dev,"mclk");+if(IS_ERR(clk_can))+gotoerr_notavail;+if(clk_prepare(clk_can)){+devm_clk_put(&ofdev->dev,clk_can);+gotoerr_notavail;+}+priv=netdev_priv(dev_get_drvdata(&ofdev->dev));+priv->clk_can=clk_can;+if(clk_from==CLK_FROM_SYS)+clk_in=devm_clk_get(&ofdev->dev,"sys");+if(clk_from==CLK_FROM_REF)+clk_in=devm_clk_get(&ofdev->dev,"ref");+if(IS_ERR(clk_in))+gotoerr_notavail;+clk_set_parent(clk_can,clk_in);+freq_calc=clk_get_rate(clk_in);+freq_calc/=clockdiv;+clk_set_rate(clk_can,freq_calc);+freq_calc=clk_get_rate(clk_can);+*mscan_clksrc=MSCAN_CLKSRC_BUS;+dev_dbg(&ofdev->dev,"clk from MCLK, clksrc[%d] freq[%lu]\n",+*mscan_clksrc,freq_calc);+break;+default:+gotoerr_invalid;+}++returnfreq_calc;++err_invalid:+dev_err(&ofdev->dev,"invalid clock source specification\n");+return0;++err_notavail:+dev_err(&ofdev->dev,"cannot acquire or setup clock source\n");+return0;+}++staticvoidmpc512x_can_put_clock(structplatform_device*ofdev)+{+structmscan_priv*priv;++priv=netdev_priv(dev_get_drvdata(&ofdev->dev));+if(priv->clk_can){+clk_unprepare(priv->clk_can);+devm_clk_put(&ofdev->dev,priv->clk_can);+}+}++#else /* COMMON_CLK */+structmpc512x_clockctl{u32spmr;/* System PLL Mode Reg */u32sccr[2];/* System Clk Ctrl Reg 1 & 2 */
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:55
adapt the DIU clock initialization to the COMMON_CLK approach: device
tree based clock lookup, prepare and unprepare for clocks, work with
frequencies not dividers, call the appropriate clk_*() routines and
don't access CCM registers, remove the pre-enable workaround in the
platform's clock driver
the "best clock" determination now completely relies on the platform's
clock driver to pick a frequency close to what the caller requests, and
merely checks whether the desired frequency was met (is acceptable since
it meets the tolerance of the monitor) -- this approach shall succeed
upon first try in the usual case, will test a few less desirable yet
acceptable frequencies in edge cases, and will fallback to "best effort"
if none of the previously tried frequencies pass the test
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 1 -
arch/powerpc/platforms/512x/mpc512x_shared.c | 165 +++++++++++++------------
2 files changed, 88 insertions(+), 78 deletions(-)
@@ -696,7 +696,6 @@ static void mpc512x_clk_setup_clock_tree(int busfreq)clk_prepare_enable(clks[MPC512x_CLK_LPC]);/* boot media *//* some are not yet acquired by their respective drivers */clk_prepare_enable(clks[MPC512x_CLK_PSC3_MCLK]);/* serial console */-clk_prepare_enable(clks[MPC512x_CLK_DIU]);/* display *//**somehavetheirindividualclocksubtreewithseparateclock*itemsandtheirindividualenablecounters,yetsharea
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:56
completely switch to, i.e. unconditionally use COMMON_CLK for the
MPC512x platform, and retire the PPC_CLOCK implementation for that
platform after the transition has completed
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/Kconfig | 14 +-
arch/powerpc/platforms/512x/Makefile | 3 +-
arch/powerpc/platforms/512x/clock.c | 753 ----------------------------------
3 files changed, 2 insertions(+), 768 deletions(-)
delete mode 100644 arch/powerpc/platforms/512x/clock.c
@@ -1,8 +1,7 @@## Makefile for the Freescale PowerPC 512x linux kernel.#-obj-$(CONFIG_PPC_CLOCK)+=clock.o-obj-$(CONFIG_COMMON_CLK)+=clock-commonclk.o+obj-y+=clock-commonclk.oobj-y+=mpc512x_shared.oobj-$(CONFIG_MPC5121_ADS)+=mpc5121_ads.ompc5121_ads_cpld.oobj-$(CONFIG_MPC512x_GENERIC)+=mpc512x_generic.o
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:57
transition to the common clock framework has completed and the PPC_CLOCK
is no longer available for the MPC512x platform, remove the now obsolete
code path of the mpc5xxx mscan driver which accessed clock control
module registers directly
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 141 -----------------------------------
1 file changed, 141 deletions(-)
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:14:58
remove the last clkdev registration call ("sys_clk" and "ref_clk"
for mscan), as well as the clkdev header inclusion and the "compat
registration" comment
all client lookups for clock items are device tree based now, no
compatibility alias names need to get provided any longer
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/clock-commonclk.c | 5 -----
1 file changed, 5 deletions(-)
From: Marc Kleine-Budde <mkl@pengutronix.de> Date: 2013-07-22 12:28:36
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
make the Freescale ethernet driver get, prepare and enable the FEC clock
during probe(); disable, unprepare and put the clock upon remove(); hold
a reference to the clock over the period of use; use devm_{get,put}_clk()
There's no need for devm_clk_put(), devm will take care of this.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
From: Marc Kleine-Budde <mkl@pengutronix.de> Date: 2013-07-22 12:31:21
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
the .get_clock() callback is run from probe() and might allocate
resources, introduce a .put_clock() callback that is run from remove()
to undo any allocation activities
looks good
use devm_get_clk() upon lookup (for SYS and REF) to have the clocks put
upon driver unload
fine
assume that resources get prepared but not necessarily enabled in the
setup phase, make the open() and close() callbacks of the CAN network
device enable and disable a previously acquired and prepared clock
I think you should call prepare_enable and disable_unprepare in the
open/close functions.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
From: Gerhard Sittig <hidden> Date: 2013-07-22 12:33:24
[ manually added devicetree at vger now ]
On Mon, Jul 22, 2013 at 14:14 +0200, Gerhard Sittig wrote:
this series
- fixes several drivers that are used in the MPC512x platform (UART,
SPI, ethernet, PCI, USB, CAN, NAND flash, video capture) in how they
handle clocks (appropriately acquire and setup them, hold references
during use, release clocks after use)
- introduces support for the common clock framework (CCF, COMMON_CLK
Kconfig option) in the PowerPC based MPC512x platform, which brings
device tree based clock lookup as well
Haven't noticed before that the ozlabs device tree list started
actively rejecting reception. And the scope of the series has
widened to include clock related fixes in many more drivers while
the initial submission only introduced CCF for MPC512x.
Shall I resend (with vger devicetree included and more subsystems
lists added)? Which other lists to include without spamming too
many channels? Poor moderators have to ACK messages for lists
that I'm not subscribed to.
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Marc Kleine-Budde <mkl@pengutronix.de> Date: 2013-07-22 13:04:52
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
quoted hunk
implement a .get_clock() callback for the MPC512x platform which uses
the common clock infrastructure (eliminating direct access to the clock
control registers from within the CAN network driver), and provide the
corresponding .put_clock() callback to release resources after use
keep the previous implementation of MPC512x support in place during
migration, since common clock support is optional
this change is neutral to the MPC5200 platform
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 169 +++++++++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
@@ -109,6 +109,167 @@ static u32 mpc52xx_can_get_clock(struct platform_device *ofdev,#endif /* CONFIG_PPC_MPC52xx */#ifdef CONFIG_PPC_MPC512x++#if IS_ENABLED(CONFIG_COMMON_CLK)++staticu32mpc512x_can_get_clock(structplatform_device*ofdev,+constchar*clock_source,int*mscan_clksrc)+{+structdevice_node*np;+u32clockdiv;+enum{+CLK_FROM_AUTO,+CLK_FROM_IPS,+CLK_FROM_SYS,+CLK_FROM_REF,+}clk_from;+structclk*clk_in,*clk_can;+unsignedlongfreq_calc;+structmscan_priv*priv;++/* the caller passed in the clock source spec that was read from+*thedevicetree,gettheoptionalclockdivideraswell+*/+np=ofdev->dev.of_node;+clockdiv=1;+of_property_read_u32(np,"fsl,mscan-clock-divider",&clockdiv);+dev_dbg(&ofdev->dev,"device tree specs: clk src[%s] div[%d]\n",+clock_source?clock_source:"<NULL>",clockdiv);++/* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to+*getset,andthe'ips'clockistheinputtotheMSCAN+*component+*+*forclock-sourcevaluesof'ref'or'sys'theCANCTL1[CLKSRC]+*bitneedstogetcleared,anoptionalclock-dividermayhave+*beenspecified(thedefaultvalueis1),theappropriate+*MSCANrelatedMCLKistheinputtotheMSCANcomponent+*+*intheabsenceofaclock-sourcespec,firstanoptimalclock+*getsdeterminedbasedonthe'sys'clock,ifthatfailsthe+*'ref'clockisused+*/+clk_from=CLK_FROM_AUTO;+if(clock_source){+/* interpret the device tree's spec for the clock source */+if(!strcmp(clock_source,"ip"))+clk_from=CLK_FROM_IPS;+elseif(!strcmp(clock_source,"sys"))+clk_from=CLK_FROM_SYS;+elseif(!strcmp(clock_source,"ref"))+clk_from=CLK_FROM_REF;+else+gotoerr_invalid;+dev_dbg(&ofdev->dev,"got a clk source spec[%d]\n",clk_from);+}+if(clk_from==CLK_FROM_AUTO){+/* no spec so far, try the 'sys' clock; round to the+*nextMHzandseeifwecangetamultipleof16MHz+*/+dev_dbg(&ofdev->dev,"no clk source spec, trying SYS\n");+clk_in=devm_clk_get(&ofdev->dev,"sys");+if(IS_ERR(clk_in))+gotoerr_notavail;+freq_calc=clk_get_rate(clk_in);+freq_calc+=499999;+freq_calc/=1000000;+freq_calc*=1000000;+if((freq_calc%16000000)==0){+clk_from=CLK_FROM_SYS;+clockdiv=freq_calc/16000000;+dev_dbg(&ofdev->dev,+"clk fit, sys[%lu] div[%d] freq[%lu]\n",+freq_calc,clockdiv,freq_calc/clockdiv);+}+}+if(clk_from==CLK_FROM_AUTO){+/* no spec so far, use the 'ref' clock */+dev_dbg(&ofdev->dev,"no clk source spec, trying REF\n");+clk_in=devm_clk_get(&ofdev->dev,"ref");+if(IS_ERR(clk_in))+gotoerr_notavail;+clk_from=CLK_FROM_REF;+freq_calc=clk_get_rate(clk_in);+dev_dbg(&ofdev->dev,+"clk fit, ref[%lu] (no div) freq[%lu]\n",+freq_calc,freq_calc);+}++/* select IPS or MCLK as the MSCAN input (returned to the caller),+*setuptheMCLKmuxsourceandrateifapplicable,applythe+*optionallyspecifiedorderivedabovedivider,anddetermine+*theactualresultingclockratetoreturntothecaller+*/+switch(clk_from){+caseCLK_FROM_IPS:+clk_can=devm_clk_get(&ofdev->dev,"ips");+if(IS_ERR(clk_can))+gotoerr_notavail;+if(clk_prepare(clk_can)){
I would just call prepare_enable in the main mscan driver, then we don't
need a special "clock is prepared but not enabled" contract.
+ devm_clk_put(&ofdev->dev, clk_can);
not needed, as this driver instance will fail, doesn't it?
From: Gerhard Sittig <hidden> Date: 2013-07-23 11:53:48
On Mon, Jul 22, 2013 at 14:31 +0200, Marc Kleine-Budde wrote:
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
quoted
the .get_clock() callback is run from probe() and might allocate
resources, introduce a .put_clock() callback that is run from remove()
to undo any allocation activities
looks good
quoted
use devm_get_clk() upon lookup (for SYS and REF) to have the clocks put
upon driver unload
fine
quoted
assume that resources get prepared but not necessarily enabled in the
setup phase, make the open() and close() callbacks of the CAN network
device enable and disable a previously acquired and prepared clock
I think you should call prepare_enable and disable_unprepare in the
open/close functions.
After more local research, which totally eliminated the need to
pre-enable the CAN related clocks, but might need more discussion
as it touches the common gate support, I've learned something
more:
The CAN clock needs to get enabled during probe() already, since
registers get accessed between probe() for the driver and open()
for the network device -- while access to peripheral registers
crashes the kernel when clocks still are disabled (other hardware
may just hang or provide fake data, neither of this is OK).
But I see the point in your suggestion to prepare _and_ enable
the clock during open() as well -- to have open() cope with
whatever probe() did, after all the driver is shared among
platforms, which may differ in what they do during probe().
So I will:
- make open() of the network device prepare _and_ enable the
clock for the peripheral (if acquired during probe())
- adjust open() because ATM it leaves the clock enabled when the
network device operation fails (the error path is incomplete in
v3)
- make the MPC512x specific probe() time .get_clock() routine not
just prepare but enable the clock as well
- and of course address all the shutdown counter parts of the
above setup paths
This results in:
- specific chip drivers only need to balance their private get
and put clock routines which are called from probe and remove,
common paths DTRT for all of them
- correct operation for MPC512x, where common clock is used
- still everything is neutral for MPC5200 where common clock
isn't used, behaviour is identical to before the change
- no assumptions are made about what occurs or doesn't occur
during probe(), when the network device is used then the clock
is fully setup and operational
- when the CAN network device isn't setup (because device tree
doesn't describe it, or disables that node), then its clock
remains idle (neither gets setup nor enabled)
- complete preparation for future improvement wrt power
consumption, where potential changes remain isolated to the
specific chip (probe() time setup, get_clock() routine) while
the ndo part need not get touched any more
So this is the most appropriate approach I can come up with.
Removing unnecessary devm_put_clk() calls is orthogonal to that.
Putting these in isn't totally wrong (they won't harm, and they
do signal "visual balance" more clearly such that the next person
won't stop and wonder), but it's true that they are redundant.
"Trained persons" will wonder as much about their presence as
untrained persons wonder about their absence. :) Apparently I'm
not well trained yet.
I thought that being explicit and cautious would be good, but the
feedback I got suggests that encoding unnecessary instructions
isn't desirable. So I will remove those devm_put_clk() in v4.
To save us one more iteration, shall I remove those calls only
from error paths during setup? Or shall I remove them from
regular shutdown paths as well? How much pain does the community
feel with harmless yet unnecessary instructions? :)
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Gerhard Sittig <hidden> Date: 2013-07-23 11:58:12
[ devicetree@vger adjusted ]
On Mon, Jul 22, 2013 at 15:09 +0100, Mark Brown wrote:
On Mon, Jul 22, 2013 at 02:14:28PM +0200, Gerhard Sittig wrote:
quoted
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ devm_clk_put(dev, clk);
+ goto free_irq;
The main point of the devm_ APIs is to avoid the need for explicit
freeing so you should just remove these puts.
OK, will do in v4.
Shall these get removed everywhere including regular shutdown
paths, or just from error paths during setup?
[ the same topic came up for the CAN patch, might answer there ]
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Gerhard Sittig <hidden> Date: 2013-07-23 12:07:33
[ adjusted devtree to use the vger address ]
On Mon, Jul 22, 2013 at 15:04 +0200, Marc Kleine-Budde wrote:
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
quoted
implement a .get_clock() callback for the MPC512x platform which uses
the common clock infrastructure (eliminating direct access to the clock
control registers from within the CAN network driver), and provide the
corresponding .put_clock() callback to release resources after use
keep the previous implementation of MPC512x support in place during
migration, since common clock support is optional
this change is neutral to the MPC5200 platform
Signed-off-by: Gerhard Sittig <redacted>
---
drivers/net/can/mscan/mpc5xxx_can.c | 169 +++++++++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
@@ -109,6 +109,167 @@ static u32 mpc52xx_can_get_clock(struct platform_device *ofdev,#endif /* CONFIG_PPC_MPC52xx */#ifdef CONFIG_PPC_MPC512x++#if IS_ENABLED(CONFIG_COMMON_CLK)++staticu32mpc512x_can_get_clock(structplatform_device*ofdev,+constchar*clock_source,int*mscan_clksrc)+{+structdevice_node*np;+u32clockdiv;+enum{+CLK_FROM_AUTO,+CLK_FROM_IPS,+CLK_FROM_SYS,+CLK_FROM_REF,+}clk_from;+structclk*clk_in,*clk_can;+unsignedlongfreq_calc;+structmscan_priv*priv;++/* the caller passed in the clock source spec that was read from+*thedevicetree,gettheoptionalclockdivideraswell+*/+np=ofdev->dev.of_node;+clockdiv=1;+of_property_read_u32(np,"fsl,mscan-clock-divider",&clockdiv);+dev_dbg(&ofdev->dev,"device tree specs: clk src[%s] div[%d]\n",+clock_source?clock_source:"<NULL>",clockdiv);++/* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to+*getset,andthe'ips'clockistheinputtotheMSCAN+*component+*+*forclock-sourcevaluesof'ref'or'sys'theCANCTL1[CLKSRC]+*bitneedstogetcleared,anoptionalclock-dividermayhave+*beenspecified(thedefaultvalueis1),theappropriate+*MSCANrelatedMCLKistheinputtotheMSCANcomponent+*+*intheabsenceofaclock-sourcespec,firstanoptimalclock+*getsdeterminedbasedonthe'sys'clock,ifthatfailsthe+*'ref'clockisused+*/+clk_from=CLK_FROM_AUTO;+if(clock_source){+/* interpret the device tree's spec for the clock source */+if(!strcmp(clock_source,"ip"))+clk_from=CLK_FROM_IPS;+elseif(!strcmp(clock_source,"sys"))+clk_from=CLK_FROM_SYS;+elseif(!strcmp(clock_source,"ref"))+clk_from=CLK_FROM_REF;+else+gotoerr_invalid;+dev_dbg(&ofdev->dev,"got a clk source spec[%d]\n",clk_from);+}+if(clk_from==CLK_FROM_AUTO){+/* no spec so far, try the 'sys' clock; round to the+*nextMHzandseeifwecangetamultipleof16MHz+*/+dev_dbg(&ofdev->dev,"no clk source spec, trying SYS\n");+clk_in=devm_clk_get(&ofdev->dev,"sys");+if(IS_ERR(clk_in))+gotoerr_notavail;+freq_calc=clk_get_rate(clk_in);+freq_calc+=499999;+freq_calc/=1000000;+freq_calc*=1000000;+if((freq_calc%16000000)==0){+clk_from=CLK_FROM_SYS;+clockdiv=freq_calc/16000000;+dev_dbg(&ofdev->dev,+"clk fit, sys[%lu] div[%d] freq[%lu]\n",+freq_calc,clockdiv,freq_calc/clockdiv);+}+}+if(clk_from==CLK_FROM_AUTO){+/* no spec so far, use the 'ref' clock */+dev_dbg(&ofdev->dev,"no clk source spec, trying REF\n");+clk_in=devm_clk_get(&ofdev->dev,"ref");+if(IS_ERR(clk_in))+gotoerr_notavail;+clk_from=CLK_FROM_REF;+freq_calc=clk_get_rate(clk_in);+dev_dbg(&ofdev->dev,+"clk fit, ref[%lu] (no div) freq[%lu]\n",+freq_calc,freq_calc);+}++/* select IPS or MCLK as the MSCAN input (returned to the caller),+*setuptheMCLKmuxsourceandrateifapplicable,applythe+*optionallyspecifiedorderivedabovedivider,anddetermine+*theactualresultingclockratetoreturntothecaller+*/+switch(clk_from){+caseCLK_FROM_IPS:+clk_can=devm_clk_get(&ofdev->dev,"ips");+if(IS_ERR(clk_can))+gotoerr_notavail;+if(clk_prepare(clk_can)){
I would just call prepare_enable in the main mscan driver, then we don't
need a special "clock is prepared but not enabled" contract.
Yes, I addressed this concern (although differently after
learning more from a local test setup), see the other reply for
the CAN driver adjustment for clock API use.
quoted
+ devm_clk_put(&ofdev->dev, clk_can);
not needed, as this driver instance will fail, doesn't it?
This is discussed in the other subthread as well. The only
remaining question is how many of them to remove, before I'll
update the series.
return 0 in case of error? Please add a comment what this 0 means here.
The .get_clock() callback is supposed to return the resulting
rate after the clock source was determined and the clock subtree
was setup. It wasn't (explicitly) documented before (in the
non-common-clock case), so I did not bother to comment it in the
parallel common-clock case.
But it's true that returning zero in the error case may be
unexpected, and I will add a comment in v4 (in all the
.get_clock() implementations).
Thank you for the fast and detailled review. It's very much
appreciated.
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Marc Kleine-Budde <mkl@pengutronix.de> Date: 2013-07-23 12:33:00
On 07/23/2013 01:53 PM, Gerhard Sittig wrote:
On Mon, Jul 22, 2013 at 14:31 +0200, Marc Kleine-Budde wrote:
quoted
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
quoted
the .get_clock() callback is run from probe() and might allocate
resources, introduce a .put_clock() callback that is run from remove()
to undo any allocation activities
looks good
quoted
use devm_get_clk() upon lookup (for SYS and REF) to have the clocks put
upon driver unload
fine
quoted
assume that resources get prepared but not necessarily enabled in the
setup phase, make the open() and close() callbacks of the CAN network
device enable and disable a previously acquired and prepared clock
I think you should call prepare_enable and disable_unprepare in the
open/close functions.
After more local research, which totally eliminated the need to
pre-enable the CAN related clocks, but might need more discussion
as it touches the common gate support, I've learned something
more:
The CAN clock needs to get enabled during probe() already, since
registers get accessed between probe() for the driver and open()
for the network device -- while access to peripheral registers
crashes the kernel when clocks still are disabled (other hardware
may just hang or provide fake data, neither of this is OK).
Then call prepare_enable(); before and disable_unprepare(); after
accessing the registers. Have a look at the flexcan driver.
But I see the point in your suggestion to prepare _and_ enable
the clock during open() as well -- to have open() cope with
whatever probe() did, after all the driver is shared among
platforms, which may differ in what they do during probe().
If you enable a clock to access the registers before open() (and disable
it afterwards), it should not harm any architecture that doesn't need
this clock enabled.
So I will:
- make open() of the network device prepare _and_ enable the
clock for the peripheral (if acquired during probe())
good
- adjust open() because ATM it leaves the clock enabled when the
network device operation fails (the error path is incomplete in
v3)
yes, clock should be disabled if open() fails.
- make the MPC512x specific probe() time .get_clock() routine not
just prepare but enable the clock as well
If needed enable the clock, but disable after probe() has finished.
- and of course address all the shutdown counter parts of the
above setup paths
This results in:
- specific chip drivers only need to balance their private get
and put clock routines which are called from probe and remove,
common paths DTRT for all of them
Yes, but clock should not stay enabled between probe() and open().
[...]
Removing unnecessary devm_put_clk() calls is orthogonal to that.
Putting these in isn't totally wrong (they won't harm, and they
do signal "visual balance" more clearly such that the next person
won't stop and wonder), but it's true that they are redundant.
"Trained persons" will wonder as much about their presence as
untrained persons wonder about their absence. :) Apparently I'm
not well trained yet.
The whole point about devm_* is to get rid of auto manually tear down
functions. So please remove all devm_put_clk() calls, as it will be
called automatically if a driver instance is removed.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
return 0 in case of error? Please add a comment what this 0 means here.
The .get_clock() callback is supposed to return the resulting
rate after the clock source was determined and the clock subtree
was setup. It wasn't (explicitly) documented before (in the
non-common-clock case), so I did not bother to comment it in the
parallel common-clock case.
Yes, but returning 0 in case of error should be documented, as it's very
uncommon.
But it's true that returning zero in the error case may be
unexpected, and I will add a comment in v4 (in all the
.get_clock() implementations).
I think a one liner stating that the return value is the clock rate and
"0" indicates is an invalid clock rate, this meaning an error here, is
enough.
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
From: Gerhard Sittig <hidden> Date: 2013-07-23 13:14:06
[ summary: "shared gate" support desirable? approach acceptable? ]
On Mon, Jul 22, 2013 at 14:14 +0200, Gerhard Sittig wrote:
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
[ ... ]
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 help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
This remark was related to the CAN clocks of the MPC512x SoC.
The clock subtrees which are involved in generating CAN bitrates
include one path from the XTAL to an internal MCLK (this is part
of the CCF support for the platform), and another path from the
MCLK or yet another IP bus clock to the actual bitrate on the
wire (this is taken care of within the mscan(4) driver).
The MCLK generation for CAN is documented in the MPC5121e
Reference Manual, chapter 5, section 5.2.5 "MSCAN Clock
Generation". SYS, REF (both internal), PSC_MCLK_IN, and SPDIF_TX
(both external) are muxed, gated, and divided. The result is
muxed with IP. The result is fed into the MSCAN component and
gets muxed with IP again (can't tell why, maybe for backwards
compatibility).
In parallel to this MCLK block there is SCCR2[25], the "BDLC and
MSCAN clock enable", documented in section 5.3.1.3 "System Clock
Control Register 2". So there is a gate that "somehow needs to
get setup" yet isn't part of the visible MCLK chain.
The series up to and including v3 approaches the problem by
- adding a gate after the second MCLK mux, which gets exported
for client lookups and is the MCLK input for the mscan(4)
driver
- creating that gate for each of the four MSCAN clocks of the
SoC, all of them referencing the single "enable" bit in the
SCCR2 register
- pre-enabling the MSCAN clocks from within the clock driver, and
thus avoid having the clock disabled from the common
infrastructure, because disabling one of these clocks had
closed the shared gate and thus had broken all other clock uses
clkdev registration provides "alias names" for few clock items
[ ... ]
[ ... ]
+
+/* setup the MCLK clock subtree of an individual PSC/MSCAN/SPDIF */
+static void mpc512x_clk_setup_mclk(struct mclk_setup_data *entry)
+{
+ size_t clks_idx_pub, clks_idx_int;
+ u32 __iomem *mccr_reg; /* MCLK control register (mux, en, div) */
+ u32 __iomem *sccr_reg; /* system clock control register (enable) */
+ int sccr_bit;
+ 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 + entry->comp_idx;
+ clks_idx_int = MPC512x_CLK_MCLKS_FIRST
+ + (entry->comp_idx) * MCLK_MAX_IDX;
+ mccr_reg = &clkregs->psc_ccr[entry->comp_idx];
+ break;
+ case MCLK_TYPE_MSCAN:
+ clks_idx_pub = MPC512x_CLK_MSCAN0_MCLK + entry->comp_idx;
+ clks_idx_int = MPC512x_CLK_MCLKS_FIRST
+ + (NR_PSCS + entry->comp_idx) * MCLK_MAX_IDX;
+ mccr_reg = &clkregs->mscan_ccr[entry->comp_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;
+ }
+ if (entry->bit_sccr1 >= 0) {
+ sccr_reg = &clkregs->sccr1;
+ sccr_bit = entry->bit_sccr1;
+ } else if (entry->bit_sccr2 >= 0) {
+ sccr_reg = &clkregs->sccr2;
+ sccr_bit = entry->bit_sccr2;
+ } else {
+ sccr_reg = NULL;
+ }
+
+ /*
+ * 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_int + MCLK_IDX_MUX1] = mpc512x_clk_muxed(
+ entry->name_mux1,
+ &entry->parent_names_mux1[0],
+ ARRAY_SIZE(entry->parent_names_mux1),
+ mccr_reg, 7, 1);
+ } else {
+ clks[clks_idx_int + MCLK_IDX_MUX1] = mpc512x_clk_factor(
+ entry->name_mux1, entry->parent_names_mux1[0],
+ 1, 1);
+ }
+ if (sccr_reg) {
+ clks[clks_idx_pub] = mpc512x_clk_gated(
+ entry->name_mclk,
+ entry->name_mux1, sccr_reg, sccr_bit);
+ } else {
+ clks[clks_idx_pub] = mpc512x_clk_factor(
+ entry->name_mclk,
+ entry->name_mux1, 1, 1);
+ }
+
+ /*
+ * without this "clock device" registration, "simple" lookups in
+ * the SPI master initialization and serial port setup will fail
+ *
+ * those drivers need to get adjusted to lookup their required
+ * clocks from device tree specs, and device tree nodes need to
+ * provide the clock specs, before this clkdev registration
+ * becomes obsolete
+ */
+ clk_register_clkdev(clks[clks_idx_pub], entry->name_mclk, NULL);
+}
[ ... ]
This was the routine which sets up _one_ MCLK block, note the
assignment at the routine's end to the "published" clock item
that's the gate's output after the second mux stage.
This is the invocation of the routine which sets up four MCLK
blocks for the MSCAN components, while all of them refer to bit
25 of SCCR2.
[ ... ]
+
+ /* enable some of the clocks here unconditionally because ... */
+ pr_debug("automatically enabling some clocks\n");
+ /* some are essential yet never get claimed by any driver */
+ 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 */
+ /* some are required yet no dependencies were declared */
+ clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);
+ /* some are not yet acquired by their respective drivers */
+ 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]);
+ /*
+ * some have their individual clock subtree with separate clock
+ * items and their individual enable counters, yet share a
+ * common gate (refer to the same register location) while the
+ * common clock driver code is not aware of the fact and the
+ * platform's code doesn't provide specific support either
+ *
+ * what might happen is that e.g. enabling two MSCAN clock items
+ * and disabling one of them will disable the common gate and
+ * thus break the other MSCAN clock as well
+ */
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN1_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN2_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN3_MCLK]);
+}
This is the pre-enable workaround for the MSCAN0 to MSCAN3 clock
items.
The above approach does work in that it introduces complete
support for common clock on the MPC512x platform, with the CAN
component being operational, and the clock driver using shared
logic across platforms.
The remaining issue is that regardless of whether CAN is used,
the (chip internal) clock is enabled. This may not be a problem
when bitrates aren't generated and the wire isn't driven.
The question now is how to correctly support the situation where
a gate is shared between subtrees yet isn't really part of any
path within the subtrees. I really cannot find a single spot
where to introduce the gate such that it's not duplicated.
The appropriate solution would not be to pre-enable those clocks,
but to either introduce another gate clock type which supports a
shared reference, or to add support for the shared reference to
the existing gate code.
I'd rather not duplicate most or all of the code of clk-gate.c,
instead I looked into how to add "shared gate" support to the
existing driver.
My question is whether the approach is acceptable. It adds
minimal overhead and shall be OK for the enable/disable path from
a technical POV. And it doesn't feel like too much of a stretch.
But there may be non-technical reasons to reject the approach.
I'd like to learn whether to follow that path before preparing
another version of the patch series.
The diffs were taken with the '-w -b' options to demonstrate
their essence and not drown it in whitespace changes. The
implementation assumes that the caller which registers the gate
(the platform's clock driver) provides both the counter cell and
the lock. And that all gates with a "shared use counter" use the
same lock (which is satisfied as they all get registered from the
same spot in the platform's clock driver).
The CLK_IGNORE_UNUSED flag addresses a different problem. The
SoC has four MSCAN components, while two of them are enabled in
the device tree (the other two are present but disabled). So
during probe two of the clocks get enabled. After probe all
unused clocks automatically get disabled (that's another two).
So the "shared use counter" drops to zero although components are
in use, because "disable, it's unused" isn't told from "disable
after enable, regular use". The flag would become obsolete if
the common gate logic would implement a separate disable_unused()
routine, but I guess this isn't necessary and the use of the flag
is appropriate.
That the example use creates a field for just one counter is to
better demonstrate the use and potential extension as need
arises. Reducing this to a mere integer variable would be a
micro optimization.
The extension of the existing clk_gate implementation:
reg, pos, 0, &clklock);
}
+enum mpc512x_clk_shared_gate_id_t {
+ MPC512x_CLK_SHARED_GATE_MSCAN,
+ MPC512x_CLK_SHARED_GATE_MAX,
+};
+
+static int mpc512x_clk_gate_counters[MPC512x_CLK_SHARED_GATE_MAX];
+
+/*
+ * implementor's note: since clk_gate items don't implement a separate
+ * .disable_unused() callback, their .disable() routine gets called and
+ * "disable the clock as we can't see it's in use" cannot be told from
+ * "regular disable, count these events please"
+ *
+ * passing the CLK_IGNORE_UNUSED flag upon clock creation will suppress
+ * the "disable, unused" call, so use counts won't get unbalanced, the
+ * clock either never got enabled and thus need not get disabled, or
+ * part of the hardware got enabled while disabling the other part isn't
+ * wanted
+ */
+static inline struct clk *mpc512x_clk_gated_shared(
+ const char *name, const char *parent_name,
+ u32 __iomem *reg, u8 pos,
+ enum mpc512x_clk_shared_gate_id_t share_id)
+{
+ int clkflags;
+
+ clkflags = CLK_SET_RATE_PARENT;
+ clkflags |= CLK_IGNORE_UNUSED;
+ return clk_register_gate_shared(NULL, name, parent_name, clkflags,
+ reg, pos, 0, &clklock,
+ &mpc512x_clk_gate_counters[share_id]);
+}
+
static inline struct clk *mpc512x_clk_muxed(const char *name,
const char **parent_names, int parent_count,
u32 __iomem *reg, u8 pos, u8 len)
1, 1);
}
if (sccr_reg) {
+ if (entry->type == MCLK_TYPE_MSCAN) {
+ clks[clks_idx_pub] = mpc512x_clk_gated_shared(
+ entry->name_mclk,
+ entry->name_mux1, sccr_reg, sccr_bit,
+ MPC512x_CLK_SHARED_GATE_MSCAN);
+ } else {
clks[clks_idx_pub] = mpc512x_clk_gated(
entry->name_mclk,
entry->name_mux1, sccr_reg, sccr_bit);
+ }
} else {
clks[clks_idx_pub] = mpc512x_clk_factor(
entry->name_mclk,
Local tests have shown that the extension solves the problem of
how to satisfy the SoC's constraints on the MPC512x platform.
The MSCAN clocks no longer need to get pre-enabled, instead they
get setup and enabled only as the mscan(4) driver probes devices
according to how it was instructed (device tree nodes).
What do you think? Is the "shared gate" support in the common
logic appropriate? I'd rather not duplicate all of this code
just to introduce the specific gate I need, while most of the
logic is identical to the existing gate implementation. The
desire isn't to override the gate's operations, but to wrap them
and to consult a counter in addition, while the register access
still applies.
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Mike Turquette <hidden> Date: 2013-08-02 22:30:00
Quoting Gerhard Sittig (2013-07-22 05:14:40)
the common clock drivers were motivated/initiated by ARM development
and apparently assume little endian peripherals
=
wrap register/peripherals access in the common code (div, gate, mux)
in preparation of adding COMMON_CLK support for other platforms
=
Signed-off-by: Gerhard Sittig <redacted>
I've taken this into clk-next for testing. regmap deserves investigation
but I don't think your series should be blocked on that. We can always
overhaul the basic clock primitives with regmap support later on if that
makes sense.
Regards,
Mike
* val =3D 0x4 really means "bit 2, index starts at bit 0"
*/
- val =3D readl(mux->reg) >> mux->shift;
+ val =3D clk_readl(mux->reg) >> mux->shift;
val &=3D mux->mask;
=
From: Mike Turquette <hidden> Date: 2013-08-02 22:43:08
Quoting Gerhard Sittig (2013-07-22 05:14:42)
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
=
Signed-off-by: Gerhard Sittig <redacted>
---
include/dt-bindings/clock/mpc512x-clock.h | 59 +++++++++++++++++++++++=
From: Mike Turquette <hidden> Date: 2013-08-02 22:46:13
Quoting Gerhard Sittig (2013-07-22 05:14:43)
this addresses the clock driver aka provider's side of clocks
- prepare for future '<&clks ID>' phandle references for device tree
based clock lookup in client drivers
- introduce a 'clocks' subtree with an 'osc' node for the crystal
or oscillator SoC input (fixed frequency)
- provide default values with 33MHz oscillator frequency in the
common include (the 66MHz IPS bus already was there), add
override values for the ifm AC14xx board which deviates from
the reference design (25MHz xtal, 80MHz IPS bus)
=
From: Mike Turquette <hidden> Date: 2013-08-02 23:30:09
Quoting Gerhard Sittig (2013-07-23 06:14:06)
[ summary: "shared gate" support desirable? approach acceptable? ]
=
On Mon, Jul 22, 2013 at 14:14 +0200, Gerhard Sittig wrote:
quoted
=
quoted
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
=
quoted
[ ... ]
=
quoted
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 help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
=
This remark was related to the CAN clocks of the MPC512x SoC.
Gerhard,
Thanks for the patch (way far down below here). I'll check into it to
see if that implementation looks OK. It would be helpful if another
platform with shared gates could weigh in on whether the implementation
works for them.
Still, a shared gate solution is not a prerequisite for this series,
correct?
Regards,
Mike
=
The clock subtrees which are involved in generating CAN bitrates
include one path from the XTAL to an internal MCLK (this is part
of the CCF support for the platform), and another path from the
MCLK or yet another IP bus clock to the actual bitrate on the
wire (this is taken care of within the mscan(4) driver).
=
The MCLK generation for CAN is documented in the MPC5121e
Reference Manual, chapter 5, section 5.2.5 "MSCAN Clock
Generation". SYS, REF (both internal), PSC_MCLK_IN, and SPDIF_TX
(both external) are muxed, gated, and divided. The result is
muxed with IP. The result is fed into the MSCAN component and
gets muxed with IP again (can't tell why, maybe for backwards
compatibility).
=
In parallel to this MCLK block there is SCCR2[25], the "BDLC and
MSCAN clock enable", documented in section 5.3.1.3 "System Clock
Control Register 2". So there is a gate that "somehow needs to
get setup" yet isn't part of the visible MCLK chain.
=
The series up to and including v3 approaches the problem by
- adding a gate after the second MCLK mux, which gets exported
for client lookups and is the MCLK input for the mscan(4)
driver
- creating that gate for each of the four MSCAN clocks of the
SoC, all of them referencing the single "enable" bit in the
SCCR2 register
- pre-enabling the MSCAN clocks from within the clock driver, and
thus avoid having the clock disabled from the common
infrastructure, because disabling one of these clocks had
closed the shared gate and thus had broken all other clock uses
=
quoted
clkdev registration provides "alias names" for few clock items
[ ... ]
=
[ ... ]
quoted
+
+/* setup the MCLK clock subtree of an individual PSC/MSCAN/SPDIF */
+static void mpc512x_clk_setup_mclk(struct mclk_setup_data *entry)
+{
+ size_t clks_idx_pub, clks_idx_int;
+ u32 __iomem *mccr_reg; /* MCLK control register (mux, en, div) */
+ u32 __iomem *sccr_reg; /* system clock control register (enable)=
*/
quoted
+ int sccr_bit;
+ int div;
+
+ /* derive a few parameters from the component type and index */
+ switch (entry->type) {
+ case MCLK_TYPE_PSC:
+ clks_idx_pub =3D MPC512x_CLK_PSC0_MCLK + entry->comp_idx;
+ clks_idx_int =3D MPC512x_CLK_MCLKS_FIRST
+ + (entry->comp_idx) * MCLK_MAX_IDX;
+ mccr_reg =3D &clkregs->psc_ccr[entry->comp_idx];
+ break;
+ case MCLK_TYPE_MSCAN:
+ clks_idx_pub =3D MPC512x_CLK_MSCAN0_MCLK + entry->comp_id=
x;
quoted
+ clks_idx_int =3D MPC512x_CLK_MCLKS_FIRST
+ + (NR_PSCS + entry->comp_idx) * MCLK_MAX_IDX;
+ mccr_reg =3D &clkregs->mscan_ccr[entry->comp_idx];
+ break;
+ case MCLK_TYPE_SPDIF:
+ clks_idx_pub =3D MPC512x_CLK_SPDIF_MCLK;
+ clks_idx_int =3D MPC512x_CLK_MCLKS_FIRST
+ + (NR_PSCS + NR_MSCANS) * MCLK_MAX_IDX;
+ mccr_reg =3D &clkregs->spccr;
+ break;
+ default:
+ return;
+ }
+ if (entry->bit_sccr1 >=3D 0) {
+ sccr_reg =3D &clkregs->sccr1;
+ sccr_bit =3D entry->bit_sccr1;
+ } else if (entry->bit_sccr2 >=3D 0) {
+ sccr_reg =3D &clkregs->sccr2;
+ sccr_bit =3D entry->bit_sccr2;
+ } else {
+ sccr_reg =3D NULL;
+ }
+
+ /*
+ * 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 <=3D IPS" constraint, the fixed divider value of 1
+ * results in a divider of 2 and thus MCLK =3D 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 =3D clk_get_rate(clks[MPC512x_CLK_SYS]);
+ div /=3D 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] =3D mpc512x_clk_muxed(
+ entry->name_mux0,
+ &parent_names_mux0[0], ARRAY_SIZE(parent_names_mu=
+ 1, 1);
+ }
+ if (sccr_reg) {
+ clks[clks_idx_pub] =3D mpc512x_clk_gated(
+ entry->name_mclk,
+ entry->name_mux1, sccr_reg, sccr_bit);
+ } else {
+ clks[clks_idx_pub] =3D mpc512x_clk_factor(
+ entry->name_mclk,
+ entry->name_mux1, 1, 1);
+ }
+
+ /*
+ * without this "clock device" registration, "simple" lookups in
+ * the SPI master initialization and serial port setup will fail
+ *
+ * those drivers need to get adjusted to lookup their required
+ * clocks from device tree specs, and device tree nodes need to
+ * provide the clock specs, before this clkdev registration
+ * becomes obsolete
+ */
+ clk_register_clkdev(clks[clks_idx_pub], entry->name_mclk, NULL);
+}
[ ... ]
=
This was the routine which sets up _one_ MCLK block, note the
assignment at the routine's end to the "published" clock item
that's the gate's output after the second mux stage.
=
This is the invocation of the routine which sets up four MCLK
blocks for the MSCAN components, while all of them refer to bit
25 of SCCR2.
=
quoted
[ ... ]
+
+ /* enable some of the clocks here unconditionally because ... */
+ pr_debug("automatically enabling some clocks\n");
+ /* some are essential yet never get claimed by any driver */
+ 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 */
+ /* some are required yet no dependencies were declared */
+ clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);
+ /* some are not yet acquired by their respective drivers */
+ clk_prepare_enable(clks[MPC512x_CLK_PSC3_MCLK]);/* serial console=
*/
quoted
+ clk_prepare_enable(clks[MPC512x_CLK_FEC]); /* network, NFS */
+ clk_prepare_enable(clks[MPC512x_CLK_DIU]); /* display */
+ clk_prepare_enable(clks[MPC512x_CLK_I2C]);
+ /*
+ * some have their individual clock subtree with separate clock
+ * items and their individual enable counters, yet share a
+ * common gate (refer to the same register location) while the
+ * common clock driver code is not aware of the fact and the
+ * platform's code doesn't provide specific support either
+ *
+ * what might happen is that e.g. enabling two MSCAN clock items
+ * and disabling one of them will disable the common gate and
+ * thus break the other MSCAN clock as well
+ */
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN1_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN2_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN3_MCLK]);
+}
=
This is the pre-enable workaround for the MSCAN0 to MSCAN3 clock
items.
=
The above approach does work in that it introduces complete
support for common clock on the MPC512x platform, with the CAN
component being operational, and the clock driver using shared
logic across platforms.
=
The remaining issue is that regardless of whether CAN is used,
the (chip internal) clock is enabled. This may not be a problem
when bitrates aren't generated and the wire isn't driven.
=
=
The question now is how to correctly support the situation where
a gate is shared between subtrees yet isn't really part of any
path within the subtrees. I really cannot find a single spot
where to introduce the gate such that it's not duplicated.
=
The appropriate solution would not be to pre-enable those clocks,
but to either introduce another gate clock type which supports a
shared reference, or to add support for the shared reference to
the existing gate code.
=
=
I'd rather not duplicate most or all of the code of clk-gate.c,
instead I looked into how to add "shared gate" support to the
existing driver.
=
My question is whether the approach is acceptable. It adds
minimal overhead and shall be OK for the enable/disable path from
a technical POV. And it doesn't feel like too much of a stretch.
But there may be non-technical reasons to reject the approach.
I'd like to learn whether to follow that path before preparing
another version of the patch series.
=
The diffs were taken with the '-w -b' options to demonstrate
their essence and not drown it in whitespace changes. The
implementation assumes that the caller which registers the gate
(the platform's clock driver) provides both the counter cell and
the lock. And that all gates with a "shared use counter" use the
same lock (which is satisfied as they all get registered from the
same spot in the platform's clock driver).
=
The CLK_IGNORE_UNUSED flag addresses a different problem. The
SoC has four MSCAN components, while two of them are enabled in
the device tree (the other two are present but disabled). So
during probe two of the clocks get enabled. After probe all
unused clocks automatically get disabled (that's another two).
So the "shared use counter" drops to zero although components are
in use, because "disable, it's unused" isn't told from "disable
after enable, regular use". The flag would become obsolete if
the common gate logic would implement a separate disable_unused()
routine, but I guess this isn't necessary and the use of the flag
is appropriate.
=
That the example use creates a field for just one counter is to
better demonstrate the use and potential extension as need
arises. Reducing this to a mere integer variable would be a
micro optimization.
=
=
The extension of the existing clk_gate implementation:
=
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);
=
quoted hunk
+ /*
+ * if a "shared use counter" was specified, keep track of enable
+ * and disable calls and only access hardware registers upon the
+ * very first enable or very last disable call
+ */
+ if (!gate->share_count) {
+ need_reg_access =3D 1;
+ } else if (enable) {
+ need_reg_access =3D (*gate->share_count)++ =3D=3D 0;
+ } else {
+ need_reg_access =3D --(*gate->share_count) =3D=3D 0;
+ }
+
+ if (need_reg_access) {
if (gate->flags & CLK_GATE_HIWORD_MASK) {
reg =3D BIT(gate->bit_idx + 16);
if (set)
+enum mpc512x_clk_shared_gate_id_t {
+ MPC512x_CLK_SHARED_GATE_MSCAN,
+ MPC512x_CLK_SHARED_GATE_MAX,
+};
+
+static int mpc512x_clk_gate_counters[MPC512x_CLK_SHARED_GATE_MAX];
+
+/*
+ * implementor's note: since clk_gate items don't implement a separate
+ * .disable_unused() callback, their .disable() routine gets called and
+ * "disable the clock as we can't see it's in use" cannot be told from
+ * "regular disable, count these events please"
+ *
+ * passing the CLK_IGNORE_UNUSED flag upon clock creation will suppress
+ * the "disable, unused" call, so use counts won't get unbalanced, the
+ * clock either never got enabled and thus need not get disabled, or
+ * part of the hardware got enabled while disabling the other part isn't
+ * wanted
+ */
+static inline struct clk *mpc512x_clk_gated_shared(
+ const char *name, const char *parent_name,
+ u32 __iomem *reg, u8 pos,
+ enum mpc512x_clk_shared_gate_id_t share_id)
+{
+ int clkflags;
+
+ clkflags =3D CLK_SET_RATE_PARENT;
+ clkflags |=3D CLK_IGNORE_UNUSED;
+ return clk_register_gate_shared(NULL, name, parent_name, clkflags,
+ reg, pos, 0, &clklock,
+ &mpc512x_clk_gate_counters[share_=
Local tests have shown that the extension solves the problem of
how to satisfy the SoC's constraints on the MPC512x platform.
The MSCAN clocks no longer need to get pre-enabled, instead they
get setup and enabled only as the mscan(4) driver probes devices
according to how it was instructed (device tree nodes).
=
What do you think? Is the "shared gate" support in the common
logic appropriate? I'd rather not duplicate all of this code
just to introduce the specific gate I need, while most of the
logic is identical to the existing gate implementation. The
desire isn't to override the gate's operations, but to wrap them
and to consult a counter in addition, while the register access
still applies.
=
From: Mike Turquette <hidden> Date: 2013-08-02 23:41:20
Quoting Gerhard Sittig (2013-07-22 05:14:45)
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
=
these specs map 'clock-names' encoded in drivers to their respective
'struct clk' items in the platform's clock driver
=
From: Mike Turquette <hidden> Date: 2013-08-02 23:41:43
Quoting Gerhard Sittig (2013-07-22 05:14:44)
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 (which can
get referenced by means of symbolic identifiers from 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 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 in
parallel to the common clock implementation for test and comparison
during migration, a compile time option picks one of the two
alternatives (Kconfig switch, common clock used by default)
=
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)
- some help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
=
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
=
Signed-off-by: Gerhard Sittig <redacted>
Hi Gerhard,
This looks OK to me. Do you want me to take it or will you keep the
series together? Note that I took "clk: wrap I/O access for improved
portability" into the clk tree already.
Regards,
Mike
@@ -1,7 +1,9 @@## Makefile for the Freescale PowerPC 512x linux kernel.#-obj-y+=3Dclock.ompc512x_shared.o+obj-$(CONFIG_PPC_CLOCK)+=3Dclock.o+obj-$(CONFIG_COMMON_CLK)+=3Dclock-commonclk.o+obj-y+=3Dmpc512x_shared.oobj-$(CONFIG_MPC5121_ADS)+=3Dmpc5121_ads.ompc5121_ads_cpld.oobj-$(CONFIG_MPC512x_GENERIC)+=3Dmpc512x_generic.oobj-$(CONFIG_PDM360NG)+=3Dpdm360ng.o
@@ -0,0 +1,786 @@+/*+*Copyright(C)2013DENXSoftwareEngineering+*+*GerhardSittig,<gsi@denx.de>+*+*commonclockdriversupportfortheMPC512xplatform+*+*Thisisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<linux/clk-provider.h>+#include<linux/clkdev.h>+#include<linux/device.h>+#include<linux/errno.h>+#include<linux/io.h>+#include<linux/of.h>++#include<asm/mpc5121.h>+#include<dt-bindings/clock/mpc512x-clock.h>++#include"mpc512x.h" /* our public mpc5121_clk_init() API */++/* helpers to keep the MCLK intermediates "somewhere" in our table */+enum{+MCLK_IDX_MUX0,+MCLK_IDX_EN0,+MCLK_IDX_DIV0,+MCLK_IDX_MUX1,+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 manageme=
nt */
+enum {
+ /* arrange for adjacent numbers after the public set */
+ MPC512x_CLK_START_PRIVATE =3D 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 =3D 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, rat=
+{
+ uint32_t val;
+
+ val =3D in_be32(reg);
+ val >>=3D pos;
+ val &=3D (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[] =3D {
+ 68, 1, 12, 16, 20, 24, 28, 32,
+ 36, 40, 44, 48, 52, 56, 60, 64,
+ };
+ int spmf;
+
+ spmf =3D 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[] =3D {
+ 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 =3D 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[] =3D {
+ 72, 2, 2, 3, 4, 5, 6, 7,
+ };
+ int cpmf;
+
+ cpmf =3D 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[] =3D {
+ { .val =3D 2, .div =3D 2, },
+ { .val =3D 3, .div =3D 3, },
+ { .val =3D 4, .div =3D 4, },
+ { .val =3D 6, .div =3D 6, },
+ { .div =3D 0, },
+};
+
+/* applies to the MBX_DIV, LPC_DIV, and NFC_DIV values */
+static struct clk_div_table divtab_1234[] =3D {
+ { .val =3D 1, .div =3D 1, },
+ { .val =3D 2, .div =3D 2, },
+ { .val =3D 3, .div =3D 3, },
+ { .val =3D 4, .div =3D 4, },
+ { .div =3D 0, },
+};
+
+static int get_freq_from_dt(char *propname)
+{
+ struct device_node *np;
+ const unsigned int *prop;
+ int val;
+
+ val =3D 0;
+ np =3D of_find_compatible_node(NULL, NULL, "fsl,mpc5121-immr");
+ if (np) {
+ prop =3D of_get_property(np, propname, NULL);
+ if (prop)
+ val =3D *prop;
+ of_node_put(np);
+ }
+ return val;
+}
+
+static void mpc512x_clk_preset_data(void)
+{
+ size_t i;
+
+ for (i =3D 0; i < ARRAY_SIZE(clks); i++)
+ clks[i] =3D 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(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 =3D get_spmf_mult();
+ *sys_mul *=3D 2; /* compensate for the fractional divide=
+ }
+}
+
+/*
+ * 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 <linux/clk-provider.h> expects,
+ * making this declaration pass checkpatch will break compilation
+ */
+static const char *parent_names_mux0[] =3D {
+ "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;
+ int comp_idx;
+ bool has_mclk1;
+ int bit_sccr1, bit_sccr2;
+ const char *name_mux0;
+ const char *name_en0;
+ const char *name_div0;
+ const char *parent_names_mux1[2];
+ const char *name_mux1;
+ const char *name_mclk;
+};
+
+#define MCLK_SETUP_DATA_PSC(id) { \
+ MCLK_TYPE_PSC, id, \
+ 0, 27 - id, -1, \
+ "psc" #id "-mux0", \
+ "psc" #id "-en0", \
+ "psc" #id "_mclk_div", \
+ { "psc" #id "_mclk_div", "dummy", }, \
+ "psc" #id "_mclk_out", \
+ "psc" #id "_mclk", \
+}
+
+#define MCLK_SETUP_DATA_MSCAN(id) { \
+ MCLK_TYPE_MSCAN, id, \
+ 0, -1, 25, \
+ "mscan" #id "-mux0", \
+ "mscan" #id "-en0", \
+ "mscan" #id "_mclk_div", \
+ { "mscan" #id "_mclk_div", "dummy", }, \
+ "mscan" #id "_mclk_out", \
+ "mscan" #id "_mclk", \
+}
+
+#define MCLK_SETUP_DATA_SPDIF { \
+ MCLK_TYPE_SPDIF, 0, \
+ 1, -1, 23, \
+ "spdif-mux0", \
+ "spdif-en0", \
+ "spdif_mclk_div", \
+ { "spdif_mclk_div", "spdif-rx", }, \
+ "spdif_mclk_out", \
+ "spdif_mclk", \
+}
+
+static struct mclk_setup_data mclk_psc_data[] =3D {
+ 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[] =3D {
+ 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[] =3D {
+ 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 clks_idx_pub, clks_idx_int;
+ u32 __iomem *mccr_reg; /* MCLK control register (mux, en, div) */
+ u32 __iomem *sccr_reg; /* system clock control register (enable)=
*/
+ int sccr_bit;
+ int div;
+
+ /* derive a few parameters from the component type and index */
+ switch (entry->type) {
+ case MCLK_TYPE_PSC:
+ clks_idx_pub =3D MPC512x_CLK_PSC0_MCLK + entry->comp_idx;
+ clks_idx_int =3D MPC512x_CLK_MCLKS_FIRST
+ + (entry->comp_idx) * MCLK_MAX_IDX;
+ mccr_reg =3D &clkregs->psc_ccr[entry->comp_idx];
+ break;
+ case MCLK_TYPE_MSCAN:
+ clks_idx_pub =3D MPC512x_CLK_MSCAN0_MCLK + entry->comp_id=
x;
+ clks_idx_int =3D MPC512x_CLK_MCLKS_FIRST
+ + (NR_PSCS + entry->comp_idx) * MCLK_MAX_IDX;
+ mccr_reg =3D &clkregs->mscan_ccr[entry->comp_idx];
+ break;
+ case MCLK_TYPE_SPDIF:
+ clks_idx_pub =3D MPC512x_CLK_SPDIF_MCLK;
+ clks_idx_int =3D MPC512x_CLK_MCLKS_FIRST
+ + (NR_PSCS + NR_MSCANS) * MCLK_MAX_IDX;
+ mccr_reg =3D &clkregs->spccr;
+ break;
+ default:
+ return;
+ }
+ if (entry->bit_sccr1 >=3D 0) {
+ sccr_reg =3D &clkregs->sccr1;
+ sccr_bit =3D entry->bit_sccr1;
+ } else if (entry->bit_sccr2 >=3D 0) {
+ sccr_reg =3D &clkregs->sccr2;
+ sccr_bit =3D entry->bit_sccr2;
+ } else {
+ sccr_reg =3D NULL;
+ }
+
+ /*
+ * 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 <=3D IPS" constraint, the fixed divider value of 1
+ * results in a divider of 2 and thus MCLK =3D 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 =3D clk_get_rate(clks[MPC512x_CLK_SYS]);
+ div /=3D 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] =3D mpc512x_clk_muxed(
+ entry->name_mux0,
+ &parent_names_mux0[0], ARRAY_SIZE(parent_names_mu=
+ 1, 1);
+ }
+ if (sccr_reg) {
+ clks[clks_idx_pub] =3D mpc512x_clk_gated(
+ entry->name_mclk,
+ entry->name_mux1, sccr_reg, sccr_bit);
+ } else {
+ clks[clks_idx_pub] =3D mpc512x_clk_factor(
+ entry->name_mclk,
+ entry->name_mux1, 1, 1);
+ }
+
+ /*
+ * without this "clock device" registration, "simple" lookups in
+ * the SPI master initialization and serial port setup will fail
+ *
+ * those drivers need to get adjusted to lookup their required
+ * clocks from device tree specs, and device tree nodes need to
+ * provide the clock specs, before this clkdev registration
+ * becomes obsolete
+ */
+ clk_register_clkdev(clks[clks_idx_pub], entry->name_mclk, NULL);
+}
+
+static void mpc512x_clk_setup_mclks(struct mclk_setup_data *table, size_=
t count)
+{
+ while (count-- > 0)
+ mpc512x_clk_setup_mclk(table++);
+}
+
+static void mpc512x_clk_setup_clock_tree(int busfreq)
+{
+ int sys_mul, sys_div, ips_div;
+ int mul, div;
+ int freq;
+
+ /*
+ * TODO
+ * - 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 see the comment about
+ * "intermediates are referenced from elsewhere, too")
+ * - trim the list of auto-enabled clocks after drivers acquire
+ * them correctly as needed
+ */
+
+ /* regardless of whether XTAL/OSC exists, have REF created */
+ mpc512x_clk_setup_ref_clock(busfreq, &sys_mul, &sys_div, &ips_div=
+
+ /*
+ * 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 =3D get_cpmf_mult_x2();
+ div =3D 2; /* compensate for the fractional factor */
+ clks[MPC512x_CLK_E300] =3D mpc512x_clk_factor("e300", "csb", mul,=
+
+ /* fixed frequency for AC97, always 24.567MHz */
+ clks[MPC512x_CLK_AC97] =3D mpc512x_clk_fixed("ac97", 24567000);
+
+ /* clkdev registration for compatibility reasons */
+ 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);
+
+ pr_debug("clock tree setup complete\n");
+ freq =3D clk_get_rate(clks[MPC512x_CLK_E300]);
+ pr_debug("derived PPC freq [%d]\n", freq);
+ freq =3D clk_get_rate(clks[MPC512x_CLK_IPS]);
+ pr_debug("derived IPS freq [%d]\n", freq);
+ freq =3D clk_get_rate(clks[MPC512x_CLK_LPC]);
+ pr_debug("derived LPC freq [%d]\n", freq);
+
+ /* enable some of the clocks here unconditionally because ... */
+ pr_debug("automatically enabling some clocks\n");
+ /* some are essential yet never get claimed by any driver */
+ 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 */
+ /* some are required yet no dependencies were declared */
+ clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);
+ /* some are not yet acquired by their respective drivers */
+ 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]);
+ /*
+ * some have their individual clock subtree with separate clock
+ * items and their individual enable counters, yet share a
+ * common gate (refer to the same register location) while the
+ * common clock driver code is not aware of the fact and the
+ * platform's code doesn't provide specific support either
+ *
+ * what might happen is that e.g. enabling two MSCAN clock items
+ * and disabling one of them will disable the common gate and
+ * thus break the other MSCAN clock as well
+ */
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN1_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN2_MCLK]);
+ clk_prepare_enable(clks[MPC512x_CLK_MSCAN3_MCLK]);
+}
+
+/*
+ * 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 =3D clks;
+ clk_data.clk_num =3D MPC512x_CLK_LAST_PUBLIC + 1; /* _not_ ARRAY_=
+ if (!clk_np)
+ return -ENODEV;
+ clkregs =3D 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] =3D 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 =3D get_freq_from_dt("bus-frequency");
+ mpc512x_clk_setup_clock_tree(busfreq);
+
+ /* register as an OF clock provider */
+ mpc5121_clk_register_of_provider(clk_np);
+
+ return 0;
+}
From: Gerhard Sittig <hidden> Date: 2013-08-03 14:08:46
[ trimming the CC: list for this strictly clock related and
source code adjusting change, to not spam the device tree ML or
other subsystem maintainers, just keeping ARM (for clock) and
PPC lists and people in the loop ]
On Fri, Aug 02, 2013 at 15:30 -0700, Mike Turquette wrote:
Quoting Gerhard Sittig (2013-07-22 05:14:40)
quoted
the common clock drivers were motivated/initiated by ARM development
and apparently assume little endian peripherals
wrap register/peripherals access in the common code (div, gate, mux)
in preparation of adding COMMON_CLK support for other platforms
Signed-off-by: Gerhard Sittig <redacted>
I've taken this into clk-next for testing. regmap deserves investigation
but I don't think your series should be blocked on that. We can always
overhaul the basic clock primitives with regmap support later on if that
makes sense.
Regards,
Mike
That's fine. Though I will re-post this change when updating the
series, but this should not harm (won't conflict) as this
specific patch is stable and won't change any longer. Keeping
this one patch in the series keeps the series applicable on top
of v3.11-rcN as well as clk-next.
Note that this patch only changes those parts of the code under
drivers/clk/ which get shared among platforms (div, gate, mux).
It doesn't touch non-shared and platform specific drivers. I
felt this was the most appropriate thing to do.
@@ -89,7 +89,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw)u32reg;structclk_gate*gate=to_clk_gate(hw);-reg=readl(gate->reg);+reg=clk_readl(gate->reg);/* if a set bit disables this clk, flip it before masking */if(gate->flags&CLK_GATE_SET_TO_DISABLE)
@@ -42,7 +42,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)*OTOH,pmd_trace_clk_mux_ckusesaseparatebitforeachclock,so*val=0x4reallymeans"bit 2, index starts at bit 0"*/-val=readl(mux->reg)>>mux->shift;+val=clk_readl(mux->reg)>>mux->shift;val&=mux->mask;if(mux->table){
From: Gerhard Sittig <hidden> Date: 2013-08-03 14:19:54
On Fri, Aug 02, 2013 at 15:43 -0700, Mike Turquette wrote:
Quoting Gerhard Sittig (2013-07-22 05:14:42)
quoted
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
Signed-off-by: Gerhard Sittig <redacted>
---
include/dt-bindings/clock/mpc512x-clock.h | 59 +++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 include/dt-bindings/clock/mpc512x-clock.h
From: Gerhard Sittig <hidden> Date: 2013-08-03 14:39:56
[ we are strictly talking about clocks and source code again,
I have trimmed the CC: list to not spam the device tree ML or
subsystem maintainers ]
On Fri, Aug 02, 2013 at 16:30 -0700, Mike Turquette wrote:
Quoting Gerhard Sittig (2013-07-23 06:14:06)
quoted
[ summary: "shared gate" support desirable? approach acceptable? ]
On Mon, Jul 22, 2013 at 14:14 +0200, Gerhard Sittig wrote:
quoted
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
[ ... ]
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 help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
This remark was related to the CAN clocks of the MPC512x SoC.
Gerhard,
Thanks for the patch (way far down below here). I'll check into it to
see if that implementation looks OK. It would be helpful if another
platform with shared gates could weigh in on whether the implementation
works for them.
Still, a shared gate solution is not a prerequisite for this series,
correct?
Well, the recent CAN driver related discussion suggested that I
had a mental misconception there. The need for "shared gates"
was felt because of mixing up unrelated paths in the clock tree.
But the MCLK subtree is for bitrate generation, while the BDLC
gate is for register access into the peripheral controller.
Currently I'm investigating how I can cleanly tell those
individual aspects apart. Telling the gate for register access
(in ARM speak often referred to as 'ipg') from the bitrate
generation (the 'per' clock, or 'mclk' here) seems so much more
appropriate.
After clean separation, and more testing to make sure nothing
gets broken throughout the series, there will be v4.
So "shared gate" support might have become obsolete for the
MPC512x platform. But if others need it, the outlined approach
(patch below) may be viable. The change to the common code is
minimal. The use in the platform's clock driver was kind of
overengineered for the case of exactly one such gate, but this
immediately makes it a working approach for several gates, if
others need it.
I'll trim the motivation and just leave the suggested approach
for "shared gates" here. Feel free to drop it or to only
resurrect it as the need may re-arise later. So far nobody
appears to have felt the need up to now ...
quoted
[ ... ]
The question now is how to correctly support the situation where
a gate is shared between subtrees yet isn't really part of any
path within the subtrees. I really cannot find a single spot
where to introduce the gate such that it's not duplicated.
The appropriate solution would not be to pre-enable those clocks,
but to either introduce another gate clock type which supports a
shared reference, or to add support for the shared reference to
the existing gate code.
I'd rather not duplicate most or all of the code of clk-gate.c,
instead I looked into how to add "shared gate" support to the
existing driver.
My question is whether the approach is acceptable. It adds
minimal overhead and shall be OK for the enable/disable path from
a technical POV. And it doesn't feel like too much of a stretch.
But there may be non-technical reasons to reject the approach.
I'd like to learn whether to follow that path before preparing
another version of the patch series.
The diffs were taken with the '-w -b' options to demonstrate
their essence and not drown it in whitespace changes. The
implementation assumes that the caller which registers the gate
(the platform's clock driver) provides both the counter cell and
the lock. And that all gates with a "shared use counter" use the
same lock (which is satisfied as they all get registered from the
same spot in the platform's clock driver).
The CLK_IGNORE_UNUSED flag addresses a different problem. The
SoC has four MSCAN components, while two of them are enabled in
the device tree (the other two are present but disabled). So
during probe two of the clocks get enabled. After probe all
unused clocks automatically get disabled (that's another two).
So the "shared use counter" drops to zero although components are
in use, because "disable, it's unused" isn't told from "disable
after enable, regular use". The flag would become obsolete if
the common gate logic would implement a separate disable_unused()
routine, but I guess this isn't necessary and the use of the flag
is appropriate.
That the example use creates a field for just one counter is to
better demonstrate the use and potential extension as need
arises. Reducing this to a mere integer variable would be a
micro optimization.
The extension of the existing clk_gate implementation:
@@ -123,6 +123,39 @@ static inline struct clk *mpc512x_clk_gated(reg,pos,0,&clklock);}+enummpc512x_clk_shared_gate_id_t{+MPC512x_CLK_SHARED_GATE_MSCAN,+MPC512x_CLK_SHARED_GATE_MAX,+};++staticintmpc512x_clk_gate_counters[MPC512x_CLK_SHARED_GATE_MAX];++/*+*implementor'snote:sinceclk_gateitemsdon'timplementaseparate+*.disable_unused()callback,their.disable()routinegetscalledand+*"disable the clock as we can't see it's in use"cannotbetoldfrom+*"regular disable, count these events please"+*+*passingtheCLK_IGNORE_UNUSEDflaguponclockcreationwillsuppress+*the"disable, unused"call,sousecountswon'tgetunbalanced,the+*clockeithernevergotenabledandthusneednotgetdisabled,or+*partofthehardwaregotenabledwhiledisablingtheotherpartisn't+*wanted+*/+staticinlinestructclk*mpc512x_clk_gated_shared(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos,+enummpc512x_clk_shared_gate_id_tshare_id)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+clkflags|=CLK_IGNORE_UNUSED;+returnclk_register_gate_shared(NULL,name,parent_name,clkflags,+reg,pos,0,&clklock,+&mpc512x_clk_gate_counters[share_id]);+}+staticinlinestructclk*mpc512x_clk_muxed(constchar*name,constchar**parent_names,intparent_count,u32__iomem*reg,u8pos,u8len)
Local tests have shown that the extension solves the problem of
how to satisfy the SoC's constraints on the MPC512x platform.
The MSCAN clocks no longer need to get pre-enabled, instead they
get setup and enabled only as the mscan(4) driver probes devices
according to how it was instructed (device tree nodes).
What do you think? Is the "shared gate" support in the common
logic appropriate? I'd rather not duplicate all of this code
just to introduce the specific gate I need, while most of the
logic is identical to the existing gate implementation. The
desire isn't to override the gate's operations, but to wrap them
and to consult a counter in addition, while the register access
still applies.
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Gerhard Sittig <hidden> Date: 2013-08-03 15:03:42
[ this is about the MPC512x platform's clock driver, the core of
the series; trim the CC: list and only keep ARM (for clock) and
PPC lists and people ]
On Fri, Aug 02, 2013 at 16:41 -0700, Mike Turquette wrote:
Quoting Gerhard Sittig (2013-07-22 05:14:44)
quoted
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 (which can
get referenced by means of symbolic identifiers from 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 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 in
parallel to the common clock implementation for test and comparison
during migration, a compile time option picks one of the two
alternatives (Kconfig switch, common clock used by default)
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)
- some help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
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
Signed-off-by: Gerhard Sittig <redacted>
Hi Gerhard,
This looks OK to me. Do you want me to take it or will you keep the
series together? Note that I took "clk: wrap I/O access for improved
portability" into the clk tree already.
Regards,
Mike
Thank you for the feedback and for the interest! It's nice to
hear that you like the central part of the series. :)
There will be another version of the series (v4), addressing the
remaining feedback, further reducing pre-enable workarounds,
eliminating the need for "shared gates" by adding more 'ipg'
clocks for serial communication (UART, SPI, CAN). It's true that
the series has widened its scope after initial submission, but it
as well has much improved from the feedback.
This specific patch introduces an alternative clock driver which
is enabled by default, so it depends on the device tree data
being available before the code becomes operational. And the
common clock platform driver might break those peripheral drivers
which haven't received their clock API use cleanup yet (earlier
parts of the series).
So I'd like to keep the series together until it has passed
review. I will make sure that the series remains bisectable and
always keeps working when applied in sequence, and would prefer
to only start splitting it or staging parts of it later if
needed.
@@ -1,7 +1,9 @@## Makefile for the Freescale PowerPC 512x linux kernel.#-obj-y+=clock.ompc512x_shared.o+obj-$(CONFIG_PPC_CLOCK)+=clock.o+obj-$(CONFIG_COMMON_CLK)+=clock-commonclk.o+obj-y+=mpc512x_shared.oobj-$(CONFIG_MPC5121_ADS)+=mpc5121_ads.ompc5121_ads_cpld.oobj-$(CONFIG_MPC512x_GENERIC)+=mpc512x_generic.oobj-$(CONFIG_PDM360NG)+=pdm360ng.o
@@ -0,0 +1,786 @@+/*+*Copyright(C)2013DENXSoftwareEngineering+*+*GerhardSittig,<gsi@denx.de>+*+*commonclockdriversupportfortheMPC512xplatform+*+*Thisisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublishedby+*theFreeSoftwareFoundation;eitherversion2oftheLicense,or+*(atyouroption)anylaterversion.+*/++#include<linux/clk-provider.h>+#include<linux/clkdev.h>+#include<linux/device.h>+#include<linux/errno.h>+#include<linux/io.h>+#include<linux/of.h>++#include<asm/mpc5121.h>+#include<dt-bindings/clock/mpc512x-clock.h>++#include"mpc512x.h" /* our public mpc5121_clk_init() API */++/* helpers to keep the MCLK intermediates "somewhere" in our table */+enum{+MCLK_IDX_MUX0,+MCLK_IDX_EN0,+MCLK_IDX_DIV0,+MCLK_IDX_MUX1,+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 */+staticstructclk*clks[MPC512x_CLK_LAST_PRIVATE];+staticstructclk_onecell_dataclk_data;++/* CCM register access */+staticstructmpc512x_ccm__iomem*clkregs;+staticDEFINE_SPINLOCK(clklock);++/* convenience wrappers around the common clk API */+staticinlinestructclk*mpc512x_clk_fixed(constchar*name,intrate)+{+returnclk_register_fixed_rate(NULL,name,NULL,CLK_IS_ROOT,rate);+}++staticinlinestructclk*mpc512x_clk_factor(+constchar*name,constchar*parent_name,+intmul,intdiv)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+returnclk_register_fixed_factor(NULL,name,parent_name,clkflags,+mul,div);+}++staticinlinestructclk*mpc512x_clk_divider(+constchar*name,constchar*parent_name,u8clkflags,+u32__iomem*reg,u8pos,u8len,intdivflags)+{+returnclk_register_divider(NULL,name,parent_name,clkflags,+reg,pos,len,divflags,&clklock);+}++staticinlinestructclk*mpc512x_clk_divtable(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos,u8len,+conststructclk_div_table*divtab)+{+u8divflags;++divflags=0;+returnclk_register_divider_table(NULL,name,parent_name,0,+reg,pos,len,divflags,+divtab,&clklock);+}++staticinlinestructclk*mpc512x_clk_gated(+constchar*name,constchar*parent_name,+u32__iomem*reg,u8pos)+{+intclkflags;++clkflags=CLK_SET_RATE_PARENT;+returnclk_register_gate(NULL,name,parent_name,clkflags,+reg,pos,0,&clklock);+}++staticinlinestructclk*mpc512x_clk_muxed(constchar*name,+constchar**parent_names,intparent_count,+u32__iomem*reg,u8pos,u8len)+{+intclkflags;+u8muxflags;++clkflags=CLK_SET_RATE_PARENT;+muxflags=0;+returnclk_register_mux(NULL,name,+parent_names,parent_count,clkflags,+reg,pos,len,muxflags,&clklock);+}++/* helper to isolate a bit field from a register */+staticinlineintget_bit_field(uint32_t__iomem*reg,uint8_tpos,uint8_tlen)+{+uint32_tval;++val=in_be32(reg);+val>>=pos;+val&=(1<<len)-1;+returnval;+}++/* get the SPMF and translate it into the "sys pll" multiplier */+staticintget_spmf_mult(void)+{+staticintspmf_to_mult[]={+68,1,12,16,20,24,28,32,+36,40,44,48,52,56,60,64,+};+intspmf;++spmf=get_bit_field(&clkregs->spmr,24,4);+returnspmf_to_mult[spmf];+}++/*+*gettheSYS_DIVvalueandtranslateitintoadividefactor+*+*valuesreturnedfromhereareamultipleoftherealfactorsincethe+*divideratioisfractional+*/+staticintget_sys_div_x2(void)+{+staticintsysdiv_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,+};+intdivcode;++divcode=get_bit_field(&clkregs->scfr2,26,6);+returnsysdiv_code_to_x2[divcode];+}++/*+*gettheCPMFvalueandtranslateitintoamultiplierfactor+*+*valuesreturnedfromhereareamultipleoftherealfactorsincethe+*multiplierratioisfractional+*/+staticintget_cpmf_mult_x2(void)+{+staticintcpmf_to_mult[]={+72,2,2,3,4,5,6,7,+};+intcpmf;++cpmf=get_bit_field(&clkregs->spmr,16,4);+returncpmf_to_mult[cpmf];+}++/*+*someoftheclockdividersdoscaleinalinearway,yetnotallof+*theirbitcombinationsarelegal;useadividertabletogeta+*resultingsetofapplicabledividervalues+*/++/* applies to the IPS_DIV, and PCI_DIV values */+staticstructclk_div_tabledivtab_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 */+staticstructclk_div_tabledivtab_1234[]={+{.val=1,.div=1,},+{.val=2,.div=2,},+{.val=3,.div=3,},+{.val=4,.div=4,},+{.div=0,},+};++staticintget_freq_from_dt(char*propname)+{+structdevice_node*np;+constunsignedint*prop;+intval;++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);+}+returnval;+}++staticvoidmpc512x_clk_preset_data(void)+{+size_ti;++for(i=0;i<ARRAY_SIZE(clks);i++)+clks[i]=ERR_PTR(-ENODEV);+}++/*+*-receivesthe"bus frequency"fromthecaller(that'stheIPSclock+*rate,thehistoricalsourceofclockinformation)+*-fetchesthesystemPLLmultiplieranddividervaluesaswellasthe+*IPSdividervaluefromhardware+*-determinestheREFclockrateeitherfromtheXTAL/OSCspec(if+*thereisadevicetreenodedescribingtheoscillator)orfromthe+*IPSbusclock(supportedforbackwardscompatibility,suchthat+*setupswithoutXTAL/OSCspecskeepworking)+*-createsthe"ref"clockitemintheclocktree,suchthat+*subsequentcodecancreatetheremainderofthehierarchy(REF->+*SYS->CSB->IPS)fromtheREFclockrateandthereturnedmul/div+*values+*/+staticvoidmpc512x_clk_setup_ref_clock(intbus_freq,+int*sys_mul,int*sys_div,int*ips_div)+{+structclk*osc_clk;+intcalc_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 node */+osc_clk=clk_get(NULL,"osc");+if(osc_clk){+/* descend REF directly from OSC, verify the IPS rate */+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{+/* calculate OSC rate and create REF from the freq value */+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);+}+}++/*+*helpercodefortheMCLKsubtreesetup+*+*theoverviewinsection5.2.4oftheMPC5121eReferenceManualrev4+*suggeststhatallinstancesofthe"PSC clock generation"areequal,+*andthatonemightre-usethePSCsetupforMSCANclockgeneration+*(section5.2.5)aswell,atleastthelogicifnotthedatafor+*description+*+*thedetails(startingatpage5-20)showdifferencesinthespecific+*inputsofthefirstmuxstage("can clk in","spdif tx"),andthe+*factualnon-availabilityofthesecondmuxstage(it'spresentyet+*onlyoneinputisvalid)+*+*theMSCANclockrelatedregisters(startingatpage5-35)all+*reference"spdif clk"atthefirstmuxstageanddon'tmentionany+*"can clk"atall,whichsomehowisunexpected+*+*TODOre-checkthedocument,andclarifywhethertheRMiscorrectin+*theovervieworinthedetails,andwhetherthedifferenceisa+*clipboardinducederrororresultsfromchiprevisions+*+*itturnsoutthattheRMrev4asof2012-06talksabout"can"forthe+*PSCswhileRMrev3asof2008-10talksabout"spdif",soIguessthat+*firstadocupdateisrequiredwhichbetterreflectsrealityinthe+*SoCbeforetheimplementationshouldfollowwhilenoquestionsremain+*/++/*+*notethatthisdeclarationraisesacheckpatchwarning,but+*it'stheverydatatypewhich<linux/clk-provider.h>expects,+*makingthisdeclarationpasscheckpatchwillbreakcompilation+*/+staticconstchar*parent_names_mux0[]={+"sys","ref","psc-mclk-in","spdif-tx",+};++enummclk_type{+MCLK_TYPE_PSC,+MCLK_TYPE_MSCAN,+MCLK_TYPE_SPDIF,+};++structmclk_setup_data{+enummclk_typetype;+intcomp_idx;+boolhas_mclk1;+intbit_sccr1,bit_sccr2;+constchar*name_mux0;+constchar*name_en0;+constchar*name_div0;+constchar*parent_names_mux1[2];+constchar*name_mux1;+constchar*name_mclk;+};++#define MCLK_SETUP_DATA_PSC(id) { \+MCLK_TYPE_PSC,id,\+0,27-id,-1,\+"psc"#id"-mux0",\+"psc"#id"-en0",\+"psc"#id"_mclk_div",\+{"psc"#id"_mclk_div","dummy",},\+"psc"#id"_mclk_out",\+"psc"#id"_mclk",\+}++#define MCLK_SETUP_DATA_MSCAN(id) { \+MCLK_TYPE_MSCAN,id,\+0,-1,25,\+"mscan"#id"-mux0",\+"mscan"#id"-en0",\+"mscan"#id"_mclk_div",\+{"mscan"#id"_mclk_div","dummy",},\+"mscan"#id"_mclk_out",\+"mscan"#id"_mclk",\+}++#define MCLK_SETUP_DATA_SPDIF { \+MCLK_TYPE_SPDIF,0,\+1,-1,23,\+"spdif-mux0",\+"spdif-en0",\+"spdif_mclk_div",\+{"spdif_mclk_div","spdif-rx",},\+"spdif_mclk_out",\+"spdif_mclk",\+}++staticstructmclk_setup_datamclk_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),+};++staticstructmclk_setup_datamclk_mscan_data[]={+MCLK_SETUP_DATA_MSCAN(0),+MCLK_SETUP_DATA_MSCAN(1),+MCLK_SETUP_DATA_MSCAN(2),+MCLK_SETUP_DATA_MSCAN(3),+};++staticstructmclk_setup_datamclk_spdif_data[]={+MCLK_SETUP_DATA_SPDIF,+};++/* setup the MCLK clock subtree of an individual PSC/MSCAN/SPDIF */+staticvoidmpc512x_clk_setup_mclk(structmclk_setup_data*entry)+{+size_tclks_idx_pub,clks_idx_int;+u32__iomem*mccr_reg;/* MCLK control register (mux, en, div) */+u32__iomem*sccr_reg;/* system clock control register (enable) */+intsccr_bit;+intdiv;++/* derive a few parameters from the component type and index */+switch(entry->type){+caseMCLK_TYPE_PSC:+clks_idx_pub=MPC512x_CLK_PSC0_MCLK+entry->comp_idx;+clks_idx_int=MPC512x_CLK_MCLKS_FIRST++(entry->comp_idx)*MCLK_MAX_IDX;+mccr_reg=&clkregs->psc_ccr[entry->comp_idx];+break;+caseMCLK_TYPE_MSCAN:+clks_idx_pub=MPC512x_CLK_MSCAN0_MCLK+entry->comp_idx;+clks_idx_int=MPC512x_CLK_MCLKS_FIRST++(NR_PSCS+entry->comp_idx)*MCLK_MAX_IDX;+mccr_reg=&clkregs->mscan_ccr[entry->comp_idx];+break;+caseMCLK_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;+}+if(entry->bit_sccr1>=0){+sccr_reg=&clkregs->sccr1;+sccr_bit=entry->bit_sccr1;+}elseif(entry->bit_sccr2>=0){+sccr_reg=&clkregs->sccr2;+sccr_bit=entry->bit_sccr2;+}else{+sccr_reg=NULL;+}++/*+*thiswasgrabbedfromthePPC_CLOCKimplementation,which+*enforcedaspecificMCLKdividerwhiletheclockwasgated+*duringsetup(that'sadocumentedhardwarerequirement)+*+*thePPC_CLOCKimplementationmightevenhaveviolatedthe+*"MCLK <= IPS"constraint,thefixeddividervalueof1+*resultsinadividerof2andthusMCLK=SYS/2whichequals+*CSBwhichisgreaterthanIPS;theserialportsetupmayhave+*adjustedthedividerwhichtheclocksetupmighthaveleftin+*anundesirablestate+*+*initialsetupis:+*-MCLK0fromSYS+*-MCLKDIVsuchtonotexceedtheIPSclock+*-MCLK0enabled+*-MCLK1fromMCLKDIV+*/+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));++/*+*createthe'structclk'itemsoftheMCLK'sclocksubtree+*+*notethatbydesignwealwayscreateallnodesandwon'ttake+*shortcutshere,because+*-the"internal"MCLK_DIVandMCLK_OUTsignalinturnare+*selectableinputstotheCFMwhilethosewho"actually use"+*thePSC/MSCAN/SPDIF(serialdriversetal)needtheMCLK+*fortheirbitrate+*-intheabsenceof"aliases"forclocksweneedtocreate+*individial'structclk'itemsforwhatevermightget+*referencedorlookedup,evenifseveralofthoseitemsare+*identicalfromthelogicalPOV(theirratevalue)+*-foreasierfuturemaintenanceandforbetterreflectionof+*theSoC'sdocumentation,itappearsappropriatetogenerate+*clockitemsevenforthosemuxerswhichactuallyareNOPs+*(thosewithtwoinputsofwhichoneisreserved)+*/+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_int+MCLK_IDX_MUX1]=mpc512x_clk_muxed(+entry->name_mux1,+&entry->parent_names_mux1[0],+ARRAY_SIZE(entry->parent_names_mux1),+mccr_reg,7,1);+}else{+clks[clks_idx_int+MCLK_IDX_MUX1]=mpc512x_clk_factor(+entry->name_mux1,entry->parent_names_mux1[0],+1,1);+}+if(sccr_reg){+clks[clks_idx_pub]=mpc512x_clk_gated(+entry->name_mclk,+entry->name_mux1,sccr_reg,sccr_bit);+}else{+clks[clks_idx_pub]=mpc512x_clk_factor(+entry->name_mclk,+entry->name_mux1,1,1);+}++/*+*withoutthis"clock device"registration,"simple"lookupsin+*theSPImasterinitializationandserialportsetupwillfail+*+*thosedriversneedtogetadjustedtolookuptheirrequired+*clocksfromdevicetreespecs,anddevicetreenodesneedto+*providetheclockspecs,beforethisclkdevregistration+*becomesobsolete+*/+clk_register_clkdev(clks[clks_idx_pub],entry->name_mclk,NULL);+}++staticvoidmpc512x_clk_setup_mclks(structmclk_setup_data*table,size_tcount)+{+while(count-->0)+mpc512x_clk_setup_mclk(table++);+}++staticvoidmpc512x_clk_setup_clock_tree(intbusfreq)+{+intsys_mul,sys_div,ips_div;+intmul,div;+intfreq;++/*+*TODO+*-considerwhethertohandleclockswhichhavebothgatesand+*dividersviaintermediatesorbymeansofcomposites+*-fractionaldividersappeartonotmapwelltocomposites+*sincetheycanbeseenasafixedmultiplierandan+*adjustabledivider,whilecompositescanonlycombineat+*mostoneofamux,div,andgateeachintoone'structclk'+*item+*-PSC/MSCAN/SPDIFclockgenerationOTOHalreadyisvery+*specificandcannotgetmappedtocomponsites(atleastnot+*asingleone,maybetwoofthem,butseethecommentabout+*"intermediates are referenced from elsewhere, too")+*-trimthelistofauto-enabledclocksafterdriversacquire+*themcorrectlyasneeded+*/++/* regardless of whether XTAL/OSC exists, have REF created */+mpc512x_clk_setup_ref_clock(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"wassetupfromdatawhichwas+*sampledfromtheresetconfigword,atthispointintimethe+*configurationcanbeconsideredfixedandreadonly(i.e.no+*longeradjustable,ornolongerinneedofadjustment),which+*iswhywedon'tregisteraPLLherebutassumefixedfactors+*/+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);+mpc512x_clk_setup_mclks(mclk_psc_data,ARRAY_SIZE(mclk_psc_data));+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);+mpc512x_clk_setup_mclks(mclk_mscan_data,ARRAY_SIZE(mclk_mscan_data));+clks[MPC512x_CLK_SDHC]=mpc512x_clk_gated("sdhc","sdhc-ug",+&clkregs->sccr2,24);+mpc512x_clk_setup_mclks(mclk_spdif_data,ARRAY_SIZE(mclk_spdif_data));+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);++/*+*externallyprovidedclocks(whenimplementedinhardware,+*devicetreemayspecifyvalueswhichotherwisewereunknown)+*/+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);++/* clkdev registration for compatibility reasons */+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);++pr_debug("clock tree setup complete\n");+freq=clk_get_rate(clks[MPC512x_CLK_E300]);+pr_debug("derived PPC freq [%d]\n",freq);+freq=clk_get_rate(clks[MPC512x_CLK_IPS]);+pr_debug("derived IPS freq [%d]\n",freq);+freq=clk_get_rate(clks[MPC512x_CLK_LPC]);+pr_debug("derived LPC freq [%d]\n",freq);++/* enable some of the clocks here unconditionally because ... */+pr_debug("automatically enabling some clocks\n");+/* some are essential yet never get claimed by any driver */+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 */+/* some are required yet no dependencies were declared */+clk_prepare_enable(clks[MPC512x_CLK_PSC_FIFO]);+/* some are not yet acquired by their respective drivers */+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]);+/*+*somehavetheirindividualclocksubtreewithseparateclock+*itemsandtheirindividualenablecounters,yetsharea+*commongate(refertothesameregisterlocation)whilethe+*commonclockdrivercodeisnotawareofthefactandthe+*platform'scodedoesn'tprovidespecificsupporteither+*+*whatmighthappenisthate.g.enablingtwoMSCANclockitems+*anddisablingoneofthemwilldisablethecommongateand+*thusbreaktheotherMSCANclockaswell+*/+clk_prepare_enable(clks[MPC512x_CLK_MSCAN0_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN1_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN2_MCLK]);+clk_prepare_enable(clks[MPC512x_CLK_MSCAN3_MCLK]);+}++/*+*registersthesetofpublicclocks(thoselistedinthedt-bindings/+*headerfile)forOFlookups,keepstheintermediatesprivatetous+*/+staticvoidmpc5121_clk_register_of_provider(structdevice_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);+}++int__initmpc5121_clk_init(void)+{+structdevice_node*clk_np;+intbusfreq;++/* 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();++/*+*havethedevicetreescannedfor"fixed-clock"nodes(which+*includestheoscillatornodeiftheboard'sDTprovidesone)+*/+of_clk_init(NULL);++/*+*addadummyclockforthosesituationswhereaclockspecis+*requiredyetnorealclockisinvolved+*/+clks[MPC512x_CLK_DUMMY]=mpc512x_clk_fixed("dummy",0);++/*+*havealltherealnodesintheclocktreepopulatedfromREF+*downtoallleaves,eitherstartingfromtheOSCnodeorfrom+*aREFrootthatwascreatedfromtheIPSbusclockinput+*/+busfreq=get_freq_from_dt("bus-frequency");+mpc512x_clk_setup_clock_tree(busfreq);++/* register as an OF clock provider */+mpc5121_clk_register_of_provider(clk_np);++return0;+}
From: Gerhard Sittig <hidden> Date: 2013-08-03 15:36:30
[ trimming the CC: list to device tree and ARM(clock) and PPC ]
On Fri, Aug 02, 2013 at 16:41 -0700, Mike Turquette wrote:
Quoting Gerhard Sittig (2013-07-22 05:14:45)
quoted
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
these specs map 'clock-names' encoded in drivers to their respective
'struct clk' items in the platform's clock driver
Signed-off-by: Gerhard Sittig <redacted>
Reviewed-by: Mike Turquette <redacted>
Thank you for the review, Mike!
Please note that there will be another update for the series.
These clock specs for client side lookups will have additional
'ipg' clock items next to 'mclk' for the PSC (UART, SPI) and
MSCAN (CAN) nodes. But this change is straight forward.
From: Gerhard Sittig <hidden> Date: 2013-08-03 17:07:38
[ trimming the CC: list a bit, as this is CAN and clock specific,
keeping Mark Brown and Greg KH for the UART and SPI part ]
On Tue, Jul 23, 2013 at 14:33 +0200, Marc Kleine-Budde wrote:
On 07/23/2013 01:53 PM, Gerhard Sittig wrote:
quoted
On Mon, Jul 22, 2013 at 14:31 +0200, Marc Kleine-Budde wrote:
quoted
On 07/22/2013 02:14 PM, Gerhard Sittig wrote:
quoted
the .get_clock() callback is run from probe() and might allocate
resources, introduce a .put_clock() callback that is run from remove()
to undo any allocation activities
looks good
quoted
use devm_get_clk() upon lookup (for SYS and REF) to have the clocks put
upon driver unload
fine
quoted
assume that resources get prepared but not necessarily enabled in the
setup phase, make the open() and close() callbacks of the CAN network
device enable and disable a previously acquired and prepared clock
I think you should call prepare_enable and disable_unprepare in the
open/close functions.
After more local research, which totally eliminated the need to
pre-enable the CAN related clocks, but might need more discussion
as it touches the common gate support, I've learned something
more:
The CAN clock needs to get enabled during probe() already, since
registers get accessed between probe() for the driver and open()
for the network device -- while access to peripheral registers
crashes the kernel when clocks still are disabled (other hardware
may just hang or provide fake data, neither of this is OK).
Then call prepare_enable(); before and disable_unprepare(); after
accessing the registers. Have a look at the flexcan driver.
OK, your feedback made me notice that I mentally have mixed
peripheral access clocks ('ipg') and bitrate clocks ('per') in
the past versions of the driver.
Fixing this, telling 'ipg' and 'per' apart, "in bypassing"
eliminates the need for "shared clock gates". Since the MCLK
subtree of the clock tree apply to both the CAN controller and
the PSC controller, I will have to adjust all of the following:
- the platform's clock driver, telling the gate for the registers
and the mux/div for the bitrate apart
- the CAN driver, acquiring both the 'ipg' clock item for
register access and the "can" clock for the bitrate, the latter
may get derived from either 'ips' or 'mclk', while 'mclk' may
be derived from either 'sys' or 'ref' (or 'ips' in this
hardware while the mscan(4) driver doesn't use this feature)
- the UART and SPI drivers, acquiring both the 'ipg' clock item
for register access and the 'mclk' item for the bitrate
This obsoletes the request for "shared gates" and eliminates
another pre-enable workaround in the clock driver backend.
It also is an improvement for the MPC512x platform, and remains
neutral to the MPC52xx platform. It's clearly desirable and
useful, and doesn't break anything. So I will do it.
[ the above applied to CAN, SPI, and UART; the remainder is
specific to CAN only ]
But I won't try to even further widen the scope of the series, I
won't try to address each and every potential for improvement
which drivers may have had for several years and which happened
to not have been addressed yet. This needs to stop at some
reasonable point. I'm not refusing to improve, but I'm asking to
check what is reasonable and what needs to get avoided.
I already introduced a bug in a recent version of the series
which went unnoticed during review (the unbalanced error path in
the network device open routine). I'd rather not mess with power
management aspects "in bypassing" in a driver that I'm unable to
test thoroughly. Not when I'm trying to work on something
totally different (introducing proper common clock support) and
try to minimize risk and avoid damage.
quoted
But I see the point in your suggestion to prepare _and_ enable
the clock during open() as well -- to have open() cope with
whatever probe() did, after all the driver is shared among
platforms, which may differ in what they do during probe().
If you enable a clock to access the registers before open() (and disable
it afterwards), it should not harm any architecture that doesn't need
this clock enabled.
You suggest to turn on the clock during initialization, and turn
it off until the network device actually gets used? I had a look
at the flexcan driver, saw that it used two clock items, as
outlined above for register access and for wired communication.
This is good. But I somehow doubt that the flexcan driver will
work if the ipg clock gets disabled (I assume it's a shared clock
that happens to remain enabled since others use it as well).
I'd rather not open that can of worms, too. My gut is telling me
that either the peripheral does weird things or will lose data
when its (register access) clock gets disabled. I won't try to
address power management and save/restore issues in that driver
now, and I won't try to hunt down and instrument any register
access in the shared code paths of a driver for multiple
platforms which is full of callbacks.
That's just out of the scope of the series. It may be desirable
to address this issue as well, but it shall be done in a separate
action, not now "in bypassing". Thank you for understanding. :)
And I do appreciate your feedback and desire for even better
drivers, just disagree on what to do now in this very moment.
What we already have is:
- probe() and remove() for the driver, calling into clock setup
and allocation and deallocation routines
- no allocation for MPC52xx and thus no deallocation, keeping the
status of how things used to be
- allocation of a "can" clock for MPC512x and the respective
deallocation
- open() and close() for the network device, which prepare/enable
and disable/unprepare the allocated clocks
What I will add is:
- allocation and release of both the 'ipg' and a "can" clock for
the MPC512x case
- handling of all allocated clocks in open() and close() (such
that no assumption is made what occurs at probe() and remove()
time)
- permanently enabled 'ipg' clock when allocated, such that the
driver may happily access the controller's registers and may
assume things remain there
- usually disabled 'can' clock, but enable/disable between open()
and close()
This shall result in:
- no change in behaviour for MPC52xx
- disabled clocks and hardware for MPC512x when CAN isn't probed
(not listed in the device tree, or disabled)
- enabled internal peripherals but wire disabled for MPC512x when
CAN is probed and attached but not in use
- only enabled wire related clock when the network device is open
and in actual use
The above goal of the next update in the series won't break any
operation of peripherals, will be a clear improvement in that the
driver finally does properly use the clock API, and will result
in an appropriate use of hardware. There may be potential to
conserve even more power, but it's not essential given the
previous status of the driver, and it's out of the scope for the
very series we are talking about.
quoted
So I will:
- make open() of the network device prepare _and_ enable the
clock for the peripheral (if acquired during probe())
good
quoted
- adjust open() because ATM it leaves the clock enabled when the
network device operation fails (the error path is incomplete in
v3)
yes, clock should be disabled if open() fails.
quoted
- make the MPC512x specific probe() time .get_clock() routine not
just prepare but enable the clock as well
If needed enable the clock, but disable after probe() has finished.
quoted
- and of course address all the shutdown counter parts of the
above setup paths
quoted
This results in:
- specific chip drivers only need to balance their private get
and put clock routines which are called from probe and remove,
common paths DTRT for all of them
Yes, but clock should not stay enabled between probe() and open().
For this one I offered the compromise of only enabling the "can"
clock during network device operation, but keeping the 'ipg'
clock active over the driver's complete attachment period. This
shall be acceptable. Anything else can be done later and
independently.
[...]
quoted
Removing unnecessary devm_put_clk() calls is orthogonal to that.
Putting these in isn't totally wrong (they won't harm, and they
do signal "visual balance" more clearly such that the next person
won't stop and wonder), but it's true that they are redundant.
"Trained persons" will wonder as much about their presence as
untrained persons wonder about their absence. :) Apparently I'm
not well trained yet.
The whole point about devm_* is to get rid of auto manually tear down
functions. So please remove all devm_put_clk() calls, as it will be
called automatically if a driver instance is removed.
Ah, yes, the devm_*() feedback was processed as well. Release
won't occur explicitly, as it's done implicitly by common code.
virtually yours
Gerhard Sittig
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de
From: Mark Rutland <mark.rutland@arm.com> Date: 2013-08-05 11:37:20
On Mon, Jul 22, 2013 at 01:14:44PM +0100, Gerhard Sittig wrote:
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 (which can
get referenced by means of symbolic identifiers from 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 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 in
parallel to the common clock implementation for test and comparison
during migration, a compile time option picks one of the two
alternatives (Kconfig switch, common clock used by default)
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)
- some help introduce support for and migrate to the common
infrastructure, while more appropriate support for specific hardware
constraints isn't available yet (remaining changes are strictly
internal to the clock driver and won't affect peripheral drivers)
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
Signed-off-by: Gerhard Sittig <redacted>
---
arch/powerpc/platforms/512x/Kconfig | 14 +-
arch/powerpc/platforms/512x/Makefile | 4 +-
arch/powerpc/platforms/512x/clock-commonclk.c | 786 +++++++++++++++++++++++++
include/linux/clk-provider.h | 16 +
4 files changed, 818 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/512x/clock-commonclk.c
[...]
+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;
+}
Can you not use of_property_read_u32 here rather than of_get_property?
Also, this seems rather unlike the common clock bindings method for
describing frequencies in the dt. Given there's nothing in mainline
using this yet, we can do it 'right' from the start.
[...]
+ /*
+ * 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);
Can we not just use fixed-clocks for these in the dt? It feels odd to
describe them in a compeltely differnet way in the dt, especially as
we'll have to maintain some backwards compatibility for a while...
I see for psc_mclk_in we assume a default value if not present. I'm not
sure how to handle that, but I assume there's some way of finding out if
we've already registered a clock output with the same name?
Thanks,
Mark.