Hi,
This series reworks the Atmel Timer counter Block drivers. Those blocks
each have 3 counters with 2 channels each and can be used for
multiple functions:
- timers
- PWMs
- Quadrature decoders
- Stepper motor counters
Up until now, each TCB was fully used by each driver, possibly wasting
counters/channels.
There is a second issue motivating that rework. Until now, the PIT is
still used to boot then later in the boot sequence, the clocksource is
switched to the TCB. This ends up not working well with preempt-rt
because on some SoCs, the PIT interrupt is shared with the DBGU uart.
When using preempt-rt the interrupt flags for the PIT and the DBGU end
up being incompatible.
The whole rework doesn't break the DT ABI for the clocksource as the old
driver is kept anyway for AVR32. However, I still took the time to
switch all the upstreamed board dts to the new bindings.
However, there is no other choice than breaking the mainly unused
pwm-atmel-tcb binding. Only the kizbox is actually using it.
I hope we could quickly come to an agreement on the new DT bindings and
include those changes in 4.8.
Regarding the tcbclksrc driver, I'm not completely happy with the
request_irq/free_irq thing but there is not much choice unless we want
to pass in the clksrc handler even when using the clkevt driver as they
may share the same interrupt.
Cc: Antoine Aubert <redacted>
Cc: Daniel Lezcano <redacted>
Cc: devicetree at vger.kernel.org
Cc: Douglas Gilbert <dgilbert@interlog.com>
Cc: Fabio Porcedda <redacted>
Cc: Gregory CLEMENT <redacted>
Cc: Gregory Hermant <redacted>
Cc: Joachim Eastwood <redacted>
Cc: linux-clk at vger.kernel.org
Cc: linux-pwm at vger.kernel.org
Cc: Marek Vasut <marex@denx.de>
Cc: Martin Reimann <redacted>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Rodolfo Giometti <redacted>
Cc: Sergio Tanzilli <redacted>
Cc: Stephen Boyd <redacted>
Cc: Thierry Reding <redacted>
Cc: Thomas Gleixner <redacted>
Cc: Tim Schendekehl <redacted>
Alexandre Belloni (47):
ARM: at91: Document new TCB bindings
ARM: dts: at91: at91rm9200: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91rm9200ek; use TCB0 as clocksource
ARM: dts: at91: mpa1600; use TCB0 as clocksource
ARM: dts: at91: at91sam9260: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: ethernut5: use TCB0 as clocksource
ARM: dts: at91: foxg20: use TCB0 as clocksource
ARM: dts: at91: animeo_ip: use TCB0 as clocksource
ARM: dts: at91: kizbox: use TCB0 as clocksource
ARM: dts: at91: at91sam9g20ek: use TCB0 as clocksource
ARM: dts: at91: ge863-pro3: use TCB0 as clocksource
ARM: dts: at91: at91sam9261: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91sam9261ek: use TCB0 as clocksource
ARM: dts: at91: at91sam9263: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91sam9263ek: use TCB0 as clocksource
ARM: dts: at91: calao: use TCB0 as clocksource
ARM: dts: at91: at91sam9g45: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91sam9m10g45ek: use TCB0 as clocksource
ARM: dts: at91: pm9g45: use TCB0 as clocksource
ARM: dts: at91: at91sam9rl: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91sam9rlek: use TCB0 as clocksource
ARM: dts: at91: at91sam9n12: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91sam9n12ek: use TCB0 as clocksource
ARM: dts: at91: at91sam9x5: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: at91sam9x5cm: use TCB0 as clocksource
ARM: dts: at91: acme/g25: use TCB0 as clocksource
ARM: dts: at91: cosino: use TCB0 as clocksource
ARM: dts: at91: kizboxmini: use TCB0 as clocksource
ARM: dts: at91: sama5d3: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: sama5d3xek; use TCB0 as clocksource
ARM: dts: at91: sama5d3 Xplained: use TCB0 as clocksource
ARM: dts: at91: kizbox2: use TCB0 as clocksource
ARM: dts: at91: sama5d4: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: sama5d4: Add TCB2
ARM: dts: at91: sama5d4ek: use TCB2 as clocksource
ARM: dts: at91: sama5d4 Xplained: use TCB2 as clocksource
ARM: dts: at91: ma5d4: use TCB2 as clocksource
ARM: dts: at91: vinco: use TCB2 as clocksource
ARM: dts: at91: sama5d2: TC blocks are also simple-mfd and syscon
devices
ARM: dts: at91: sama5d2 Xplained: use TCB0 as clocksource
clocksource/drivers: Add a new driver for the Atmel ARM TC blocks
clocksource/drivers: Add a clockevent driver for Atmel TC blocks
clocksource: atmel-pit: allow unselecting ATMEL_PIT
ARM: at91/defconfig: sama5: unselect ATMEL_PIT
ARM: at91/defconfig: at91_dt unselect ATMEL_PIT
PWM: atmel-tcb: switch to new binding
ARM: dts: at91: kizbox: switch to new pwm-atmel-tcb binding
Cyrille Pitchen (1):
clk: at91: replace usleep() by udelay() calls
.../devicetree/bindings/arm/atmel-at91.txt | 32 ---
.../devicetree/bindings/mfd/atmel-tcb.txt | 62 +++++
.../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 12 +-
arch/arm/boot/dts/animeo_ip.dts | 12 +
arch/arm/boot/dts/at91-ariag25.dts | 12 +
arch/arm/boot/dts/at91-ariettag25.dts | 12 +
arch/arm/boot/dts/at91-cosino.dtsi | 12 +
arch/arm/boot/dts/at91-foxg20.dts | 12 +
arch/arm/boot/dts/at91-kizbox.dts | 54 +++-
arch/arm/boot/dts/at91-kizbox2.dts | 12 +
arch/arm/boot/dts/at91-kizboxmini.dts | 12 +
arch/arm/boot/dts/at91-qil_a9260.dts | 12 +
arch/arm/boot/dts/at91-sama5d2_xplained.dts | 12 +
arch/arm/boot/dts/at91-sama5d3_xplained.dts | 12 +
arch/arm/boot/dts/at91-sama5d4_ma5d4.dtsi | 12 +
arch/arm/boot/dts/at91-sama5d4_xplained.dts | 12 +
arch/arm/boot/dts/at91-sama5d4ek.dts | 12 +
arch/arm/boot/dts/at91-vinco.dts | 12 +
arch/arm/boot/dts/at91rm9200.dtsi | 8 +-
arch/arm/boot/dts/at91rm9200ek.dts | 12 +
arch/arm/boot/dts/at91sam9260.dtsi | 8 +-
arch/arm/boot/dts/at91sam9261.dtsi | 4 +-
arch/arm/boot/dts/at91sam9261ek.dts | 12 +
arch/arm/boot/dts/at91sam9263.dtsi | 4 +-
arch/arm/boot/dts/at91sam9263ek.dts | 12 +
arch/arm/boot/dts/at91sam9g20ek_common.dtsi | 12 +
arch/arm/boot/dts/at91sam9g45.dtsi | 8 +-
arch/arm/boot/dts/at91sam9m10g45ek.dts | 12 +
arch/arm/boot/dts/at91sam9n12.dtsi | 8 +-
arch/arm/boot/dts/at91sam9n12ek.dts | 12 +
arch/arm/boot/dts/at91sam9rl.dtsi | 4 +-
arch/arm/boot/dts/at91sam9rlek.dts | 12 +
arch/arm/boot/dts/at91sam9x5.dtsi | 8 +-
arch/arm/boot/dts/at91sam9x5cm.dtsi | 12 +
arch/arm/boot/dts/ethernut5.dts | 12 +
arch/arm/boot/dts/ge863-pro3.dtsi | 12 +
arch/arm/boot/dts/mpa1600.dts | 12 +
arch/arm/boot/dts/pm9g45.dts | 12 +
arch/arm/boot/dts/sama5d2.dtsi | 8 +-
arch/arm/boot/dts/sama5d3.dtsi | 4 +-
arch/arm/boot/dts/sama5d3_tcb1.dtsi | 4 +-
arch/arm/boot/dts/sama5d3xcm.dtsi | 12 +
arch/arm/boot/dts/sama5d4.dtsi | 18 +-
arch/arm/boot/dts/tny_a9260_common.dtsi | 12 +
arch/arm/boot/dts/tny_a9263.dts | 12 +
arch/arm/boot/dts/usb_a9260_common.dtsi | 12 +
arch/arm/boot/dts/usb_a9263.dts | 12 +
arch/arm/configs/at91_dt_defconfig | 1 +
arch/arm/configs/sama5_defconfig | 1 +
drivers/clk/at91/clk-main.c | 2 +-
drivers/clk/at91/clk-slow.c | 6 +-
drivers/clocksource/Kconfig | 32 ++-
drivers/clocksource/Makefile | 4 +-
drivers/clocksource/timer-atmel-tcbclkevt.c | 220 +++++++++++++++
drivers/clocksource/timer-atmel-tcbclksrc.c | 305 +++++++++++++++++++++
drivers/pwm/Kconfig | 3 +-
drivers/pwm/pwm-atmel-tcb.c | 219 ++++++++-------
include/soc/at91/atmel_tcb.h | 220 +++++++++++++++
58 files changed, 1452 insertions(+), 179 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/atmel-tcb.txt
create mode 100644 drivers/clocksource/timer-atmel-tcbclkevt.c
create mode 100644 drivers/clocksource/timer-atmel-tcbclksrc.c
create mode 100644 include/soc/at91/atmel_tcb.h
--
2.8.1
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use
regmap instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91rm9200.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
The current binding for the TCB is not flexible enough for some use cases
and prevents proper utilization of all the channels.
Cc: Daniel Lezcano <redacted>
Cc: Thierry Reding <redacted>
Cc: linux-pwm at vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree at vger.kernel.org
Signed-off-by: Alexandre Belloni <redacted>
---
.../devicetree/bindings/arm/atmel-at91.txt | 32 -----------
.../devicetree/bindings/mfd/atmel-tcb.txt | 62 ++++++++++++++++++++++
.../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 12 +++--
3 files changed, 69 insertions(+), 37 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/atmel-tcb.txt
@@ -60,38 +60,6 @@ System Timer (ST) required properties: Its subnodes can be: - watchdog: compatible should be "atmel,at91rm9200-wdt"-TC/TCLIB Timer required properties:-- compatible: Should be "atmel,<chip>-tcb".- <chip> can be "at91rm9200" or "at91sam9x5"-- reg: Should contain registers location and length-- interrupts: Should contain all interrupts for the TC block- Note that you can specify several interrupt cells if the TC- block has one interrupt per channel.-- clock-names: tuple listing input clock names.- Required elements: "t0_clk", "slow_clk"- Optional elements: "t1_clk", "t2_clk"-- clocks: phandles to input clocks.--Examples:--One interrupt per TC block:- tcb0: timer at fff7c000 {- compatible = "atmel,at91rm9200-tcb";- reg = <0xfff7c000 0x100>;- interrupts = <18 4>;- clocks = <&tcb0_clk>;- clock-names = "t0_clk";- };--One interrupt per TC channel in a TC block:- tcb1: timer at fffdc000 {- compatible = "atmel,at91rm9200-tcb";- reg = <0xfffdc000 0x100>;- interrupts = <26 4 27 4 28 4>;- clocks = <&tcb1_clk>;- clock-names = "t0_clk";- };- RSTC Reset Controller required properties: - compatible: Should be "atmel,<chip>-rstc". <chip> can be "at91sam9260" or "at91sam9g45" or "sama5d3"
@@ -0,0 +1,62 @@+* Device tree bindings for Atmel Timer Counter Blocks+- compatible: Should be "atmel,<chip>-tcb", "simple-mfd", "syscon".+ <chip> can be "at91rm9200" or "at91sam9x5"+- reg: Should contain registers location and length+- #address-cells: has to be 1+- #size-cells: has to be 0+- interrupts: Should contain all interrupts for the TC block+ Note that you can specify several interrupt cells if the TC+ block has one interrupt per channel.+- clock-names: tuple listing input clock names.+ Required elements: "t0_clk", "slow_clk"+ Optional elements: "t1_clk", "t2_clk"+- clocks: phandles to input clocks.++The TCB can expose multiple subdevices:+ * a clocksource and clockevent device+ - compatible: Should be "atmel,tcb-clksrc"+ - reg: Should contain the TCB channels to be used. If the+ counter width is 16 bits (at91rm9200-tcb), two consecutive+ channels are needed. Else, only one channel will be used.++ * a clockevent device+ - compatible: Should be "atmel,tcb-clkevt"+ - reg: Should contain the TCB channel to be used++ * a PWM chip: see ../pwm/atmel-tcb-pwm.txt++Examples:++One interrupt per TC block:+ tcb0: timer at fff7c000 {+ compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";+ #address-cells = <1>;+ #size-cells = <0>;+ reg = <0xfff7c000 0x100>;+ interrupts = <18 4>;+ clocks = <&tcb0_clk>;+ clock-names = "t0_clk";++ timer at 0 {+ compatible = "atmel,tcb-clksrc";+ reg = <0>, <1>;+ };++ timer at 2 {+ compatible = "atmel,tcb-clkevt";+ reg = <2>;+ };+ };++One interrupt per TC channel in a TC block:+ tcb1: timer at fffdc000 {+ compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";+ #address-cells = <1>;+ #size-cells = <0>;+ reg = <0xfffdc000 0x100>;+ interrupts = <26 4>, <27 4>, <28 4>;+ clocks = <&tcb1_clk>;+ clock-names = "t0_clk";+ };++
@@ -2,15 +2,17 @@ Atmel TCB PWM controller Required properties: - compatible: should be "atmel,tcb-pwm"+- reg: tcb channel to use. Each channel can export 2 PWMs - #pwm-cells: should be 3. See pwm.txt in this directory for a description of the cells format. The only third cell flag supported by this binding is PWM_POLARITY_INVERTED.-- tc-block: The Timer Counter block to use as a PWM chip. Example:-pwm {- compatible = "atmel,tcb-pwm";- #pwm-cells = <3>;- tc-block = <1>;+tcb0: timer at f800c000 {+ pwm at 0 {+ compatible = "atmel,tcb-pwm";+ reg = <0>;+ #pwm-cells = <3>;+ }; };
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use regmap
instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9263.dtsi | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use regmap
instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9rl.dtsi | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use
regmap instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/sama5d2.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
The PIT is not required anymore to successfully boot and may actually harm
in case preempt-rt is used because the PIT interrupt is shared.
Disable it so the TCB clocksource is used.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/configs/at91_dt_defconfig | 1 +
1 file changed, 1 insertion(+)
@@ -182,6 +182,7 @@ CONFIG_RTC_DRV_AT91RM9200=y CONFIG_RTC_DRV_AT91SAM9=y CONFIG_DMADEVICES=y CONFIG_AT_HDMAC=y+# CONFIG_ATMEL_PIT is not set # CONFIG_IOMMU_SUPPORT is not set CONFIG_IIO=y CONFIG_AT91_ADC=y
The PWM is now a subnode of the used TCB. This is cleaner and it mainly
allows to stop wasting TCB channels when only 4 PWMs are used.
Cc: Thierry Reding <redacted>
Cc: linux-pwm at vger.kernel.org
Signed-off-by: Alexandre Belloni <redacted>
---
drivers/pwm/Kconfig | 3 +-
drivers/pwm/pwm-atmel-tcb.c | 219 ++++++++++++++++++++++++--------------------
2 files changed, 120 insertions(+), 102 deletions(-)
@@ -152,35 +158,35 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)polarity=!polarity;spin_lock(&tcbpwmc->lock);-cmr=__raw_readl(regs+ATMEL_TC_REG(group,CMR));+regmap_read(tcbpwmc->regmap,ATMEL_TC_CMR(tcbpwmc->channel),&cmr);/* flush old setting and set the new one */-if(index==0){+if(pwm->hwpwm==0){cmr&=~ATMEL_TC_ACMR_MASK;if(polarity==PWM_POLARITY_INVERSED)-cmr|=ATMEL_TC_ASWTRG_CLEAR;+cmr|=ATMEL_TC_CMR_ASWTRG(CLEAR);else-cmr|=ATMEL_TC_ASWTRG_SET;+cmr|=ATMEL_TC_CMR_ASWTRG(SET);}else{cmr&=~ATMEL_TC_BCMR_MASK;if(polarity==PWM_POLARITY_INVERSED)-cmr|=ATMEL_TC_BSWTRG_CLEAR;+cmr|=ATMEL_TC_CMR_BSWTRG(CLEAR);else-cmr|=ATMEL_TC_BSWTRG_SET;+cmr|=ATMEL_TC_CMR_BSWTRG(SET);}-__raw_writel(cmr,regs+ATMEL_TC_REG(group,CMR));+regmap_write(tcbpwmc->regmap,ATMEL_TC_CMR(tcbpwmc->channel),cmr);/**Usesoftwaretriggertoapplythenewsetting.*IfbothPWMdevicesinthisgrouparedisabledwestoptheclock.*/-if(!(cmr&(ATMEL_TC_ACPC|ATMEL_TC_BCPC)))-__raw_writel(ATMEL_TC_SWTRG|ATMEL_TC_CLKDIS,-regs+ATMEL_TC_REG(group,CCR));+if(!(cmr&(ATMEL_TC_CMR_ACPC_MSK|ATMEL_TC_CMR_BCPC_MSK)))+regmap_write(tcbpwmc->regmap,ATMEL_TC_CCR(tcbpwmc->channel),+ATMEL_TC_CCR_SWTRG|ATMEL_TC_CCR_CLKDIS);else-__raw_writel(ATMEL_TC_SWTRG,regs+-ATMEL_TC_REG(group,CCR));+regmap_write(tcbpwmc->regmap,ATMEL_TC_CCR(tcbpwmc->channel),+ATMEL_TC_CCR_SWTRG);spin_unlock(&tcbpwmc->lock);}
@@ -208,25 +210,25 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)polarity=!polarity;spin_lock(&tcbpwmc->lock);-cmr=__raw_readl(regs+ATMEL_TC_REG(group,CMR));+regmap_read(tcbpwmc->regmap,ATMEL_TC_CMR(tcbpwmc->channel),&cmr);/* flush old setting and set the new one */-cmr&=~ATMEL_TC_TCCLKS;+cmr&=~ATMEL_TC_CMR_TCLKS_MSK;-if(index==0){+if(pwm->hwpwm==0){cmr&=~ATMEL_TC_ACMR_MASK;/* Set CMR flags according to given polarity */if(polarity==PWM_POLARITY_INVERSED)-cmr|=ATMEL_TC_ASWTRG_CLEAR;+cmr|=ATMEL_TC_CMR_ASWTRG(CLEAR);else-cmr|=ATMEL_TC_ASWTRG_SET;+cmr|=ATMEL_TC_CMR_ASWTRG(SET);}else{cmr&=~ATMEL_TC_BCMR_MASK;if(polarity==PWM_POLARITY_INVERSED)-cmr|=ATMEL_TC_BSWTRG_CLEAR;+cmr|=ATMEL_TC_CMR_BSWTRG(CLEAR);else-cmr|=ATMEL_TC_BSWTRG_SET;+cmr|=ATMEL_TC_CMR_BSWTRG(SET);}/*
@@ -236,33 +238,40 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)*thisconfigtillnextconfigcall.*/if(tcbpwm->duty!=tcbpwm->period&&tcbpwm->duty>0){-if(index==0){+if(pwm->hwpwm==0){if(polarity==PWM_POLARITY_INVERSED)-cmr|=ATMEL_TC_ACPA_SET|ATMEL_TC_ACPC_CLEAR;+cmr|=ATMEL_TC_CMR_ACPA(SET)|+ATMEL_TC_CMR_ACPC(CLEAR);else-cmr|=ATMEL_TC_ACPA_CLEAR|ATMEL_TC_ACPC_SET;+cmr|=ATMEL_TC_CMR_ACPA(CLEAR)|+ATMEL_TC_CMR_ACPC(SET);}else{if(polarity==PWM_POLARITY_INVERSED)-cmr|=ATMEL_TC_BCPB_SET|ATMEL_TC_BCPC_CLEAR;+cmr|=ATMEL_TC_CMR_BCPB(SET)|+ATMEL_TC_CMR_BCPC(CLEAR);else-cmr|=ATMEL_TC_BCPB_CLEAR|ATMEL_TC_BCPC_SET;+cmr|=ATMEL_TC_CMR_BCPB(CLEAR)|+ATMEL_TC_CMR_BCPC(SET);}}-cmr|=(tcbpwm->div&ATMEL_TC_TCCLKS);+cmr|=(tcbpwm->div&ATMEL_TC_CMR_TCLKS_MSK);-__raw_writel(cmr,regs+ATMEL_TC_REG(group,CMR));+regmap_write(tcbpwmc->regmap,ATMEL_TC_CMR(tcbpwmc->channel),cmr);-if(index==0)-__raw_writel(tcbpwm->duty,regs+ATMEL_TC_REG(group,RA));+if(pwm->hwpwm==0)+regmap_write(tcbpwmc->regmap,ATMEL_TC_RA(tcbpwmc->channel),+tcbpwm->duty);else-__raw_writel(tcbpwm->duty,regs+ATMEL_TC_REG(group,RB));+regmap_write(tcbpwmc->regmap,ATMEL_TC_RB(tcbpwmc->channel),+tcbpwm->duty);-__raw_writel(tcbpwm->period,regs+ATMEL_TC_REG(group,RC));+regmap_write(tcbpwmc->regmap,ATMEL_TC_RC(tcbpwmc->channel),+tcbpwm->period);/* Use software trigger to apply the new setting */-__raw_writel(ATMEL_TC_CLKEN|ATMEL_TC_SWTRG,-regs+ATMEL_TC_REG(group,CCR));+regmap_write(tcbpwmc->regmap,ATMEL_TC_CCR(tcbpwmc->channel),+ATMEL_TC_CCR_SWTRG|ATMEL_TC_CCR_CLKEN);spin_unlock(&tcbpwmc->lock);return0;}
@@ -305,9 +311,9 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,*/if(i==5){i=slowclk;-rate=clk_get_rate(tc->slow_clk);+rate=clk_get_rate(tcbpwmc->slow_clk);min=div_u64(NSEC_PER_SEC,rate);-max=min<<tc->tcb_config->counter_width;+max=min<<tcbpwmc->width;/* If period is too big return ERANGE error */if(max<period_ns)
@@ -365,31 +367,40 @@ static const struct pwm_ops atmel_tcb_pwm_ops = {staticintatmel_tcb_pwm_probe(structplatform_device*pdev){+conststructof_device_id*match;structatmel_tcb_pwm_chip*tcbpwm;structdevice_node*np=pdev->dev.of_node;-structatmel_tc*tc;+structregmap*regmap;+structclk*clk;+structclk*slow_clk;interr;-inttcblock;+intchannel;-err=of_property_read_u32(np,"tc-block",&tcblock);+err=of_property_read_u32(np,"reg",&channel);if(err<0){dev_err(&pdev->dev,-"failed to get Timer Counter Block number from device tree (error: %d)\n",+"failed to get Timer Counter Block channel from device tree (error: %d)\n",err);returnerr;}-tc=atmel_tc_alloc(tcblock);-if(tc==NULL){-dev_err(&pdev->dev,"failed to allocate Timer Counter Block\n");-return-ENOMEM;-}+regmap=syscon_node_to_regmap(np->parent);+if(IS_ERR(regmap))+returnPTR_ERR(regmap);++slow_clk=of_clk_get_by_name(np->parent,"slow_clk");+if(IS_ERR(slow_clk))+returnPTR_ERR(slow_clk);++clk=tcb_clk_get(np,channel);+if(IS_ERR(clk))+returnPTR_ERR(clk);tcbpwm=devm_kzalloc(&pdev->dev,sizeof(*tcbpwm),GFP_KERNEL);if(tcbpwm==NULL){err=-ENOMEM;dev_err(&pdev->dev,"failed to allocate memory\n");-gotoerr_free_tc;+gotoerr_slow_clk;}tcbpwm->chip.dev=&pdev->dev;
@@ -398,11 +409,17 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)tcbpwm->chip.of_pwm_n_cells=3;tcbpwm->chip.base=-1;tcbpwm->chip.npwm=NPWM;-tcbpwm->tc=tc;+tcbpwm->channel=channel;+tcbpwm->regmap=regmap;+tcbpwm->clk=clk;+tcbpwm->slow_clk=slow_clk;-err=clk_prepare_enable(tc->slow_clk);+match=of_match_node(atmel_tcb_dt_ids,np->parent);+tcbpwm->width=(int)match->data;++err=clk_prepare_enable(slow_clk);if(err)-gotoerr_free_tc;+gotoerr_slow_clk;spin_lock_init(&tcbpwm->lock);
@@ -415,10 +432,10 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)return0;err_disable_clk:-clk_disable_unprepare(tcbpwm->tc->slow_clk);+clk_disable_unprepare(tcbpwm->slow_clk);-err_free_tc:-atmel_tc_free(tc);+err_slow_clk:+clk_put(slow_clk);returnerr;}
@@ -428,14 +445,14 @@ static int atmel_tcb_pwm_remove(struct platform_device *pdev)structatmel_tcb_pwm_chip*tcbpwm=platform_get_drvdata(pdev);interr;-clk_disable_unprepare(tcbpwm->tc->slow_clk);+clk_disable_unprepare(tcbpwm->slow_clk);+clk_put(tcbpwm->slow_clk);+clk_put(tcbpwm->clk);err=pwmchip_remove(&tcbpwm->chip);if(err<0)returnerr;-atmel_tc_free(tcbpwm->tc);-return0;}
The PIT is not required anymore to successfully boot and may actually harm
in case preempt-rt is used because the PIT interrupt is shared.
Disable it so the TCB clocksource is used.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/configs/sama5_defconfig | 1 +
1 file changed, 1 insertion(+)
@@ -192,6 +192,7 @@ CONFIG_RTC_DRV_AT91RM9200=y CONFIG_DMADEVICES=y CONFIG_AT_HDMAC=y CONFIG_AT_XDMAC=y+# CONFIG_ATMEL_PIT is not set # CONFIG_IOMMU_SUPPORT is not set CONFIG_IIO=y CONFIG_AT91_ADC=y
With the new TCB clocksource driver, atmel platforms are now able to boot
without the PIT driver. Allow unselecting it.
Cc: Daniel Lezcano <redacted>
Cc: Thomas Gleixner <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
drivers/clocksource/Kconfig | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
@@ -0,0 +1,220 @@+#include<linux/clk.h>+#include<linux/clockchips.h>+#include<linux/interrupt.h>+#include<linux/kernel.h>+#include<linux/mfd/syscon.h>+#include<linux/module.h>+#include<linux/of_irq.h>+#include<linux/platform_device.h>+#include<linux/regmap.h>+#include<soc/at91/atmel_tcb.h>++structtc_clkevt_device{+structclock_event_deviceclkevt;+structregmap*regmap;+structclk*slow_clk;+structclk*clk;+intchannel;+intirq;+};++staticstructtc_clkevt_device*to_tc_clkevt(structclock_event_device*clkevt)+{+returncontainer_of(clkevt,structtc_clkevt_device,clkevt);+}++staticinttc_shutdown(structclock_event_device*d)+{+structtc_clkevt_device*tcd=to_tc_clkevt(d);++regmap_write(tcd->regmap,ATMEL_TC_IDR(tcd->channel),0xff);+regmap_write(tcd->regmap,ATMEL_TC_CCR(tcd->channel),+ATMEL_TC_CCR_CLKDIS);+if(!clockevent_state_detached(d))+clk_disable(tcd->clk);++return0;+}++/* For now, we always use the 32K clock ... this optimizes for NO_HZ,+*becauseusingoneofthedividedclockswouldusuallymeanthe+*tickratecanneverbelessthanseveraldozenHz(vs0.5Hz).+*+*Adividedclockcouldbegoodforhighresolutiontimers,since+*30.5usecresolutioncanseem"low".+*/+staticinttc_set_oneshot(structclock_event_device*d)+{+structtc_clkevt_device*tcd=to_tc_clkevt(d);++if(clockevent_state_oneshot(d)||clockevent_state_periodic(d))+tc_shutdown(d);++clk_enable(tcd->clk);++/* slow clock, count up to RC, then irq and stop */+regmap_write(tcd->regmap,ATMEL_TC_CMR(tcd->channel),+ATMEL_TC_CMR_TCLK(4)|ATMEL_TC_CMR_CPCSTOP|+ATMEL_TC_CMR_WAVE|ATMEL_TC_CMR_WAVESEL_UPRC);+regmap_write(tcd->regmap,ATMEL_TC_IER(tcd->channel),+ATMEL_TC_CPCS);++return0;+}++staticinttc_set_periodic(structclock_event_device*d)+{+structtc_clkevt_device*tcd=to_tc_clkevt(d);++if(clockevent_state_oneshot(d)||clockevent_state_periodic(d))+tc_shutdown(d);++/* By not making the gentime core emulate periodic mode on top+*ofoneshot,wegetloweroverheadandimprovedaccuracy.+*/+clk_enable(tcd->clk);++/* slow clock, count up to RC, then irq and restart */+regmap_write(tcd->regmap,ATMEL_TC_CMR(tcd->channel),+ATMEL_TC_CMR_TCLK(4)|ATMEL_TC_CMR_WAVE|+ATMEL_TC_CMR_WAVESEL_UPRC);+regmap_write(tcd->regmap,ATMEL_TC_RC(tcd->channel),+(32768+HZ/2)/HZ);++/* Enable clock and interrupts on RC compare */+regmap_write(tcd->regmap,ATMEL_TC_IER(tcd->channel),ATMEL_TC_CPCS);+regmap_write(tcd->regmap,ATMEL_TC_CCR(tcd->channel),+ATMEL_TC_CCR_CLKEN|ATMEL_TC_CCR_SWTRG);++return0;+}++staticinttc_next_event(unsignedlongdelta,structclock_event_device*d)+{+structtc_clkevt_device*tcd=to_tc_clkevt(d);++regmap_write(tcd->regmap,ATMEL_TC_RC(tcd->channel),delta);+regmap_write(tcd->regmap,ATMEL_TC_CCR(tcd->channel),+ATMEL_TC_CCR_CLKEN|ATMEL_TC_CCR_SWTRG);++return0;+}++staticstructtc_clkevt_deviceclkevt={+.clkevt={+.features=CLOCK_EVT_FEAT_PERIODIC|+CLOCK_EVT_FEAT_ONESHOT,+/* Should be lower than at91rm9200's system timer */+.rating=140,+.set_next_event=tc_next_event,+.set_state_shutdown=tc_shutdown,+.set_state_periodic=tc_set_periodic,+.set_state_oneshot=tc_set_oneshot,+},+};++staticirqreturn_ttc_clkevt_irq(intirq,void*handle)+{+structtc_clkevt_device*tcd=handle;+unsignedintsr;++regmap_read(tcd->regmap,ATMEL_TC_SR(tcd->channel),&sr);+if(sr&ATMEL_TC_CPCS){+tcd->clkevt.event_handler(&tcd->clkevt);+returnIRQ_HANDLED;+}++returnIRQ_NONE;+}++staticinttcb_clkevt_probe(structplatform_device*pdev)+{+structtc_clkevt_device*tcd=&clkevt;+intret;+structdevice_node*node=pdev->dev.of_node;++ret=of_property_read_u32_index(node,"reg",0,&tcd->channel);+if(ret)+returnret;++tcd->irq=tcb_irq_get(node,tcd->channel);+if(tcd->irq<0)+returntcd->irq;++tcd->regmap=syscon_node_to_regmap(node->parent);+if(IS_ERR(tcd->regmap))+returnPTR_ERR(tcd->regmap);++tcd->slow_clk=of_clk_get_by_name(node->parent,"slow_clk");+if(IS_ERR(tcd->slow_clk))+returnPTR_ERR(tcd->slow_clk);++ret=clk_prepare_enable(tcd->slow_clk);+if(ret)+returnret;++tcd->clk=tcb_clk_get(node,tcd->channel);+if(IS_ERR(tcd->clk)){+ret=PTR_ERR(tcd->clk);+gotoerr_slow;+}++clkevt.clkevt.name=dev_name(&pdev->dev);++/* try to enable clk to avoid future errors in mode change */+ret=clk_prepare_enable(tcd->clk);+if(ret)+gotoerr_slow;++clk_disable(tcd->clk);++clkevt.clkevt.cpumask=cpumask_of(0);++clockevents_config_and_register(&clkevt.clkevt,32768,1,0xffff);++ret=request_irq(tcd->irq,tc_clkevt_irq,IRQF_TIMER|IRQF_SHARED,+clkevt.clkevt.name,&clkevt);+if(ret)+gotoerr_clk;++return0;++err_clk:+clk_unprepare(tcd->clk);+err_slow:+clk_disable_unprepare(tcd->slow_clk);++returnret;+}++staticconststructof_device_idatmel_tcb_clkevt_dt_ids[]={+{.compatible="atmel,tcb-clkevt"},+{/* sentinel */}+};+MODULE_DEVICE_TABLE(of,atmel_tcb_clkevt_dt_ids);++staticstructplatform_drivertcb_clkevt_driver={+.probe=tcb_clkevt_probe,+.driver={+.name="atmel_tcb_clkevt",+.of_match_table=of_match_ptr(atmel_tcb_clkevt_dt_ids),+},+};++staticint__initatmel_tcb_clkevt_init(void)+{+returnplatform_driver_register(&tcb_clkevt_driver);+}++staticvoid__exitatmel_tcb_clkevt_exit(void)+{+platform_driver_unregister(&tcb_clkevt_driver);+}++early_platform_init("earlytimer",&tcb_clkevt_driver);+subsys_initcall(atmel_tcb_clkevt_init);+module_exit(atmel_tcb_clkevt_exit);++MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");+MODULE_DESCRIPTION("Clockevents driver for Atmel Timer Counter Blocks");+MODULE_LICENSE("GPL v2");
Add a driver for the Atmel Timer Counter Blocks. This driver provides a
clocksource and a clockevent device. The clockevent device is linked to the
clocksource counter and so it will run at the same frequency.
This driver uses regmap and syscon to be able to probe early in the boot
and avoid having to switch on the TCB clocksource later. Using regmap also
means that unused TCB channels may be used by other drivers (PWM for
example).
Cc: Daniel Lezcano <redacted>
Cc: Thomas Gleixner <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
drivers/clocksource/Kconfig | 13 ++
drivers/clocksource/Makefile | 3 +-
drivers/clocksource/timer-atmel-tcbclksrc.c | 305 ++++++++++++++++++++++++++++
include/soc/at91/atmel_tcb.h | 220 ++++++++++++++++++++
4 files changed, 540 insertions(+), 1 deletion(-)
create mode 100644 drivers/clocksource/timer-atmel-tcbclksrc.c
create mode 100644 include/soc/at91/atmel_tcb.h
@@ -0,0 +1,305 @@+#include<linux/clk.h>+#include<linux/clockchips.h>+#include<linux/clocksource.h>+#include<linux/interrupt.h>+#include<linux/kernel.h>+#include<linux/mfd/syscon.h>+#include<linux/of_irq.h>+#include<linux/regmap.h>+#include<linux/sched_clock.h>+#include<soc/at91/atmel_tcb.h>++structatmel_tcb_clksrc{+structclocksourceclksrc;+structclock_event_deviceclkevt;+structregmap*regmap;+structclk*clk[2];+intchannels[2];+u8bits;+unsignedintirq;+boolregistered;+boolirq_requested;+};++staticstructatmel_tcb_clksrctc={+.clksrc={+.name="tcb_clksrc",+.rating=200,+.mask=CLOCKSOURCE_MASK(32),+.flags=CLOCK_SOURCE_IS_CONTINUOUS,+},+.clkevt={+.name="tcb_clkevt",+.features=CLOCK_EVT_FEAT_ONESHOT,+/* Should be lower than at91rm9200's system timer */+.rating=125,+},+};++staticcycle_ttc_get_cycles(structclocksource*cs)+{+unsignedlongflags;+u32lower,upper,tmp;++raw_local_irq_save(flags);+do{+regmap_read(tc.regmap,ATMEL_TC_CV(1),&upper);+regmap_read(tc.regmap,ATMEL_TC_CV(0),&lower);+regmap_read(tc.regmap,ATMEL_TC_CV(1),&tmp);+}while(upper!=tmp);++raw_local_irq_restore(flags);+return(upper<<16)|lower;+}++staticcycle_ttc_get_cycles32(structclocksource*cs)+{+u32val;++regmap_read(tc.regmap,ATMEL_TC_CV(tc.channels[0]),&val);++returnval;+}++staticu64tc_sched_clock_read(void)+{+returntc_get_cycles(&tc.clksrc);+}++staticu64tc_sched_clock_read32(void)+{+returntc_get_cycles32(&tc.clksrc);+}++staticinttcb_clkevt_next_event(unsignedlongdelta,+structclock_event_device*d)+{+u32val;++regmap_read(tc.regmap,ATMEL_TC_CV(tc.channels[0]),&val);+regmap_write(tc.regmap,ATMEL_TC_RC(tc.channels[0]),val+delta);+regmap_write(tc.regmap,ATMEL_TC_IER(tc.channels[0]),ATMEL_TC_CPCS);++return0;+}++staticirqreturn_ttc_clkevt_irq(intirq,void*handle)+{+unsignedintsr;++regmap_read(tc.regmap,ATMEL_TC_SR(tc.channels[0]),&sr);+if(sr&ATMEL_TC_CPCS){+tc.clkevt.event_handler(&tc.clkevt);+returnIRQ_HANDLED;+}++returnIRQ_NONE;+}++staticinttcb_clkevt_oneshot(structclock_event_device*dev)+{+intret;++if(tc.irq_requested)+return0;++ret=request_irq(tc.irq,tc_clkevt_irq,IRQF_TIMER|IRQF_SHARED,+"tcb_clkevt",&tc);+if(!ret)+tc.irq_requested=true;++returnret;+}++staticinttcb_clkevt_shutdown(structclock_event_device*dev)+{+regmap_write(tc.regmap,ATMEL_TC_IDR(tc.channels[0]),0xff);+if(tc.bits==16)+regmap_write(tc.regmap,ATMEL_TC_IDR(tc.channels[1]),0xff);++if(tc.irq_requested){+free_irq(tc.irq,&tc);+tc.irq_requested=false;+}++return0;+}++staticvoid__inittcb_setup_dual_chan(structatmel_tcb_clksrc*tc,+intmck_divisor_idx)+{+/* first channel: waveform mode, input mclk/8, clock TIOA on overflow */+regmap_write(tc->regmap,ATMEL_TC_CMR(tc->channels[0]),+mck_divisor_idx/* likely divide-by-8 */+|ATMEL_TC_CMR_WAVE+|ATMEL_TC_CMR_WAVESEL_UP/* free-run */+|ATMEL_TC_CMR_ACPA(SET)/* TIOA rises at 0 */+|ATMEL_TC_CMR_ACPC(CLEAR));/* (duty cycle 50%) */+regmap_write(tc->regmap,ATMEL_TC_RA(tc->channels[0]),0x0000);+regmap_write(tc->regmap,ATMEL_TC_RC(tc->channels[0]),0x8000);+regmap_write(tc->regmap,ATMEL_TC_IDR(tc->channels[0]),0xff);/* no irqs */+regmap_write(tc->regmap,ATMEL_TC_CCR(tc->channels[0]),+ATMEL_TC_CCR_CLKEN);++/* second channel: waveform mode, input TIOA */+regmap_write(tc->regmap,ATMEL_TC_CMR(tc->channels[1]),+ATMEL_TC_CMR_XC(tc->channels[1])/* input: TIOA */+|ATMEL_TC_CMR_WAVE+|ATMEL_TC_CMR_WAVESEL_UP);/* free-run */+regmap_write(tc->regmap,ATMEL_TC_IDR(tc->channels[1]),0xff);/* no irqs */+regmap_write(tc->regmap,ATMEL_TC_CCR(tc->channels[1]),+ATMEL_TC_CCR_CLKEN);++/* chain both channel, we assume the previous channel */+regmap_write(tc->regmap,ATMEL_TC_BMR,+ATMEL_TC_BMR_TCXC(1+tc->channels[1],tc->channels[1]));+/* then reset all the timers */+regmap_write(tc->regmap,ATMEL_TC_BCR,ATMEL_TC_BCR_SYNC);+}++staticvoid__inittcb_setup_single_chan(structatmel_tcb_clksrc*tc,+intmck_divisor_idx)+{+/* channel 0: waveform mode, input mclk/8 */+regmap_write(tc->regmap,ATMEL_TC_CMR(tc->channels[0]),+mck_divisor_idx/* likely divide-by-8 */+|ATMEL_TC_CMR_WAVE+|ATMEL_TC_CMR_WAVESEL_UP/* free-run */+);+regmap_write(tc->regmap,ATMEL_TC_IDR(tc->channels[0]),0xff);/* no irqs */+regmap_write(tc->regmap,ATMEL_TC_CCR(tc->channels[0]),+ATMEL_TC_CCR_CLKEN);++/* then reset all the timers */+regmap_write(tc->regmap,ATMEL_TC_BCR,ATMEL_TC_BCR_SYNC);+}++staticvoid__inittcb_clksrc_init(structdevice_node*node)+{+conststructof_device_id*match;+u32rate,divided_rate=0;+intbest_divisor_idx=-1;+inti,err;++if(tc.registered)+return;++tc.regmap=syscon_node_to_regmap(node->parent);+if(IS_ERR(tc.regmap))+return;++match=of_match_node(atmel_tcb_dt_ids,node->parent);+tc.bits=(int)match->data;++err=of_property_read_u32_index(node,"reg",0,&tc.channels[0]);+if(err)+return;++tc.channels[1]=-1;++if(tc.bits==16){+of_property_read_u32_index(node,"reg",1,&tc.channels[1]);+if(tc.channels[1]==-1){+pr_err("%s: clocksource needs two channels\n",+node->parent->full_name);+}+}++tc.irq=tcb_irq_get(node,tc.channels[0]);+if(tc.irq<0)+return;++tc.clk[0]=tcb_clk_get(node,tc.channels[0]);+if(IS_ERR(tc.clk[0]))+return;+err=clk_prepare_enable(tc.clk[0]);+if(err){+pr_debug("can't enable T0 clk\n");+gotoerr_clk;+}++if(tc.bits==16){+tc.clk[1]=tcb_clk_get(node,tc.channels[1]);+if(IS_ERR(tc.clk[1]))+gotoerr_disable_t0;+}++/* How fast will we be counting? Pick something over 5 MHz. */+rate=(u32)clk_get_rate(tc.clk[0]);+for(i=0;i<5;i++){+unsignedintdivisor=atmel_tc_divisors[i];+unsignedinttmp;++if(!divisor)+continue;++tmp=rate/divisor;+pr_debug("TC: %u / %-3u [%d] --> %u\n",rate,divisor,i,tmp);+if(best_divisor_idx>0){+if(tmp<5*1000*1000)+continue;+}+divided_rate=tmp;+best_divisor_idx=i;+}++pr_debug("%s: %s@%d.%03d MHz\n",tc.clksrc.name,+node->parent->full_name,divided_rate/1000000,+((divided_rate+500000)%1000000)/1000);++if(tc.bits==32){+tc.clksrc.read=tc_get_cycles32;+tcb_setup_single_chan(&tc,best_divisor_idx);+}else{+err=clk_prepare_enable(tc.clk[1]);+if(err){+pr_debug("can't enable T1 clk\n");+gotoerr_clk1;+}+tc.clksrc.read=tc_get_cycles,+tcb_setup_dual_chan(&tc,best_divisor_idx);+}++err=clocksource_register_hz(&tc.clksrc,divided_rate);+if(err)+gotoerr_disable_t1;++if(tc.bits==32)+sched_clock_register(tc_sched_clock_read32,32,divided_rate);+else+sched_clock_register(tc_sched_clock_read,32,divided_rate);++tc.registered=true;++/* Set up and register clockevents */+tc.clkevt.cpumask=cpumask_of(0);+tc.clkevt.set_next_event=tcb_clkevt_next_event;+tc.clkevt.set_state_oneshot=tcb_clkevt_oneshot;+tc.clkevt.set_state_shutdown=tcb_clkevt_shutdown;+if(tc.bits==16)+clockevents_config_and_register(&tc.clkevt,divided_rate,1,+0xffff);+else+clockevents_config_and_register(&tc.clkevt,divided_rate,1,+0xffffffff);+return;++err_disable_t1:+if(tc.bits==16)+clk_disable_unprepare(tc.clk[1]);++err_clk1:+if(tc.bits==16)+clk_put(tc.clk[1]);++err_disable_t0:+clk_disable_unprepare(tc.clk[0]);++err_clk:+clk_put(tc.clk[0]);++pr_err("%s: unable to register clocksource/clockevent\n",+tc.clksrc.name);+}+CLOCKSOURCE_OF_DECLARE(atmel_tcb_clksrc,"atmel,tcb-clksrc",+tcb_clksrc_init);
As TCB2 doesn't have any output pins, use it as a clocksource.
Cc: Gregory CLEMENT <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-vinco.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
As TCB2 doesn't have any output pins, use it as a clocksource.
Cc: Marek Vasut <marex@denx.de>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-sama5d4_ma5d4.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
As TCB2 doesn't have any output pins, use it as a clocksource.
Tested-by: Alexandre Belloni <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-sama5d4_xplained.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
As TCB2 doesn't have any output pins, use it as a clocksource.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-sama5d4ek.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
The third TC block is missing from the dtsi. It has no output pins but can
still be used as a clocksource/clockevents device as the IRQ is wired.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/sama5d4.dtsi | 10 ++++++++++
1 file changed, 10 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allow sto reuse
regmap instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/sama5d4.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
Use tcb0 as the clocksource like previously selected in sama5_defconfig.
Cc: Antoine Aubert <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-kizbox2.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in sama5_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/sama5d3xcm.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use
regmap instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/sama5d3.dtsi | 4 +++-
arch/arm/boot/dts/sama5d3_tcb1.dtsi | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Cc: Antoine Aubert <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-kizboxmini.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
[tested on at91sam9x25ek and at91sam9x35ek]
Tested-by: Alexandre Belloni <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9x5cm.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use regmap
instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9x5.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9n12ek.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use regmap
instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9n12.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9rlek.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/pm9g45.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use regmap
instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9g45.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9263ek.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in sama5_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9261ek.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use regmap
instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9261.dtsi | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9g20ek_common.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Cc: Antoine Aubert <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91-kizbox.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/animeo_ip.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Cc: Martin Reimann <redacted>
Cc: Tim Schendekehl <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/ethernut5.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
Add simple-mfd and syscon to the TC blocks to allow to register one of the
channels as clocksource properly at boot time. It also allows to use
regmap instead of tclib.
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/at91sam9260.dtsi | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
Use tcb0 as the clocksource like previously selected in at91_dt_defconfig.
Cc: Joachim Eastwood <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
arch/arm/boot/dts/mpa1600.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
On Saturday, June 11, 2016 12:03:04 AM CEST Alexandre Belloni wrote:
From: Cyrille Pitchen <redacted>
Fix the main and slow clock .prepare() implementations which used to call
usleep() when the scheduler wasn't ready yet.
Does this have to be called that early? It seems wasteful to always
call udelay() here, when these are functions that are normally
allowed to sleep.
Arnd
On 11/06/2016 at 00:30:36 +0200, Arnd Bergmann wrote :
On Saturday, June 11, 2016 12:03:04 AM CEST Alexandre Belloni wrote:
quoted
From: Cyrille Pitchen <redacted>
Fix the main and slow clock .prepare() implementations which used to call
usleep() when the scheduler wasn't ready yet.
Does this have to be called that early? It seems wasteful to always
call udelay() here, when these are functions that are normally
allowed to sleep.
Well, the main issue here is that we want to be able to prepare_enable
the slow clock before we are allowed to sleep.
Thinking about it when writing that, I realize that when I reworked my
series, I removed the dependency between the clocksource TCB and the
slow_clock. But I guess we still have one on the main clock. I'll check
whether this is still necessary.
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
Ok, I'm not sure what happened, this is now fixed in my branch.
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
On 11/06/2016 at 11:43:54 +0200, Thomas Gleixner wrote :
On Sat, 11 Jun 2016, Alexandre Belloni wrote:
quoted
With the new TCB clocksource driver, atmel platforms are now able to boot
without the PIT driver. Allow unselecting it.
Yay! Thanks for doing this!
Sure! Can you still check the request_irq/free_irq in
timer-atmel-tcbclksrc? I'm not sure whether this can work reliably or
only because we are before SYSTEM_STATE=RUNNING.
In the latter case, I have another way of doing that.
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
From: Boris Brezillon <hidden> Date: 2016-06-11 12:48:16
On Sat, 11 Jun 2016 00:03:45 +0200
Alexandre Belloni [off-list ref] wrote:
Add a driver for the Atmel Timer Counter Blocks. This driver provides a
clocksource and a clockevent device. The clockevent device is linked to the
clocksource counter and so it will run at the same frequency.
This driver uses regmap and syscon to be able to probe early in the boot
and avoid having to switch on the TCB clocksource later. Using regmap also
means that unused TCB channels may be used by other drivers (PWM for
example).
First of all, thanks for working on this
tcb/libtcb/tcb-clksource/tcb-pwm mess. It looks a lot cleaner after
your changes (both the DT representation and the code itself).
^ 3 here
Can you make that consistent?
Actually, I'm not a big fan of those tabs, but if you decide to use
tabs, use the same number of them everywhere.
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ /* Should be lower than at91rm9200's system timer */
+ .rating = 125,
+ },
+};
+
[...]
+
+static int tcb_clkevt_next_event(unsigned long delta,
+ struct clock_event_device *d)
+{
+ u32 val;
+
+ regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val);
+ regmap_write(tc.regmap, ATMEL_TC_RC(tc.channels[0]), val + delta);
+ regmap_write(tc.regmap, ATMEL_TC_IER(tc.channels[0]), ATMEL_TC_CPCS);
Hm, not sure this is 100% sure. What happens if by the time you write
TC_RC, the delta value has expired? This means you'll have to wait
another round before the TC engine generates the "RC reached" interrupt.
I know this is very unlikely, but should we take the risk?
The core seems to check the ->set_next_event() return value and tries to
adjust ->min_delta_ns if it returns an error, so maybe it's worth
testing if val + delta has already occurred just before enabling the
TC_CPCS interrupt, and if it's the case, return an -ETIME error.
Something like:
u32 val[2], next;
regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val[0]);
next = (val[0] + delta) & GENMASK(tc.bits - 1, 0);
regmap_write(tc.regmap, ATMEL_TC_RC(tc.channels[0]), next);
regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val[1]);
if ((next < val[0] && val[1] < val[0] && val[1] >= next) ||
(next > val[0] && (val[1] < val[0] || val[1] >= next))) {
/*
* Clear the CPCS bit in the status register to avoid
* generating a spurious interrupt next time a valid
* timer event is configured.
* FIXME: not sure it's safe, since it also clears the
* overflow status, but it seems this flag is not used
* by the driver anyway.
*/
regmap_read(tc.regmap, ATMEL_TC_SR, &val[0]);
return -ETIME;
}
regmap_write(tc.regmap, ATMEL_TC_IER(tc.channels[0]),
ATMEL_TC_CPCS);
Thomas, Daniel, what's your opinion?
On 11/06/2016 at 00:30:36 +0200, Arnd Bergmann wrote :
On Saturday, June 11, 2016 12:03:04 AM CEST Alexandre Belloni wrote:
quoted
From: Cyrille Pitchen <redacted>
Fix the main and slow clock .prepare() implementations which used to call
usleep() when the scheduler wasn't ready yet.
Does this have to be called that early? It seems wasteful to always
call udelay() here, when these are functions that are normally
allowed to sleep.
So I've tested it and something like that would work:
if (system_state < SYSTEM_RUNNING)
udelay(osc->startup_usec);
else
usleep_range(osc->startup_usec, osc->startup_usec + 1);
But I'm afraid it would be the first driver to actually do something
like that (however, it is already the only driver trying to sleep).
--
Alexandre Belloni, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
From: Boris Brezillon <hidden> Date: 2016-06-13 19:04:25
On Sat, 11 Jun 2016 12:53:51 +0200
Alexandre Belloni [off-list ref] wrote:
On 11/06/2016 at 11:43:54 +0200, Thomas Gleixner wrote :
quoted
On Sat, 11 Jun 2016, Alexandre Belloni wrote:
quoted
With the new TCB clocksource driver, atmel platforms are now able to boot
without the PIT driver. Allow unselecting it.
Yay! Thanks for doing this!
Sure! Can you still check the request_irq/free_irq in
timer-atmel-tcbclksrc? I'm not sure whether this can work reliably or
only because we are before SYSTEM_STATE=RUNNING.
In the latter case, I have another way of doing that.
Sorry to put it back on the table, but shouldn't we consider adding
these irq_disable/enable_action() helpers?
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
From: Thomas Gleixner <hidden> Date: 2016-06-13 19:16:50
On Mon, 13 Jun 2016, Boris Brezillon wrote:
On Sat, 11 Jun 2016 12:53:51 +0200
Alexandre Belloni [off-list ref] wrote:
quoted
On 11/06/2016 at 11:43:54 +0200, Thomas Gleixner wrote :
quoted
On Sat, 11 Jun 2016, Alexandre Belloni wrote:
quoted
With the new TCB clocksource driver, atmel platforms are now able to boot
without the PIT driver. Allow unselecting it.
Yay! Thanks for doing this!
Sure! Can you still check the request_irq/free_irq in
timer-atmel-tcbclksrc? I'm not sure whether this can work reliably or
only because we are before SYSTEM_STATE=RUNNING.
In the latter case, I have another way of doing that.
Sorry to put it back on the table, but shouldn't we consider adding
these irq_disable/enable_action() helpers?
On Monday, June 13, 2016 5:24:09 PM CEST Alexandre Belloni wrote:
On 11/06/2016 at 00:30:36 +0200, Arnd Bergmann wrote :
quoted
On Saturday, June 11, 2016 12:03:04 AM CEST Alexandre Belloni wrote:
quoted
From: Cyrille Pitchen <redacted>
Fix the main and slow clock .prepare() implementations which used to call
usleep() when the scheduler wasn't ready yet.
Does this have to be called that early? It seems wasteful to always
call udelay() here, when these are functions that are normally
allowed to sleep.
So I've tested it and something like that would work:
if (system_state < SYSTEM_RUNNING)
udelay(osc->startup_usec);
else
usleep_range(osc->startup_usec, osc->startup_usec + 1);
I think that's reasonable in this case.
But I'm afraid it would be the first driver to actually do something
like that (however, it is already the only driver trying to sleep).
We have a lot of drivers that call potentially sleeping function from
the .prepare callback, e.g. when they go to an i2c controller.
Arnd
From: Afzal Mohammed <hidden> Date: 2016-06-14 16:05:32
Hi,
On Mon, Jun 13, 2016 at 05:24:09PM +0200, Alexandre Belloni wrote:
On 11/06/2016 at 00:30:36 +0200, Arnd Bergmann wrote :
quoted
Does this have to be called that early? It seems wasteful to always
call udelay() here, when these are functions that are normally
allowed to sleep.
So I've tested it and something like that would work:
if (system_state < SYSTEM_RUNNING)
udelay(osc->startup_usec);
else
usleep_range(osc->startup_usec, osc->startup_usec + 1);
But I'm afraid it would be the first driver to actually do something
like that (however, it is already the only driver trying to sleep).
From: Boris Brezillon <hidden> Date: 2016-06-14 16:18:29
On Tue, 14 Jun 2016 21:35:17 +0530
Afzal Mohammed [off-list ref] wrote:
Hi,
On Mon, Jun 13, 2016 at 05:24:09PM +0200, Alexandre Belloni wrote:
quoted
On 11/06/2016 at 00:30:36 +0200, Arnd Bergmann wrote :
quoted
quoted
Does this have to be called that early? It seems wasteful to always
call udelay() here, when these are functions that are normally
allowed to sleep.
quoted
So I've tested it and something like that would work:
if (system_state < SYSTEM_RUNNING)
udelay(osc->startup_usec);
else
usleep_range(osc->startup_usec, osc->startup_usec + 1);
But I'm afraid it would be the first driver to actually do something
like that (however, it is already the only driver trying to sleep).
Oh, interesting. Definitely a better solution than this custom check.
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
From: Rob Herring <robh@kernel.org> Date: 2016-06-14 21:47:42
On Sat, Jun 11, 2016 at 12:03:05AM +0200, Alexandre Belloni wrote:
The current binding for the TCB is not flexible enough for some use cases
and prevents proper utilization of all the channels.
Cc: Daniel Lezcano <redacted>
Cc: Thierry Reding <redacted>
Cc: linux-pwm at vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree at vger.kernel.org
Signed-off-by: Alexandre Belloni <redacted>
---
.../devicetree/bindings/arm/atmel-at91.txt | 32 -----------
.../devicetree/bindings/mfd/atmel-tcb.txt | 62 ++++++++++++++++++++++
.../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 12 +++--
3 files changed, 69 insertions(+), 37 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/atmel-tcb.txt
@@ -0,0 +1,62 @@+* Device tree bindings for Atmel Timer Counter Blocks+- compatible: Should be "atmel,<chip>-tcb", "simple-mfd", "syscon".+ <chip> can be "at91rm9200" or "at91sam9x5"+- reg: Should contain registers location and length+- #address-cells: has to be 1+- #size-cells: has to be 0+- interrupts: Should contain all interrupts for the TC block+ Note that you can specify several interrupt cells if the TC+ block has one interrupt per channel.+- clock-names: tuple listing input clock names.+ Required elements: "t0_clk", "slow_clk"+ Optional elements: "t1_clk", "t2_clk"+- clocks: phandles to input clocks.
What is the order of clocks?
+
+The TCB can expose multiple subdevices:
+ * a clocksource and clockevent device
No. These compatible names are linuxisms. Describe features of the
timers to be able to select which timer to use if you need to pick
certain timers. For example, interrupt capability could be used to
select the clkevt.
+ - compatible: Should be "atmel,tcb-clksrc"
+ - reg: Should contain the TCB channels to be used. If the
+ counter width is 16 bits (at91rm9200-tcb), two consecutive
+ channels are needed. Else, only one channel will be used.
+
+ * a clockevent device
+ - compatible: Should be "atmel,tcb-clkevt"
+ - reg: Should contain the TCB channel to be used
+
+ * a PWM chip: see ../pwm/atmel-tcb-pwm.txt
+
+Examples:
+
+One interrupt per TC block:
+ tcb0: timer at fff7c000 {
+ compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xfff7c000 0x100>;
+ interrupts = <18 4>;
+ clocks = <&tcb0_clk>;
+ clock-names = "t0_clk";
@@ -2,15 +2,17 @@ Atmel TCB PWM controller Required properties: - compatible: should be "atmel,tcb-pwm"+- reg: tcb channel to use. Each channel can export 2 PWMs
Is there a difference in channels? If not, then this compatible should
go.
- #pwm-cells: should be 3. See pwm.txt in this directory for a description of
the cells format. The only third cell flag supported by this binding is
PWM_POLARITY_INVERTED.
-- tc-block: The Timer Counter block to use as a PWM chip.
Example:
-pwm {
- compatible = "atmel,tcb-pwm";
- #pwm-cells = <3>;
- tc-block = <1>;
+tcb0: timer at f800c000 {
+ pwm at 0 {
+ compatible = "atmel,tcb-pwm";
+ reg = <0>;
+ #pwm-cells = <3>;
+ };
};
--
2.8.1
From: Boris Brezillon <hidden> Date: 2016-06-15 07:30:11
On Tue, 14 Jun 2016 16:47:37 -0500
Rob Herring [off-list ref] wrote:
On Sat, Jun 11, 2016 at 12:03:05AM +0200, Alexandre Belloni wrote:
quoted
The current binding for the TCB is not flexible enough for some use cases
and prevents proper utilization of all the channels.
Cc: Daniel Lezcano <redacted>
Cc: Thierry Reding <redacted>
Cc: linux-pwm at vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree at vger.kernel.org
Signed-off-by: Alexandre Belloni <redacted>
---
.../devicetree/bindings/arm/atmel-at91.txt | 32 -----------
.../devicetree/bindings/mfd/atmel-tcb.txt | 62 ++++++++++++++++++++++
.../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 12 +++--
3 files changed, 69 insertions(+), 37 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/atmel-tcb.txt
@@ -0,0 +1,62 @@+* Device tree bindings for Atmel Timer Counter Blocks+- compatible: Should be "atmel,<chip>-tcb", "simple-mfd", "syscon".+ <chip> can be "at91rm9200" or "at91sam9x5"+- reg: Should contain registers location and length+- #address-cells: has to be 1+- #size-cells: has to be 0+- interrupts: Should contain all interrupts for the TC block+ Note that you can specify several interrupt cells if the TC+ block has one interrupt per channel.+- clock-names: tuple listing input clock names.+ Required elements: "t0_clk", "slow_clk"+ Optional elements: "t1_clk", "t2_clk"+- clocks: phandles to input clocks.
What is the order of clocks?
quoted
+
+The TCB can expose multiple subdevices:
+ * a clocksource and clockevent device
No. These compatible names are linuxisms. Describe features of the
timers to be able to select which timer to use if you need to pick
certain timers. For example, interrupt capability could be used to
select the clkevt.
Would 'atmel,tcb-free-running-timer' (to replace 'atmel,tcb-clksrc') and
'atmel,tcb-programmable-timer' (to replace 'atmel,tcb-clkevt') be
acceptable?
quoted
+ - compatible: Should be "atmel,tcb-clksrc"
+ - reg: Should contain the TCB channels to be used. If the
+ counter width is 16 bits (at91rm9200-tcb), two consecutive
+ channels are needed. Else, only one channel will be used.
+
+ * a clockevent device
+ - compatible: Should be "atmel,tcb-clkevt"
+ - reg: Should contain the TCB channel to be used
+
+ * a PWM chip: see ../pwm/atmel-tcb-pwm.txt
+
+Examples:
+
+One interrupt per TC block:
+ tcb0: timer at fff7c000 {
+ compatible = "atmel,at91rm9200-tcb", "simple-mfd", "syscon";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0xfff7c000 0x100>;
+ interrupts = <18 4>;
+ clocks = <&tcb0_clk>;
+ clock-names = "t0_clk";
@@ -2,15 +2,17 @@ Atmel TCB PWM controller Required properties: - compatible: should be "atmel,tcb-pwm"+- reg: tcb channel to use. Each channel can export 2 PWMs
Is there a difference in channels? If not, then this compatible should
go.
This one I don't understand.
The TCB (Timer Counter Block) is an MFD containing 3 Timer Counter
devices. Each of these devices (also called channels) can be assigned a
specific mode:
- timer mode (free-running of programmable)
- waveform generator mode (IOW, a PWM)
- capture mode (an IIO device, but we don't have any driver for that
right now)
So each sub-device of the TCB is represented as a sub-node with its own
compatible. Is there a problem with that?
quoted
- #pwm-cells: should be 3. See pwm.txt in this directory for a description of
the cells format. The only third cell flag supported by this binding is
PWM_POLARITY_INVERTED.
-- tc-block: The Timer Counter block to use as a PWM chip.
Example:
-pwm {
- compatible = "atmel,tcb-pwm";
- #pwm-cells = <3>;
- tc-block = <1>;
+tcb0: timer at f800c000 {
+ pwm at 0 {
+ compatible = "atmel,tcb-pwm";
+ reg = <0>;
+ #pwm-cells = <3>;
+ };
};
--
2.8.1
From: Boris Brezillon <hidden> Date: 2016-06-16 14:42:16
Hi Thomas,
On Mon, 13 Jun 2016 21:14:47 +0200 (CEST)
Thomas Gleixner [off-list ref] wrote:
On Mon, 13 Jun 2016, Boris Brezillon wrote:
quoted
On Sat, 11 Jun 2016 12:53:51 +0200
Alexandre Belloni [off-list ref] wrote:
quoted
On 11/06/2016 at 11:43:54 +0200, Thomas Gleixner wrote :
quoted
On Sat, 11 Jun 2016, Alexandre Belloni wrote:
quoted
With the new TCB clocksource driver, atmel platforms are now able to boot
without the PIT driver. Allow unselecting it.
Yay! Thanks for doing this!
Sure! Can you still check the request_irq/free_irq in
timer-atmel-tcbclksrc? I'm not sure whether this can work reliably or
only because we are before SYSTEM_STATE=RUNNING.
In the latter case, I have another way of doing that.
Sorry to put it back on the table, but shouldn't we consider adding
these irq_disable/enable_action() helpers?
I fear that's the only reliable way to do it.
Alexandre told me you had a better implementation (or idea, I don't
remember) than what was proposed here [1].
Do you plan to submit something, or should we work on it?
Regards,
Boris
[1]https://lkml.org/lkml/2015/8/3/330
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
From: Rob Herring <robh@kernel.org> Date: 2016-06-21 20:08:15
On Wed, Jun 15, 2016 at 09:29:55AM +0200, Boris Brezillon wrote:
On Tue, 14 Jun 2016 16:47:37 -0500
Rob Herring [off-list ref] wrote:
quoted
On Sat, Jun 11, 2016 at 12:03:05AM +0200, Alexandre Belloni wrote:
quoted
The current binding for the TCB is not flexible enough for some use cases
and prevents proper utilization of all the channels.
Cc: Daniel Lezcano <redacted>
Cc: Thierry Reding <redacted>
Cc: linux-pwm at vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree at vger.kernel.org
Signed-off-by: Alexandre Belloni <redacted>
---
.../devicetree/bindings/arm/atmel-at91.txt | 32 -----------
.../devicetree/bindings/mfd/atmel-tcb.txt | 62 ++++++++++++++++++++++
.../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 12 +++--
3 files changed, 69 insertions(+), 37 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/atmel-tcb.txt
@@ -0,0 +1,62 @@+* Device tree bindings for Atmel Timer Counter Blocks+- compatible: Should be "atmel,<chip>-tcb", "simple-mfd", "syscon".+ <chip> can be "at91rm9200" or "at91sam9x5"+- reg: Should contain registers location and length+- #address-cells: has to be 1+- #size-cells: has to be 0+- interrupts: Should contain all interrupts for the TC block+ Note that you can specify several interrupt cells if the TC+ block has one interrupt per channel.+- clock-names: tuple listing input clock names.+ Required elements: "t0_clk", "slow_clk"+ Optional elements: "t1_clk", "t2_clk"+- clocks: phandles to input clocks.
What is the order of clocks?
quoted
+
+The TCB can expose multiple subdevices:
+ * a clocksource and clockevent device
No. These compatible names are linuxisms. Describe features of the
timers to be able to select which timer to use if you need to pick
certain timers. For example, interrupt capability could be used to
select the clkevt.
Would 'atmel,tcb-free-running-timer' (to replace 'atmel,tcb-clksrc') and
'atmel,tcb-programmable-timer' (to replace 'atmel,tcb-clkevt') be
acceptable?
If that somehow matches the documentation or accurately describes the
h/w capabilities for them, then yes.
Rob
From: Rob Herring <robh@kernel.org> Date: 2016-06-21 20:44:41
On Wed, Jun 15, 2016 at 09:29:55AM +0200, Boris Brezillon wrote:
On Tue, 14 Jun 2016 16:47:37 -0500
Rob Herring [off-list ref] wrote:
quoted
On Sat, Jun 11, 2016 at 12:03:05AM +0200, Alexandre Belloni wrote:
quoted
The current binding for the TCB is not flexible enough for some use cases
and prevents proper utilization of all the channels.
Cc: Daniel Lezcano <redacted>
Cc: Thierry Reding <redacted>
Cc: linux-pwm at vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree at vger.kernel.org
Signed-off-by: Alexandre Belloni <redacted>
---
.../devicetree/bindings/arm/atmel-at91.txt | 32 -----------
.../devicetree/bindings/mfd/atmel-tcb.txt | 62 ++++++++++++++++++++++
.../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 12 +++--
3 files changed, 69 insertions(+), 37 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mfd/atmel-tcb.txt
@@ -2,15 +2,17 @@ Atmel TCB PWM controller Required properties: - compatible: should be "atmel,tcb-pwm"+- reg: tcb channel to use. Each channel can export 2 PWMs
Is there a difference in channels? If not, then this compatible should
go.
This one I don't understand.
The TCB (Timer Counter Block) is an MFD containing 3 Timer Counter
devices. Each of these devices (also called channels) can be assigned a
specific mode:
- timer mode (free-running of programmable)
- waveform generator mode (IOW, a PWM)
- capture mode (an IIO device, but we don't have any driver for that
right now)
So each sub-device of the TCB is represented as a sub-node with its own
compatible. Is there a problem with that?
Missed this in my first reply. I guess for purposes of referencing pwm
from other nodes this is okay.
Rob
From: Daniel Lezcano <hidden> Date: 2016-06-22 13:07:23
On 06/11/2016 02:48 PM, Boris Brezillon wrote:
[ ... ]
quoted
+static int tcb_clkevt_next_event(unsigned long delta,
+ struct clock_event_device *d)
+{
+ u32 val;
+
+ regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val);
+ regmap_write(tc.regmap, ATMEL_TC_RC(tc.channels[0]), val + delta);
+ regmap_write(tc.regmap, ATMEL_TC_IER(tc.channels[0]), ATMEL_TC_CPCS);
Hm, not sure this is 100% sure. What happens if by the time you write
TC_RC, the delta value has expired? This means you'll have to wait
another round before the TC engine generates the "RC reached" interrupt.
I know this is very unlikely, but should we take the risk?
The core seems to check the ->set_next_event() return value and tries to
adjust ->min_delta_ns if it returns an error, so maybe it's worth
testing if val + delta has already occurred just before enabling the
TC_CPCS interrupt, and if it's the case, return an -ETIME error.
Something like:
u32 val[2], next;
regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val[0]);
next = (val[0] + delta) & GENMASK(tc.bits - 1, 0);
regmap_write(tc.regmap, ATMEL_TC_RC(tc.channels[0]), next);
regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val[1]);
if ((next < val[0] && val[1] < val[0] && val[1] >= next) ||
(next > val[0] && (val[1] < val[0] || val[1] >= next))) {
/*
* Clear the CPCS bit in the status register to avoid
* generating a spurious interrupt next time a valid
* timer event is configured.
* FIXME: not sure it's safe, since it also clears the
* overflow status, but it seems this flag is not used
* by the driver anyway.
*/
regmap_read(tc.regmap, ATMEL_TC_SR, &val[0]);
return -ETIME;
}
regmap_write(tc.regmap, ATMEL_TC_IER(tc.channels[0]),
ATMEL_TC_CPCS);
Thomas, Daniel, what's your opinion?
From: Boris Brezillon <hidden> Date: 2016-06-22 13:24:34
On Wed, 22 Jun 2016 15:07:00 +0200
Daniel Lezcano [off-list ref] wrote:
On 06/11/2016 02:48 PM, Boris Brezillon wrote:
[ ... ]
quoted
quoted
+static int tcb_clkevt_next_event(unsigned long delta,
+ struct clock_event_device *d)
+{
+ u32 val;
+
+ regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val);
+ regmap_write(tc.regmap, ATMEL_TC_RC(tc.channels[0]), val + delta);
+ regmap_write(tc.regmap, ATMEL_TC_IER(tc.channels[0]), ATMEL_TC_CPCS);
Hm, not sure this is 100% sure. What happens if by the time you write
TC_RC, the delta value has expired? This means you'll have to wait
another round before the TC engine generates the "RC reached" interrupt.
I know this is very unlikely, but should we take the risk?
The core seems to check the ->set_next_event() return value and tries to
adjust ->min_delta_ns if it returns an error, so maybe it's worth
testing if val + delta has already occurred just before enabling the
TC_CPCS interrupt, and if it's the case, return an -ETIME error.
Something like:
u32 val[2], next;
regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val[0]);
next = (val[0] + delta) & GENMASK(tc.bits - 1, 0);
regmap_write(tc.regmap, ATMEL_TC_RC(tc.channels[0]), next);
regmap_read(tc.regmap, ATMEL_TC_CV(tc.channels[0]), &val[1]);
if ((next < val[0] && val[1] < val[0] && val[1] >= next) ||
(next > val[0] && (val[1] < val[0] || val[1] >= next))) {
/*
* Clear the CPCS bit in the status register to avoid
* generating a spurious interrupt next time a valid
* timer event is configured.
* FIXME: not sure it's safe, since it also clears the
* overflow status, but it seems this flag is not used
* by the driver anyway.
*/
regmap_read(tc.regmap, ATMEL_TC_SR, &val[0]);
return -ETIME;
}
regmap_write(tc.regmap, ATMEL_TC_IER(tc.channels[0]),
ATMEL_TC_CPCS);
Thomas, Daniel, what's your opinion?
Are you describing the same as commit
f9eccf24615672896dc13251410c3f2f33a14f95 ?
Pretty much, yes. Note that this is purely hypothetical in the TCB
case, but I fear people might experience this problem if they're trying
to configure tiny delay values.
From: Daniel Lezcano <hidden> Date: 2016-06-24 10:07:06
On 06/11/2016 12:03 AM, Alexandre Belloni wrote:
quoted hunk
Add a driver for the Atmel Timer Counter Blocks. This driver provides a
clocksource and a clockevent device. The clockevent device is linked to the
clocksource counter and so it will run at the same frequency.
This driver uses regmap and syscon to be able to probe early in the boot
and avoid having to switch on the TCB clocksource later. Using regmap also
means that unused TCB channels may be used by other drivers (PWM for
example).
Cc: Daniel Lezcano <redacted>
Cc: Thomas Gleixner <redacted>
Signed-off-by: Alexandre Belloni <redacted>
---
drivers/clocksource/Kconfig | 13 ++
drivers/clocksource/Makefile | 3 +-
drivers/clocksource/timer-atmel-tcbclksrc.c | 305 ++++++++++++++++++++++++++++
include/soc/at91/atmel_tcb.h | 220 ++++++++++++++++++++
4 files changed, 540 insertions(+), 1 deletion(-)
create mode 100644 drivers/clocksource/timer-atmel-tcbclksrc.c
create mode 100644 include/soc/at91/atmel_tcb.h
The Kconfig options are set now with the COMPILE_TEST option in order to
increase the compilation test coverage.
Please, add bool "TC Block Clocksource" if COMPILE_TEST, ...
... remove these dependencies and let the SoC's Kconfig to select the
timer like the other timers are.
quoted hunk
+ help
+ Select this to get a high precision clocksource based on a
+ TC block with a 5+ MHz base clock rate.
+ On platforms with 16-bit counters, two timer channels are combined
+ to make a single 32-bit timer.
+ It can also be used as a clock event device supporting oneshot mode.
+
config CLKSRC_METAG_GENERIC
def_bool y if METAG
help
The legacy driver checks clockevent_state_detached() and disables the clock.
Why is 'irq_requested' and request_irq/free_irq cleaner ?
Isn't there a configuration with the TCB register to disable the
clockevent only ?
+static void __init tcb_clksrc_init(struct device_node *node)
+{
+ const struct of_device_id *match;
+ u32 rate, divided_rate = 0;
+ int best_divisor_idx = -1;
+ int i, err;
+
+ if (tc.registered)
+ return;
That's getting annoying. It is not your fault but that's a repeating
pattern when multiple nodes are defined for a timer. Someday we will
have to sit down and think about that.
+ tc.regmap = syscon_node_to_regmap(node->parent);
+ if (IS_ERR(tc.regmap))
+ return;
+
+ match = of_match_node(atmel_tcb_dt_ids, node->parent);
+ tc.bits = (int)match->data;
+
+ err = of_property_read_u32_index(node, "reg", 0, &tc.channels[0]);
+ if (err)
+ return;
+
+ tc.channels[1] = -1;
+
+ if (tc.bits == 16) {
+ of_property_read_u32_index(node, "reg", 1, &tc.channels[1]);
+ if (tc.channels[1] == -1) {
+ pr_err("%s: clocksource needs two channels\n",
+ node->parent->full_name);
+ }
+ }
+
+ tc.irq = tcb_irq_get(node, tc.channels[0]);
+ if (tc.irq < 0)
+ return;
+
+ tc.clk[0] = tcb_clk_get(node, tc.channels[0]);
+ if (IS_ERR(tc.clk[0]))
+ return;
+ err = clk_prepare_enable(tc.clk[0]);
+ if (err) {
+ pr_debug("can't enable T0 clk\n");
+ goto err_clk;
+ }
+
+ if (tc.bits == 16) {
+ tc.clk[1] = tcb_clk_get(node, tc.channels[1]);
+ if (IS_ERR(tc.clk[1]))
+ goto err_disable_t0;
+ }
+
+ /* How fast will we be counting? Pick something over 5 MHz. */
+ rate = (u32)clk_get_rate(tc.clk[0]);
+ for (i = 0; i < 5; i++) {
+ unsigned int divisor = atmel_tc_divisors[i];
+ unsigned int tmp;
+
+ if (!divisor)
+ continue;
+
+ tmp = rate / divisor;
+ pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp);
+ if (best_divisor_idx > 0) {
+ if (tmp < 5 * 1000 * 1000)
+ continue;
+ }
+ divided_rate = tmp;
+ best_divisor_idx = i;
+ }
+
+ pr_debug("%s: %s at %d.%03d MHz\n", tc.clksrc.name,
+ node->parent->full_name, divided_rate / 1000000,
+ ((divided_rate + 500000) % 1000000) / 1000);
+
+ if (tc.bits == 32) {
+ tc.clksrc.read = tc_get_cycles32;
+ tcb_setup_single_chan(&tc, best_divisor_idx);
+ } else {
+ err = clk_prepare_enable(tc.clk[1]);
+ if (err) {
+ pr_debug("can't enable T1 clk\n");
+ goto err_clk1;
+ }
+ tc.clksrc.read = tc_get_cycles,
+ tcb_setup_dual_chan(&tc, best_divisor_idx);
+ }
+
+ err = clocksource_register_hz(&tc.clksrc, divided_rate);
+ if (err)
+ goto err_disable_t1;
+
+ if (tc.bits == 32)
Can't you can group the code under tc.bits == 16 and tc.bits == 32
instead of multiple check against the reg width ?
clockevents_config_and_register(&tc.clkevt, divided_rate, 1,
BIT(tc.bits) - 1);
IIRC, there is a macro somewhere doing BIT(n) - 1. CLOCKSOURCE_MASK does
it but it is weird to set a clockevent with a clocksource mask.
The Kconfig options are set now with the COMPILE_TEST option in order to
increase the compilation test coverage.
Please, add bool "TC Block Clocksource" if COMPILE_TEST, ...
... remove these dependencies and let the SoC's Kconfig to select the timer
like the other timers are.
The main issue with what you suggest is that it removes the possibility
to not compile the driver. This may be interesting for people using the
PIT as the clocksource/clockevent and the TCBs for something else.
I've taken that from the old driver, I'm not sure this is needed. Maybe
to lower the chance to have upper != tmp.
quoted
+ return (upper << 16) | lower;
+}
+
quoted
+static int tcb_clkevt_oneshot(struct clock_event_device *dev)
+{
+ int ret;
+
+ if (tc.irq_requested)
+ return 0;
+
+ ret = request_irq(tc.irq, tc_clkevt_irq, IRQF_TIMER | IRQF_SHARED,
+ "tcb_clkevt", &tc);
+ if (!ret)
+ tc.irq_requested = true;
The legacy driver checks clockevent_state_detached() and disables the clock.
This feature is different from the legacy driver. Here, the driver is
using a single TCB channel for both clocksource and clockevent while the
legacy driver always uses at least two channels.
Why is 'irq_requested' and request_irq/free_irq cleaner ?
Isn't there a configuration with the TCB register to disable the clockevent
only ?
I'll try something cleaner anyway.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
The Kconfig options are set now with the COMPILE_TEST option in order to
increase the compilation test coverage.
Please, add bool "TC Block Clocksource" if COMPILE_TEST, ...
... remove these dependencies and let the SoC's Kconfig to select the timer
like the other timers are.
The main issue with what you suggest is that it removes the possibility
to not compile the driver. This may be interesting for people using the
PIT as the clocksource/clockevent and the TCBs for something else.
Is it really a problem? We tend as much as possible to make silent
options in order to let the platform config to select the right clock.
I've taken that from the old driver, I'm not sure this is needed. Maybe
to lower the chance to have upper != tmp.
quoted
quoted
+ return (upper << 16) | lower;
+}
+
I don't think this is needed.
quoted
quoted
+static int tcb_clkevt_oneshot(struct clock_event_device *dev)
+{
+ int ret;
+
+ if (tc.irq_requested)
+ return 0;
+
+ ret = request_irq(tc.irq, tc_clkevt_irq, IRQF_TIMER | IRQF_SHARED,
+ "tcb_clkevt", &tc);
+ if (!ret)
+ tc.irq_requested = true;
The legacy driver checks clockevent_state_detached() and disables the clock.
This feature is different from the legacy driver. Here, the driver is
using a single TCB channel for both clocksource and clockevent while the
legacy driver always uses at least two channels.
quoted
Why is 'irq_requested' and request_irq/free_irq cleaner ?
Isn't there a configuration with the TCB register to disable the clockevent
only ?
The Kconfig options are set now with the COMPILE_TEST option in order to
increase the compilation test coverage.
Please, add bool "TC Block Clocksource" if COMPILE_TEST, ...
... remove these dependencies and let the SoC's Kconfig to select the timer
like the other timers are.
The main issue with what you suggest is that it removes the possibility
to not compile the driver. This may be interesting for people using the
PIT as the clocksource/clockevent and the TCBs for something else.
Is it really a problem? We tend as much as possible to make silent
options in order to let the platform config to select the right clock.
I depends on how much you care about being able to remove as much as
possible from the kernel.
Both the PIT and the TCB can be used by AT91SAM9 and SAMA5, similarly,
both the system timer and the TCB can be used by AT91RM9200. I would
prefer letting the user select which drivers have to be compiled in the
kernel instead of pulling both with the SoC configuration.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com