Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 3/3] ARM: da850: fix da850_set_pll0rate()
From: Sekhar Nori @ 2016-12-05  8:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480693134-31324-4-git-send-email-bgolaszewski@baylibre.com>

On Friday 02 December 2016 09:08 PM, Bartosz Golaszewski wrote:
> This function is broken - its second argument is an index to the freq
> table, not the requested clock rate in Hz. It leads to an oops when
> called from clk_set_rate() since this argument isn't bounds checked
> either.
> 
> Fix it by iterating over the array of supported frequencies and
> selecting a one that matches or returning -EINVAL for unsupported
> rates.
> 
> Also: update the davinci cpufreq driver. It's the only user of this
> clock and currently it passes the cpufreq table index to
> clk_set_rate(), which is confusing. Make it pass the requested clock
> rate in Hz.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> ---
>  arch/arm/mach-davinci/da850.c     | 22 ++++++++++++++++++----
>  drivers/cpufreq/davinci-cpufreq.c |  2 +-
>  2 files changed, 19 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
> index a55101c..92e3303 100644
> --- a/arch/arm/mach-davinci/da850.c
> +++ b/arch/arm/mach-davinci/da850.c
> @@ -1179,14 +1179,28 @@ static int da850_set_armrate(struct clk *clk, unsigned long index)
>  	return clk_set_rate(pllclk, index);
>  }
>  
> -static int da850_set_pll0rate(struct clk *clk, unsigned long index)
> +static int da850_set_pll0rate(struct clk *clk, unsigned long rate)
>  {
> -	unsigned int prediv, mult, postdiv;
> -	struct da850_opp *opp;
>  	struct pll_data *pll = clk->pll_data;
> +	struct cpufreq_frequency_table *freq;
> +	unsigned int prediv, mult, postdiv;
> +	struct da850_opp *opp = NULL;
>  	int ret;
>  
> -	opp = (struct da850_opp *) cpufreq_info.freq_table[index].driver_data;
> +	for (freq = da850_freq_table;
> +	     freq->frequency != CPUFREQ_TABLE_END; freq++) {
> +		/* requested_rate is in Hz, freq->frequency is in KHz */
> +		unsigned long freq_rate = freq->frequency * 1000;

A small optimization here. Instead of multiplying potentially every
frequency in the table by 1000, you could divide the incoming rate down
to KHz. This will also avoid the need for 'freq_rate'. Should have
noticed this earlier. Sorry about that.

Thanks,
Sekhar

^ permalink raw reply

* [PATCH 2/2] arm: dts: sun8i: reuse the uart1 node of iNet D978 rev2 board
From: Maxime Ripard @ 2016-12-05  8:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161202151913.38892-2-icenowy@aosc.xyz>

On Fri, Dec 02, 2016 at 11:19:13PM +0800, Icenowy Zheng wrote:
> As a uart1 node is added into sun8i-reference-design-tablet.dtsi, simply
> use it in iNet D978 rev2 device tree.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>

I'd like to see more consolidation before that change is needed. If we
find more boards using that, it will make sense, but for a single
board it's not worth it.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/4aa6d997/attachment.sig>

^ permalink raw reply

* [PATCH v1 1/2] Add crypto driver support for some MediaTek chips
From: Corentin Labbe @ 2016-12-05  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480921284-45827-2-git-send-email-ryder.lee@mediatek.com>

Hello

I have two minor comment.

On Mon, Dec 05, 2016 at 03:01:23PM +0800, Ryder Lee wrote:
> This adds support for the MediaTek hardware accelerator on
> mt7623/mt2701/mt8521p SoC.
> 
> This driver currently implement:
> - SHA1 and SHA2 family(HMAC) hash alogrithms.

There is a typo for algorithms.

[...]
> +/**
> + * struct mtk_desc - DMA descriptor
> + * @hdr:	the descriptor control header
> + * @buf:	DMA address of input buffer segment
> + * @ct:		DMA address of command token that control operation flow
> + * @ct_hdr:	the command token control header
> + * @tag:	the user-defined field
> + * @tfm:	DMA address of transform state
> + * @bound:	align descriptors offset boundary
> + *
> + * Structure passed to the crypto engine to describe where source
> + * data needs to be fetched and how it needs to be processed.
> + */
> +struct mtk_desc {
> +	u32 hdr;
> +	u32 buf;
> +	u32 ct;
> +	u32 ct_hdr;
> +	u32 tag;
> +	u32 tfm;
> +	u32 bound[2];
> +};

Do you have tested this descriptor with BE/LE kernel ?

Regards
Corentin Labbe

^ permalink raw reply

* [PATCH v2 2/3] ARM: dts: sunxi: add support for Orange Pi Zero board
From: Maxime Ripard @ 2016-12-05  8:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <11498641480688550@web2g.yandex.ru>

On Fri, Dec 02, 2016 at 10:22:30PM +0800, Icenowy Zheng wrote:
> 
> 
> 01.12.2016, 17:36, "Maxime Ripard" <maxime.ripard@free-electrons.com>:
> > On Mon, Nov 28, 2016 at 12:29:07AM +0000, Andr? Przywara wrote:
> >> ?> Something more interesting happened.
> >> ?>
> >> ?> Xunlong made a add-on board for Orange Pi Zero, which exposes the
> >> ?> two USB Controllers exported at expansion bus as USB Type-A
> >> ?> connectors.
> >> ?>
> >> ?> Also it exposes a analog A/V jack and a microphone.
> >> ?>
> >> ?> Should I enable {e,o}hci{2.3} in the device tree?
> >>
> >> ?Actually we should do this regardless of this extension board. The USB
> >> ?pins are not multiplexed and are exposed on user accessible pins (just
> >> ?not soldered, but that's a detail), so I think they qualify for DT
> >> ?enablement. And even if a user can't use them, it doesn't hurt to have
> >> ?them (since they are not multiplexed).
> >
> > My main concern about this is that we'll leave regulators enabled by
> > default, for a minority of users. And that minority will prevent to do
> > a proper power management when the times come since we'll have to keep
> > that behaviour forever.
> 
> I think these users can add a 'fdt set /xxx/xxx status "disabled" ' .

You can't ask that from the majority of users. These users will take
debian or fedora, install it, and expect everything to work
properly. I would make the opposite argument actually. If someone is
knowledgeable enough to solder the USB pins a connector, then (s)he'll
be able to make that u-boot call.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/41c61333/attachment.sig>

^ permalink raw reply

* [PATCH v2] arm64: dts: zx: add pcu_domain node for zx296718
From: Shawn Guo @ 2016-12-05  8:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480925871-20855-1-git-send-email-baoyou.xie@linaro.org>

On Mon, Dec 05, 2016 at 04:17:51PM +0800, Baoyou Xie wrote:
> This patch adds the pcu_domain node, so it can be used
> by zte-soc's power domain driver.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>

I'm fine with the patch itself, but I need to wait the driver and
bindings being accepted to apply it.

Shawn

> ---
>  arch/arm64/boot/dts/zte/zx296718.dtsi | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/zte/zx296718.dtsi b/arch/arm64/boot/dts/zte/zx296718.dtsi
> index b44d1d1..1aa0587 100644
> --- a/arch/arm64/boot/dts/zte/zx296718.dtsi
> +++ b/arch/arm64/boot/dts/zte/zx296718.dtsi
> @@ -302,6 +302,11 @@
>  			reg = <0x116000 0x1000>;
>  		};
>  
> +		pcu_domain: pcu at 117000 {
> +			compatible = "zte,zx296718-pcu";
> +			reg = <0x00117000 0x1000>;
> +		};
> +
>  		uart0: uart at 11f000 {
>  			compatible = "arm,pl011", "arm,primecell";
>  			arm,primecell-periphid = <0x001feffe>;
> -- 
> 2.7.4
> 

^ permalink raw reply

* [PATCH v3 7/7] ARM: dts: stm32: add stm32 general purpose timer driver in DT
From: Lee Jones @ 2016-12-05  8:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <14837e15-07bb-39e5-b5b6-39a7dfb9130f@kernel.org>

On Sat, 03 Dec 2016, Jonathan Cameron wrote:
> On 02/12/16 13:22, Lee Jones wrote:
> > On Fri, 02 Dec 2016, Benjamin Gaignard wrote:
> > 
> >> Add general purpose timers and it sub-nodes into DT for stm32f4.
> >> Define and enable pwm1 and pwm3 for stm32f469 discovery board
> >>
> >> version 3:
> >> - use "st,stm32-timer-trigger" in DT
> >>
> >> version 2:
> >> - use parameters to describe hardware capabilities
> >> - do not use references for pwm and iio timer subnodes
> >>
> >> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
> >> ---
> >>  arch/arm/boot/dts/stm32f429.dtsi      | 333 +++++++++++++++++++++++++++++++++-
> >>  arch/arm/boot/dts/stm32f469-disco.dts |  28 +++
> >>  2 files changed, 360 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
> >> index bca491d..8c50d03 100644
> >> --- a/arch/arm/boot/dts/stm32f429.dtsi
> >> +++ b/arch/arm/boot/dts/stm32f429.dtsi
> >> @@ -48,7 +48,7 @@
> >>  #include "skeleton.dtsi"
> >>  #include "armv7-m.dtsi"
> >>  #include <dt-bindings/pinctrl/stm32f429-pinfunc.h>
> >> -
> >> +#include <dt-bindings/iio/timer/st,stm32-timer-triggers.h>
> >>  / {
> >>  	clocks {
> >>  		clk_hse: clk-hse {
> >> @@ -355,6 +355,21 @@
> >>  					slew-rate = <2>;
> >>  				};
> >>  			};
> >> +
> >> +			pwm1_pins: pwm at 1 {
> >> +				pins {
> >> +					pinmux = <STM32F429_PA8_FUNC_TIM1_CH1>,
> >> +						 <STM32F429_PB13_FUNC_TIM1_CH1N>,
> >> +						 <STM32F429_PB12_FUNC_TIM1_BKIN>;
> >> +				};
> >> +			};
> >> +
> >> +			pwm3_pins: pwm at 3 {
> >> +				pins {
> >> +					pinmux = <STM32F429_PB4_FUNC_TIM3_CH1>,
> >> +						 <STM32F429_PB5_FUNC_TIM3_CH2>;
> >> +				};
> >> +			};
> >>  		};
> >>  
> >>  		rcc: rcc at 40023810 {
> >> @@ -426,6 +441,322 @@
> >>  			interrupts = <80>;
> >>  			clocks = <&rcc 0 38>;
> >>  		};
> >> +
> >> +		gptimer1: gptimer1 at 40010000 {
> > 
> > timer at xxxxxxx
> > 
> > Node names should be generic and not numbered.
> > 
> > I suggest that this isn't actually a timer either.  Is contains a
> > timer (and a PWM), but in it's completeness it is not a timer per
> > say.
> That's just mean ;)  At least suggest an alternative?
> 
> stm32-gptimerish-magic?

Perfect! ;)

I already did:
  https://lkml.org/lkml/2016/11/23/131

> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40010000 0x400>;
> >> +			clocks = <&rcc 0 160>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm1 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <4>;
> >> +				st,breakinput;
> >> +				st,complementary;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer1 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <27>;
> >> +				st,input-triggers-names = TIM5_TRGO,
> >> +							  TIM2_TRGO,
> >> +							  TIM4_TRGO,
> >> +							  TIM3_TRGO;
> > 
> > I'm still dubious with matching by strings.
> > 
> > I'll take a look at the C code to see what the alternatives could be.
> > 
> >> +				st,output-triggers-names = TIM1_TRGO,
> >> +							   TIM1_CH1,
> >> +							   TIM1_CH2,
> >> +							   TIM1_CH3,
> >> +							   TIM1_CH4;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer2: gptimer2 at 40000000 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40000000 0x400>;
> >> +			clocks = <&rcc 0 128>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm2 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <4>;
> >> +				st,32bits-counter;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer2 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <28>;
> >> +				st,input-triggers-names = TIM1_TRGO,
> >> +							  TIM8_TRGO,
> >> +							  TIM3_TRGO,
> >> +							  TIM4_TRGO;
> >> +				st,output-triggers-names = TIM2_TRGO,
> >> +							   TIM2_CH1,
> >> +							   TIM2_CH2,
> >> +							   TIM2_CH3,
> >> +							   TIM2_CH4;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer3: gptimer3 at 40000400 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40000400 0x400>;
> >> +			clocks = <&rcc 0 129>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm3 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <4>;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer3 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <29>;
> >> +				st,input-triggers-names = TIM1_TRGO,
> >> +							  TIM8_TRGO,
> >> +							  TIM5_TRGO,
> >> +							  TIM4_TRGO;
> >> +				st,output-triggers-names = TIM3_TRGO,
> >> +							   TIM3_CH1,
> >> +							   TIM3_CH2,
> >> +							   TIM3_CH3,
> >> +							   TIM3_CH4;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer4: gptimer4 at 40000800 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40000800 0x400>;
> >> +			clocks = <&rcc 0 130>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm4 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <4>;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer4 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <30>;
> >> +				st,input-triggers-names = TIM1_TRGO,
> >> +							  TIM2_TRGO,
> >> +							  TIM3_TRGO,
> >> +							  TIM8_TRGO;
> >> +				st,output-triggers-names = TIM4_TRGO,
> >> +							   TIM4_CH1,
> >> +							   TIM4_CH2,
> >> +							   TIM4_CH3,
> >> +							   TIM4_CH4;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer5: gptimer5 at 40000C00 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40000C00 0x400>;
> >> +			clocks = <&rcc 0 131>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm5 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <4>;
> >> +				st,32bits-counter;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer5 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <50>;
> >> +				st,input-triggers-names = TIM2_TRGO,
> >> +							  TIM3_TRGO,
> >> +							  TIM4_TRGO,
> >> +							  TIM8_TRGO;
> >> +				st,output-triggers-names = TIM5_TRGO,
> >> +							   TIM5_CH1,
> >> +							   TIM5_CH2,
> >> +							   TIM5_CH3,
> >> +							   TIM5_CH4;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer6: gptimer6 at 40001000 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40001000 0x400>;
> >> +			clocks = <&rcc 0 132>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			timer6 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <54>;
> >> +				st,output-triggers-names = TIM6_TRGO;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer7: gptimer7 at 40001400 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40001400 0x400>;
> >> +			clocks = <&rcc 0 133>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			timer7 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <55>;
> >> +				st,output-triggers-names = TIM7_TRGO;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer8: gptimer8 at 40010400 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40010400 0x400>;
> >> +			clocks = <&rcc 0 161>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm8 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <4>;
> >> +				st,complementary;
> >> +				st,breakinput;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer8 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <46>;
> >> +				st,input-triggers-names = TIM1_TRGO,
> >> +							  TIM2_TRGO,
> >> +							  TIM4_TRGO,
> >> +							  TIM5_TRGO;
> >> +				st,output-triggers-names = TIM8_TRGO,
> >> +							   TIM8_CH1,
> >> +							   TIM8_CH2,
> >> +							   TIM8_CH3,
> >> +							   TIM8_CH4;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer9: gptimer9 at 40014000 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40014000 0x400>;
> >> +			clocks = <&rcc 0 176>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm9 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <2>;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer9 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <24>;
> >> +				st,input-triggers-names = TIM2_TRGO,
> >> +							  TIM3_TRGO;
> >> +				st,output-triggers-names = TIM9_TRGO,
> >> +							   TIM9_CH1,
> >> +							   TIM9_CH2;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer10: gptimer10 at 40014400 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40014400 0x400>;
> >> +			clocks = <&rcc 0 177>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm10 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <1>;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer11: gptimer11 at 40014800 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40014800 0x400>;
> >> +			clocks = <&rcc 0 178>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm11 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <1>;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer12: gptimer12 at 40001800 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40001800 0x400>;
> >> +			clocks = <&rcc 0 134>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm12 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <2>;
> >> +				status = "disabled";
> >> +			};
> >> +
> >> +			timer12 at 0 {
> >> +				compatible = "st,stm32-timer-trigger";
> >> +				interrupts = <43>;
> >> +				st,input-triggers-names = TIM4_TRGO,
> >> +							  TIM5_TRGO;
> >> +				st,output-triggers-names = TIM12_TRGO,
> >> +							   TIM12_CH1,
> >> +							   TIM12_CH2;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer13: gptimer13 at 40001C00 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40001C00 0x400>;
> >> +			clocks = <&rcc 0 135>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm13 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <1>;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >> +
> >> +		gptimer14: gptimer14 at 40002000 {
> >> +			compatible = "st,stm32-gptimer";
> >> +			reg = <0x40002000 0x400>;
> >> +			clocks = <&rcc 0 136>;
> >> +			clock-names = "clk_int";
> >> +			status = "disabled";
> >> +
> >> +			pwm14 at 0 {
> >> +				compatible = "st,stm32-pwm";
> >> +				st,pwm-num-chan = <1>;
> >> +				status = "disabled";
> >> +			};
> >> +		};
> >>  	};
> >>  };
> >>  
> >> diff --git a/arch/arm/boot/dts/stm32f469-disco.dts b/arch/arm/boot/dts/stm32f469-disco.dts
> >> index 8a163d7..df4ca7e 100644
> >> --- a/arch/arm/boot/dts/stm32f469-disco.dts
> >> +++ b/arch/arm/boot/dts/stm32f469-disco.dts
> >> @@ -81,3 +81,31 @@
> >>  &usart3 {
> >>  	status = "okay";
> >>  };
> >> +
> >> +&gptimer1 {
> >> +	status = "okay";
> >> +
> >> +	pwm1 at 0 {
> >> +		pinctrl-0	= <&pwm1_pins>;
> >> +		pinctrl-names	= "default";
> >> +		status = "okay";
> >> +	};
> >> +
> >> +	timer1 at 0 {
> >> +		status = "okay";
> >> +	};
> >> +};
> > 
> > This is a much *better* format than before.
> > 
> > I still don't like the '&' syntax though.
> > 
> >> +&gptimer3 {
> >> +	status = "okay";
> >> +
> >> +	pwm3 at 0 {
> >> +		pinctrl-0	= <&pwm3_pins>;
> >> +		pinctrl-names	= "default";
> >> +		status = "okay";
> >> +	};
> >> +
> >> +	timer3 at 0 {
> >> +		status = "okay";
> >> +	};
> >> +};
> > 
> 

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* [PATCH v2 2/3] ARM: dts: sunxi: add support for Orange Pi Zero board
From: Maxime Ripard @ 2016-12-05  9:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <324c8820-aeea-3fad-0e02-1bdb8f675677@arm.com>

On Fri, Dec 02, 2016 at 04:10:46PM +0000, Andre Przywara wrote:
> Hi,
> 
> On 02/12/16 14:32, Icenowy Zheng wrote:
> > 
> > 
> > 02.12.2016, 22:30, "Hans de Goede" <hdegoede@redhat.com>:
> >> Hi,
> >>
> >> On 02-12-16 15:22, Icenowy Zheng wrote:
> >>>  01.12.2016, 17:36, "Maxime Ripard" <maxime.ripard@free-electrons.com>:
> >>>>  On Mon, Nov 28, 2016 at 12:29:07AM +0000, Andr? Przywara wrote:
> >>>>>   > Something more interesting happened.
> >>>>>   >
> >>>>>   > Xunlong made a add-on board for Orange Pi Zero, which exposes the
> >>>>>   > two USB Controllers exported at expansion bus as USB Type-A
> >>>>>   > connectors.
> >>>>>   >
> >>>>>   > Also it exposes a analog A/V jack and a microphone.
> >>>>>   >
> >>>>>   > Should I enable {e,o}hci{2.3} in the device tree?
> >>>>>
> >>>>>   Actually we should do this regardless of this extension board. The USB
> >>>>>   pins are not multiplexed and are exposed on user accessible pins (just
> >>>>>   not soldered, but that's a detail), so I think they qualify for DT
> >>>>>   enablement. And even if a user can't use them, it doesn't hurt to have
> >>>>>   them (since they are not multiplexed).
> >>>>
> >>>>  My main concern about this is that we'll leave regulators enabled by
> >>>>  default, for a minority of users. And that minority will prevent to do
> >>>>  a proper power management when the times come since we'll have to keep
> >>>>  that behaviour forever.
> >>>
> >>>  I think these users can add a 'fdt set /xxx/xxx status "disabled" ' .
> >>
> >> I don't think that will be necessary I'm pretty sure these extra usb
> >> ports do not have a regulator for the Vbus, they just hook directly
> >> to the 5V rail, can someone with a schematic check ?
> > 
> > We seems to have still no schematics for the add-on board.
> 
> From looking at the picture of that expansion board on the Aliexpress
> page and chasing the tracks, there is clearly no voltage regulator on
> there, it's just passive components. The 5V pin from the headers is
> routed forth and back between the two layers via some vias directly to
> the 5V pins of the USB sockets.
> 
> > But something is sure is that there's no any regulator-related pins
> > on the add-on pinout. There's only USB DM and DP pins.
> > 
> > So the Vbus must be directly connected to +5V.
> 
> So yes, it is.
> 
> But I think the question is moot anyways, since we don't provide DT
> support for that add-on board at that point anyways.
> One could imagine another board, though, which has regulators switched
> by GPIOs, but that would be their problem and they would have regulators
> specified in their specific DT snippet, then.
> 
> So to summarize:
> - For that specific Orange Pi Zero board which we discuss the DT for
> there is no regulator support for the additional USB ports. Thus nothing
> we could turn off to save power.
> - A user could just take these USB brackets with pin headers that are so
> common in PCs to connect additional USB ports to the back of the box.
> One just needs to re-sort the pins, which is a matter of a minute.
> - As long as we don't provide any easy way of handling DT changes, we
> should enable the USB ports for the sake of the users of either those
> brackets or the expansion board. Any more sophisticated USB expansion
> board with regulators would need to amend the DT anyway.

I disagree with this. We already have different ways of changing the
DT at runtime, and even if we didn't I'd still disagree. Once you add
that, there's simply no going back. Saying "let's enable it and we'll
figure it out later" doesn't work, and is essentially a "enable it".

So what you're suggesting is to have those regulators enabled forever,
which might be the case on that board anyway, but definitely shouldn't
be policy.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/d728f143/attachment.sig>

^ permalink raw reply

* [PATCH v2 1/3] ARM: da850: fix infinite loop in clk_set_rate()
From: Bartosz Golaszewski @ 2016-12-05  9:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <34f9e4c4-917e-0472-72af-96fbc7e0c6db@ti.com>

2016-12-05 9:38 GMT+01:00 Sekhar Nori <nsekhar@ti.com>:
> On Friday 02 December 2016 09:08 PM, Bartosz Golaszewski wrote:
>> The aemif clock is added twice to the lookup table in da850.c. This
>> breaks the children list of pll0_sysclk3 as we're using the same list
>> links in struct clk. When calling clk_set_rate(), we get stuck in
>> propagate_rate().
>>
>> Create a separate clock for nand, inheriting the rate of the aemif
>> clock and retrieve it in the davinci_nand module.
>>
>> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
>> ---
>>  arch/arm/mach-davinci/da850.c   | 7 ++++++-
>>  drivers/mtd/nand/davinci_nand.c | 2 +-
>>  2 files changed, 7 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
>> index e770c97..1fcc986 100644
>> --- a/arch/arm/mach-davinci/da850.c
>> +++ b/arch/arm/mach-davinci/da850.c
>> @@ -367,6 +367,11 @@ static struct clk aemif_clk = {
>>       .flags          = ALWAYS_ENABLED,
>>  };
>>
>> +static struct clk nand_clk = {
>> +     .name           = "nand",
>> +     .parent         = &aemif_clk,
>> +};
>> +
>>  static struct clk usb11_clk = {
>>       .name           = "usb11",
>>       .parent         = &pll0_sysclk4,
>> @@ -537,7 +542,7 @@ static struct clk_lookup da850_clks[] = {
>>       CLK("da830-mmc.0",      NULL,           &mmcsd0_clk),
>>       CLK("da830-mmc.1",      NULL,           &mmcsd1_clk),
>>       CLK("ti-aemif",         NULL,           &aemif_clk),
>> -     CLK(NULL,               "aemif",        &aemif_clk),
>> +     CLK(NULL,               "nand",         &nand_clk),
>
> Why use a NULL device name here?
>
>>       CLK("ohci-da8xx",       "usb11",        &usb11_clk),
>>       CLK("musb-da8xx",       "usb20",        &usb20_clk),
>>       CLK("spi_davinci.0",    NULL,           &spi0_clk),
>> diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
>> index 27fa8b8..5857d06 100644
>> --- a/drivers/mtd/nand/davinci_nand.c
>> +++ b/drivers/mtd/nand/davinci_nand.c
>> @@ -694,7 +694,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
>>
>>       ret = -EINVAL;
>>
>> -     info->clk = devm_clk_get(&pdev->dev, "aemif");
>> +     info->clk = devm_clk_get(&pdev->dev, "nand");
>
> This driver is used by keystone devices too. So just changing the
> connection id will likely break them. Why do you need to change the
> connection id?
>
> Thanks,
> Sekhar

Thanks, I didn't know it.

I thought it would make the purpose of the clock more obvious. I'll
change it back to "aemif".

Best regards,
Bartosz Golaszewski

^ permalink raw reply

* [PATCH v2 3/3] ARM: da850: fix da850_set_pll0rate()
From: Bartosz Golaszewski @ 2016-12-05  9:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <130825f1-5c8f-f6bc-1f27-929ff591a9c3@ti.com>

2016-12-05 9:45 GMT+01:00 Sekhar Nori <nsekhar@ti.com>:
> On Friday 02 December 2016 09:08 PM, Bartosz Golaszewski wrote:
>> This function is broken - its second argument is an index to the freq
>> table, not the requested clock rate in Hz. It leads to an oops when
>> called from clk_set_rate() since this argument isn't bounds checked
>> either.
>>
>> Fix it by iterating over the array of supported frequencies and
>> selecting a one that matches or returning -EINVAL for unsupported
>> rates.
>>
>> Also: update the davinci cpufreq driver. It's the only user of this
>> clock and currently it passes the cpufreq table index to
>> clk_set_rate(), which is confusing. Make it pass the requested clock
>> rate in Hz.
>>
>> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
>> ---
>>  arch/arm/mach-davinci/da850.c     | 22 ++++++++++++++++++----
>>  drivers/cpufreq/davinci-cpufreq.c |  2 +-
>>  2 files changed, 19 insertions(+), 5 deletions(-)
>>
>> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
>> index a55101c..92e3303 100644
>> --- a/arch/arm/mach-davinci/da850.c
>> +++ b/arch/arm/mach-davinci/da850.c
>> @@ -1179,14 +1179,28 @@ static int da850_set_armrate(struct clk *clk, unsigned long index)
>>       return clk_set_rate(pllclk, index);
>>  }
>>
>> -static int da850_set_pll0rate(struct clk *clk, unsigned long index)
>> +static int da850_set_pll0rate(struct clk *clk, unsigned long rate)
>>  {
>> -     unsigned int prediv, mult, postdiv;
>> -     struct da850_opp *opp;
>>       struct pll_data *pll = clk->pll_data;
>> +     struct cpufreq_frequency_table *freq;
>> +     unsigned int prediv, mult, postdiv;
>> +     struct da850_opp *opp = NULL;
>>       int ret;
>>
>> -     opp = (struct da850_opp *) cpufreq_info.freq_table[index].driver_data;
>> +     for (freq = da850_freq_table;
>> +          freq->frequency != CPUFREQ_TABLE_END; freq++) {
>> +             /* requested_rate is in Hz, freq->frequency is in KHz */
>> +             unsigned long freq_rate = freq->frequency * 1000;
>
> A small optimization here. Instead of multiplying potentially every
> frequency in the table by 1000, you could divide the incoming rate down
> to KHz. This will also avoid the need for 'freq_rate'. Should have
> noticed this earlier. Sorry about that.
>
> Thanks,
> Sekhar

I thought about it, but figured the multiplication would be safer. I
will change it if you prefer this version.

Best regards,
Bartosz Golaszewski

^ permalink raw reply

* [linux-sunxi] [PATCH v3 -next 2/2] ARM: dts: sunxi: add support for Orange Pi Zero board
From: Maxime Ripard @ 2016-12-05  9:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161203162455.OlDm27ch@smtp3m.mail.yandex.net>

Hi Icenowy,

On Sat, Dec 03, 2016 at 09:24:19PM +0800, Icenowy Zheng wrote:
> <p dir="ltr"><br>
> 2016&#24180;12&#26376;3&#26085; &#19979;&#21320;5:43&#20110; Jernej Skrabec &lt;jernej.skrabec at gmail.com&gt;&#20889;&#36947;&#65306;<br>
> &gt;<br>
> &gt; Hi,<br>
> &gt;<br>
> &gt; Dne petek, 02. december 2016 17.42.04 UTC+1 je oseba Chen-Yu Tsai napisala:<br>
> &gt;&gt;<br>
> &gt;&gt; Hi, <br>
> &gt;&gt;<br>
> &gt;&gt; On Fri, Dec 2, 2016 at 11:05 PM, Icenowy Zheng &lt;ice... at aosc.xyz&gt; wrote: <br>
> &gt;&gt; &gt; Orange Pi Zero is a board that came with the new Allwinner H2+ SoC and a <br>
> &gt;&gt; &gt; SDIO Wi-Fi chip by Allwinner (XR819). <br>
> &gt;&gt; &gt; <br>
> &gt;&gt; &gt; Add a device tree file for it. <br>
> &gt;&gt; &gt; <br>
> &gt;&gt; &gt; Signed-off-by: Icenowy Zheng &lt;ice... at aosc.xyz&gt; <br>

Please make sure to disable the HTML replies, this is what your mail
looks like on a !HTML MUA :)

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/8617213b/attachment.sig>

^ permalink raw reply

* [PATCH v3 0/4]  mm: fix the "counter.sh" failure for libhugetlbfs
From: Huang Shijie @ 2016-12-05  9:17 UTC (permalink / raw)
  To: linux-arm-kernel

(1) Background
   For the arm64, the hugetlb page size can be 32M (PMD + Contiguous bit).
   In the 4K page environment, the max page order is 10 (max_order - 1),
   so 32M page is the gigantic page.    

   The arm64 MMU supports a Contiguous bit which is a hint that the TTE
   is one of a set of contiguous entries which can be cached in a single
   TLB entry.  Please refer to the arm64v8 mannul :
       DDI0487A_f_armv8_arm.pdf (in page D4-1811)

(2) The bug   
   After I tested the libhugetlbfs, I found the test case "counter.sh"
   will fail with the gigantic page (32M page in arm64 board).

   The counter.sh is just a wrapper for counter.c.
   You can find them in:
       https://github.com/libhugetlbfs/libhugetlbfs/blob/master/tests/counters.c
       https://github.com/libhugetlbfs/libhugetlbfs/blob/master/tests/counters.sh

   The error log shows below:

   ----------------------------------------------------------
        ...........................................
	LD_PRELOAD=libhugetlbfs.so shmoverride_unlinked (32M: 64):	PASS
	LD_PRELOAD=libhugetlbfs.so HUGETLB_SHM=yes shmoverride_unlinked (32M: 64):	PASS
	quota.sh (32M: 64):	PASS
	counters.sh (32M: 64):	FAIL mmap failed: Invalid argument
	********** TEST SUMMARY
	*                      32M           
	*                      32-bit 64-bit 
	*     Total testcases:     0     87   
	*             Skipped:     0      0   
	*                PASS:     0     86   
	*                FAIL:     0      1   
	*    Killed by signal:     0      0   
	*   Bad configuration:     0      0   
	*       Expected FAIL:     0      0   
	*     Unexpected PASS:     0      0   
	* Strange test result:     0      0   
	**********
   ----------------------------------------------------------

   The failure is caused by:
    1) kernel fails to allocate a gigantic page for the surplus case.
       And the gather_surplus_pages() will return NULL in the end.

    2) The condition checks for some functions are wrong:
        return_unused_surplus_pages()
        nr_overcommit_hugepages_store()
        hugetlb_overcommit_handler()
   
   This patch set adds support for gigantic surplus hugetlb pages,
   allowing the counter.sh unit test to pass. 
   Test this patch set with Juno-r1 board.

   	
v2 -- > v3:
   1.) In patch 2, change argument "no_init" to "do_prep" 
   2.) In patch 3, also change alloc_fresh_huge_page().
       In the v2, this patch only changes the alloc_fresh_gigantic_page().  
   3.) Merge old patch #4,#5 into the last one.    
   4.) Follow Babka's suggestion, do the NULL check for @mask.
   5.) others.


v1 -- > v2:
   1.) fix the compiler error in X86.
   2.) add new patches for NUMA.
       The patch #2 ~ #5 are new patches.

Huang Shijie (4):
  mm: hugetlb: rename some allocation functions
  mm: hugetlb: add a new parameter for some functions
  mm: hugetlb: change the return type for some functions
  mm: hugetlb: support gigantic surplus pages

 include/linux/mempolicy.h |   8 +++
 mm/hugetlb.c              | 146 +++++++++++++++++++++++++++++++++++-----------
 mm/mempolicy.c            |  44 ++++++++++++++
 3 files changed, 163 insertions(+), 35 deletions(-)

-- 
2.5.5

^ permalink raw reply

* [PATCH v3 1/4] mm: hugetlb: rename some allocation functions
From: Huang Shijie @ 2016-12-05  9:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480929431-22348-1-git-send-email-shijie.huang@arm.com>

After a future patch, the __alloc_buddy_huge_page() will not necessarily
use the buddy allocator.

So this patch removes the "buddy" from these functions:
	__alloc_buddy_huge_page -> __alloc_huge_page
	__alloc_buddy_huge_page_no_mpol -> __alloc_huge_page_no_mpol
	__alloc_buddy_huge_page_with_mpol -> __alloc_huge_page_with_mpol

This patch also adds the description for alloc_gigantic_page().

This patch makes preparation for the later patch.

Signed-off-by: Huang Shijie <shijie.huang@arm.com>
---
 mm/hugetlb.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5f228cd..5f4213d 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1089,6 +1089,12 @@ static bool zone_spans_last_pfn(const struct zone *zone,
 	return zone_spans_pfn(zone, last_pfn);
 }
 
+/*
+ * Allocate a gigantic page from @nid node.
+ *
+ * Scan the zones of @nid node, and try to allocate a number of contiguous
+ * pages (1 << order).
+ */
 static struct page *alloc_gigantic_page(int nid, unsigned int order)
 {
 	unsigned long nr_pages = 1 << order;
@@ -1157,6 +1163,10 @@ static int alloc_fresh_gigantic_page(struct hstate *h,
 
 static inline bool gigantic_page_supported(void) { return true; }
 #else
+static inline struct page *alloc_gigantic_page(int nid, unsigned int order)
+{
+	return NULL;
+}
 static inline bool gigantic_page_supported(void) { return false; }
 static inline void free_gigantic_page(struct page *page, unsigned int order) { }
 static inline void destroy_compound_gigantic_page(struct page *page,
@@ -1568,7 +1578,7 @@ static struct page *__hugetlb_alloc_buddy_huge_page(struct hstate *h,
  * For (2), we ignore 'vma' and 'addr' and use 'nid' exclusively. This
  * implies that memory policies will not be taken in to account.
  */
-static struct page *__alloc_buddy_huge_page(struct hstate *h,
+static struct page *__alloc_huge_page(struct hstate *h,
 		struct vm_area_struct *vma, unsigned long addr, int nid)
 {
 	struct page *page;
@@ -1649,21 +1659,21 @@ static struct page *__alloc_buddy_huge_page(struct hstate *h,
  * anywhere.
  */
 static
-struct page *__alloc_buddy_huge_page_no_mpol(struct hstate *h, int nid)
+struct page *__alloc_huge_page_no_mpol(struct hstate *h, int nid)
 {
 	unsigned long addr = -1;
 
-	return __alloc_buddy_huge_page(h, NULL, addr, nid);
+	return __alloc_huge_page(h, NULL, addr, nid);
 }
 
 /*
  * Use the VMA's mpolicy to allocate a huge page from the buddy.
  */
 static
-struct page *__alloc_buddy_huge_page_with_mpol(struct hstate *h,
+struct page *__alloc_huge_page_with_mpol(struct hstate *h,
 		struct vm_area_struct *vma, unsigned long addr)
 {
-	return __alloc_buddy_huge_page(h, vma, addr, NUMA_NO_NODE);
+	return __alloc_huge_page(h, vma, addr, NUMA_NO_NODE);
 }
 
 /*
@@ -1681,7 +1691,7 @@ struct page *alloc_huge_page_node(struct hstate *h, int nid)
 	spin_unlock(&hugetlb_lock);
 
 	if (!page)
-		page = __alloc_buddy_huge_page_no_mpol(h, nid);
+		page = __alloc_huge_page_no_mpol(h, nid);
 
 	return page;
 }
@@ -1711,7 +1721,7 @@ static int gather_surplus_pages(struct hstate *h, int delta)
 retry:
 	spin_unlock(&hugetlb_lock);
 	for (i = 0; i < needed; i++) {
-		page = __alloc_buddy_huge_page_no_mpol(h, NUMA_NO_NODE);
+		page = __alloc_huge_page_no_mpol(h, NUMA_NO_NODE);
 		if (!page) {
 			alloc_ok = false;
 			break;
@@ -2027,7 +2037,7 @@ struct page *alloc_huge_page(struct vm_area_struct *vma,
 	page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve, gbl_chg);
 	if (!page) {
 		spin_unlock(&hugetlb_lock);
-		page = __alloc_buddy_huge_page_with_mpol(h, vma, addr);
+		page = __alloc_huge_page_with_mpol(h, vma, addr);
 		if (!page)
 			goto out_uncharge_cgroup;
 		if (!avoid_reserve && vma_has_reserves(vma, gbl_chg)) {
@@ -2285,7 +2295,7 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count,
 	 * First take pages out of surplus state.  Then make up the
 	 * remaining difference by allocating fresh huge pages.
 	 *
-	 * We might race with __alloc_buddy_huge_page() here and be unable
+	 * We might race with __alloc_huge_page() here and be unable
 	 * to convert a surplus huge page to a normal huge page. That is
 	 * not critical, though, it just means the overall size of the
 	 * pool might be one hugepage larger than it needs to be, but
@@ -2331,7 +2341,7 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count,
 	 * By placing pages into the surplus state independent of the
 	 * overcommit value, we are allowing the surplus pool size to
 	 * exceed overcommit. There are few sane options here. Since
-	 * __alloc_buddy_huge_page() is checking the global counter,
+	 * __alloc_huge_page() is checking the global counter,
 	 * though, we'll note that we're not allowed to exceed surplus
 	 * and won't grow the pool anywhere else. Not until one of the
 	 * sysctls are changed, or the surplus pages go out of use.
-- 
2.5.5

^ permalink raw reply related

* [PATCH v3 2/4] mm: hugetlb: add a new parameter for some functions
From: Huang Shijie @ 2016-12-05  9:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480929431-22348-1-git-send-email-shijie.huang@arm.com>

This patch adds a new parameter, the "do_prep", for these functions:
   alloc_fresh_gigantic_page_node()
   alloc_fresh_gigantic_page()

The prep_new_huge_page() does some initialization for the new page.
But sometime, we do not need it to do so, such as in the surplus case
in later patch.

With this parameter, the prep_new_huge_page() can be called by needed:
   If the "do_prep" is true, calls the prep_new_huge_page() in
   the alloc_fresh_gigantic_page_node();

This patch makes preparation for the later patches.

Signed-off-by: Huang Shijie <shijie.huang@arm.com>
---
 mm/hugetlb.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5f4213d..b7c73a1 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1133,27 +1133,29 @@ static struct page *alloc_gigantic_page(int nid, unsigned int order)
 static void prep_new_huge_page(struct hstate *h, struct page *page, int nid);
 static void prep_compound_gigantic_page(struct page *page, unsigned int order);
 
-static struct page *alloc_fresh_gigantic_page_node(struct hstate *h, int nid)
+static struct page *alloc_fresh_gigantic_page_node(struct hstate *h,
+					int nid, bool do_prep)
 {
 	struct page *page;
 
 	page = alloc_gigantic_page(nid, huge_page_order(h));
 	if (page) {
 		prep_compound_gigantic_page(page, huge_page_order(h));
-		prep_new_huge_page(h, page, nid);
+		if (do_prep)
+			prep_new_huge_page(h, page, nid);
 	}
 
 	return page;
 }
 
 static int alloc_fresh_gigantic_page(struct hstate *h,
-				nodemask_t *nodes_allowed)
+				nodemask_t *nodes_allowed, bool do_prep)
 {
 	struct page *page = NULL;
 	int nr_nodes, node;
 
 	for_each_node_mask_to_alloc(h, nr_nodes, node, nodes_allowed) {
-		page = alloc_fresh_gigantic_page_node(h, node);
+		page = alloc_fresh_gigantic_page_node(h, node, do_prep);
 		if (page)
 			return 1;
 	}
@@ -1172,7 +1174,7 @@ static inline void free_gigantic_page(struct page *page, unsigned int order) { }
 static inline void destroy_compound_gigantic_page(struct page *page,
 						unsigned int order) { }
 static inline int alloc_fresh_gigantic_page(struct hstate *h,
-					nodemask_t *nodes_allowed) { return 0; }
+		nodemask_t *nodes_allowed, bool do_prep) { return 0; }
 #endif
 
 static void update_and_free_page(struct hstate *h, struct page *page)
@@ -2319,7 +2321,7 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count,
 		cond_resched();
 
 		if (hstate_is_gigantic(h))
-			ret = alloc_fresh_gigantic_page(h, nodes_allowed);
+			ret = alloc_fresh_gigantic_page(h, nodes_allowed, true);
 		else
 			ret = alloc_fresh_huge_page(h, nodes_allowed);
 		spin_lock(&hugetlb_lock);
-- 
2.5.5

^ permalink raw reply related

* [PATCH v3 3/4] mm: hugetlb: change the return type for some functions
From: Huang Shijie @ 2016-12-05  9:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480929431-22348-1-git-send-email-shijie.huang@arm.com>

This patch changes the return type to "struct page*" for
alloc_fresh_gigantic_page()/alloc_fresh_huge_page().

This patch makes preparation for later patch.

Signed-off-by: Huang Shijie <shijie.huang@arm.com>
---
 mm/hugetlb.c | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index b7c73a1..1395bef 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1148,7 +1148,7 @@ static struct page *alloc_fresh_gigantic_page_node(struct hstate *h,
 	return page;
 }
 
-static int alloc_fresh_gigantic_page(struct hstate *h,
+static struct page *alloc_fresh_gigantic_page(struct hstate *h,
 				nodemask_t *nodes_allowed, bool do_prep)
 {
 	struct page *page = NULL;
@@ -1157,10 +1157,10 @@ static int alloc_fresh_gigantic_page(struct hstate *h,
 	for_each_node_mask_to_alloc(h, nr_nodes, node, nodes_allowed) {
 		page = alloc_fresh_gigantic_page_node(h, node, do_prep);
 		if (page)
-			return 1;
+			return page;
 	}
 
-	return 0;
+	return NULL;
 }
 
 static inline bool gigantic_page_supported(void) { return true; }
@@ -1173,8 +1173,8 @@ static inline bool gigantic_page_supported(void) { return false; }
 static inline void free_gigantic_page(struct page *page, unsigned int order) { }
 static inline void destroy_compound_gigantic_page(struct page *page,
 						unsigned int order) { }
-static inline int alloc_fresh_gigantic_page(struct hstate *h,
-		nodemask_t *nodes_allowed, bool do_prep) { return 0; }
+static inline struct page *alloc_fresh_gigantic_page(struct hstate *h,
+		nodemask_t *nodes_allowed, bool do_prep) { return NULL; }
 #endif
 
 static void update_and_free_page(struct hstate *h, struct page *page)
@@ -1387,26 +1387,24 @@ static struct page *alloc_fresh_huge_page_node(struct hstate *h, int nid)
 	return page;
 }
 
-static int alloc_fresh_huge_page(struct hstate *h, nodemask_t *nodes_allowed)
+static struct page *alloc_fresh_huge_page(struct hstate *h,
+					nodemask_t *nodes_allowed)
 {
-	struct page *page;
+	struct page *page = NULL;
 	int nr_nodes, node;
-	int ret = 0;
 
 	for_each_node_mask_to_alloc(h, nr_nodes, node, nodes_allowed) {
 		page = alloc_fresh_huge_page_node(h, node);
-		if (page) {
-			ret = 1;
+		if (page)
 			break;
-		}
 	}
 
-	if (ret)
+	if (page)
 		count_vm_event(HTLB_BUDDY_PGALLOC);
 	else
 		count_vm_event(HTLB_BUDDY_PGALLOC_FAIL);
 
-	return ret;
+	return page;
 }
 
 /*
@@ -2321,9 +2319,10 @@ static unsigned long set_max_huge_pages(struct hstate *h, unsigned long count,
 		cond_resched();
 
 		if (hstate_is_gigantic(h))
-			ret = alloc_fresh_gigantic_page(h, nodes_allowed, true);
+			ret = !!alloc_fresh_gigantic_page(h, nodes_allowed,
+							true);
 		else
-			ret = alloc_fresh_huge_page(h, nodes_allowed);
+			ret = !!alloc_fresh_huge_page(h, nodes_allowed);
 		spin_lock(&hugetlb_lock);
 		if (!ret)
 			goto out;
-- 
2.5.5

^ permalink raw reply related

* [PATCH v3 4/4] mm: hugetlb: support gigantic surplus pages
From: Huang Shijie @ 2016-12-05  9:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480929431-22348-1-git-send-email-shijie.huang@arm.com>

When testing the gigantic page whose order is too large for the buddy
allocator, the libhugetlbfs test case "counter.sh" will fail.

The counter.sh is just a wrapper for counter.c, you can find them in:
  https://github.com/libhugetlbfs/libhugetlbfs/blob/master/tests/counters.c
  https://github.com/libhugetlbfs/libhugetlbfs/blob/master/tests/counters.sh

Please see the error log below:
 ............................................
    ........
    quota.sh (32M: 64):	PASS
    counters.sh (32M: 64):	FAIL mmap failed: Invalid argument
    ********** TEST SUMMARY
    *                      32M
    *                      32-bit 64-bit
    *     Total testcases:     0     87
    *             Skipped:     0      0
    *                PASS:     0     86
    *                FAIL:     0      1
    *    Killed by signal:     0      0
    *   Bad configuration:     0      0
    *       Expected FAIL:     0      0
    *     Unexpected PASS:     0      0
    * Strange test result:     0      0
    **********
 ............................................

The failure is caused by:
 1) kernel fails to allocate a gigantic page for the surplus case.
    And the gather_surplus_pages() will return NULL in the end.

 2) The condition checks for "over-commit" is wrong.

This patch does following things:

 1) This patch changes the condition checks for:
     return_unused_surplus_pages()
     nr_overcommit_hugepages_store()
     hugetlb_overcommit_handler()

 2) This patch introduces two helper functions:
    huge_nodemask() and __hugetlb_alloc_gigantic_page().
    Please see the descritions in the two functions.

 3) This patch uses __hugetlb_alloc_gigantic_page() to allocate the
    gigantic page in the __alloc_huge_page(). After this patch,
    gather_surplus_pages() can return a gigantic page for the surplus case.

After this patch, the counter.sh can pass for the gigantic page.

Signed-off-by: Huang Shijie <shijie.huang@arm.com>
---
 include/linux/mempolicy.h |  8 +++++
 mm/hugetlb.c              | 77 +++++++++++++++++++++++++++++++++++++++++++----
 mm/mempolicy.c            | 44 +++++++++++++++++++++++++++
 3 files changed, 123 insertions(+), 6 deletions(-)

diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h
index 5f4d828..6539fbb 100644
--- a/include/linux/mempolicy.h
+++ b/include/linux/mempolicy.h
@@ -146,6 +146,8 @@ extern void mpol_rebind_task(struct task_struct *tsk, const nodemask_t *new,
 				enum mpol_rebind_step step);
 extern void mpol_rebind_mm(struct mm_struct *mm, nodemask_t *new);
 
+extern bool huge_nodemask(struct vm_area_struct *vma,
+				unsigned long addr, nodemask_t *mask);
 extern struct zonelist *huge_zonelist(struct vm_area_struct *vma,
 				unsigned long addr, gfp_t gfp_flags,
 				struct mempolicy **mpol, nodemask_t **nodemask);
@@ -269,6 +271,12 @@ static inline void mpol_rebind_mm(struct mm_struct *mm, nodemask_t *new)
 {
 }
 
+static inline bool huge_nodemask(struct vm_area_struct *vma,
+				unsigned long addr, nodemask_t *mask)
+{
+	return false;
+}
+
 static inline struct zonelist *huge_zonelist(struct vm_area_struct *vma,
 				unsigned long addr, gfp_t gfp_flags,
 				struct mempolicy **mpol, nodemask_t **nodemask)
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 1395bef..04440b8 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1506,6 +1506,69 @@ int dissolve_free_huge_pages(unsigned long start_pfn, unsigned long end_pfn)
 
 /*
  * There are 3 ways this can get called:
+ *
+ * 1. When the NUMA is not enabled, use alloc_gigantic_page() to get
+ *    the gigantic page.
+ *
+ * 2. The NUMA is enabled, but the vma is NULL.
+ *    Initialize the @mask, and use alloc_fresh_gigantic_page() to get
+ *    the gigantic page.
+ *
+ * 3. The NUMA is enabled, and the vma is valid.
+ *    Use the @vma's memory policy.
+ *    Get @mask by huge_nodemask(), and use alloc_fresh_gigantic_page()
+ *    to get the gigantic page.
+ */
+static struct page *__hugetlb_alloc_gigantic_page(struct hstate *h,
+		struct vm_area_struct *vma, unsigned long addr, int nid)
+{
+	NODEMASK_ALLOC(nodemask_t, mask, GFP_KERNEL | __GFP_NORETRY);
+	struct page *page = NULL;
+
+	/* Not NUMA */
+	if (!IS_ENABLED(CONFIG_NUMA)) {
+		if (nid == NUMA_NO_NODE)
+			nid = numa_mem_id();
+
+		page = alloc_gigantic_page(nid, huge_page_order(h));
+		if (page)
+			prep_compound_gigantic_page(page, huge_page_order(h));
+		goto got_page;
+	}
+
+	/* NUMA && !vma */
+	if (!vma) {
+		/* First, check the mask */
+		if (!mask) {
+			mask = &node_states[N_MEMORY];
+		} else {
+			if (nid == NUMA_NO_NODE) {
+				if (!init_nodemask_of_mempolicy(mask)) {
+					NODEMASK_FREE(mask);
+					mask = &node_states[N_MEMORY];
+				}
+			} else {
+				init_nodemask_of_node(mask, nid);
+			}
+		}
+
+		page = alloc_fresh_gigantic_page(h, mask, false);
+		goto got_page;
+	}
+
+	/* NUMA && vma */
+	if (mask && huge_nodemask(vma, addr, mask))
+		page = alloc_fresh_gigantic_page(h, mask, false);
+
+got_page:
+	if (mask != &node_states[N_MEMORY])
+		NODEMASK_FREE(mask);
+
+	return page;
+}
+
+/*
+ * There are 3 ways this can get called:
  * 1. With vma+addr: we use the VMA's memory policy
  * 2. With !vma, but nid=NUMA_NO_NODE:  We try to allocate a huge
  *    page from any node, and let the buddy allocator itself figure
@@ -1584,7 +1647,7 @@ static struct page *__alloc_huge_page(struct hstate *h,
 	struct page *page;
 	unsigned int r_nid;
 
-	if (hstate_is_gigantic(h))
+	if (hstate_is_gigantic(h) && !gigantic_page_supported())
 		return NULL;
 
 	/*
@@ -1629,7 +1692,10 @@ static struct page *__alloc_huge_page(struct hstate *h,
 	}
 	spin_unlock(&hugetlb_lock);
 
-	page = __hugetlb_alloc_buddy_huge_page(h, vma, addr, nid);
+	if (hstate_is_gigantic(h))
+		page = __hugetlb_alloc_gigantic_page(h, vma, addr, nid);
+	else
+		page = __hugetlb_alloc_buddy_huge_page(h, vma, addr, nid);
 
 	spin_lock(&hugetlb_lock);
 	if (page) {
@@ -1796,8 +1862,7 @@ static void return_unused_surplus_pages(struct hstate *h,
 	/* Uncommit the reservation */
 	h->resv_huge_pages -= unused_resv_pages;
 
-	/* Cannot return gigantic pages currently */
-	if (hstate_is_gigantic(h))
+	if (hstate_is_gigantic(h) && !gigantic_page_supported())
 		return;
 
 	nr_pages = min(unused_resv_pages, h->surplus_huge_pages);
@@ -2514,7 +2579,7 @@ static ssize_t nr_overcommit_hugepages_store(struct kobject *kobj,
 	unsigned long input;
 	struct hstate *h = kobj_to_hstate(kobj, NULL);
 
-	if (hstate_is_gigantic(h))
+	if (hstate_is_gigantic(h) && !gigantic_page_supported())
 		return -EINVAL;
 
 	err = kstrtoul(buf, 10, &input);
@@ -2966,7 +3031,7 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write,
 
 	tmp = h->nr_overcommit_huge_pages;
 
-	if (write && hstate_is_gigantic(h))
+	if (write && hstate_is_gigantic(h) && !gigantic_page_supported())
 		return -EINVAL;
 
 	table->data = &tmp;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 6d3639e..3550a29 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1800,6 +1800,50 @@ static inline unsigned interleave_nid(struct mempolicy *pol,
 
 #ifdef CONFIG_HUGETLBFS
 /*
+ * huge_nodemask(@vma, @addr, @mask)
+ * @vma: virtual memory area whose policy is sought
+ * @addr: address in @vma
+ * @mask: should be a valid nodemask pointer, not NULL
+ *
+ * Return true if we can succeed in extracting the policy nodemask
+ * for 'bind' or 'interleave' policy into the argument @mask, or
+ * initializing the argument @mask to contain the single node for
+ * 'preferred' or 'local' policy.
+ */
+bool huge_nodemask(struct vm_area_struct *vma, unsigned long addr,
+			nodemask_t *mask)
+{
+	struct mempolicy *mpol;
+	bool ret = true;
+	int nid;
+
+	mpol = get_vma_policy(vma, addr);
+
+	switch (mpol->mode) {
+	case MPOL_PREFERRED:
+		if (mpol->flags & MPOL_F_LOCAL)
+			nid = numa_node_id();
+		else
+			nid = mpol->v.preferred_node;
+		init_nodemask_of_node(mask, nid);
+		break;
+
+	case MPOL_BIND:
+		/* Fall through */
+	case MPOL_INTERLEAVE:
+		*mask = mpol->v.nodes;
+		break;
+
+	default:
+		ret = false;
+		break;
+	}
+	mpol_cond_put(mpol);
+
+	return ret;
+}
+
+/*
  * huge_zonelist(@vma, @addr, @gfp_flags, @mpol)
  * @vma: virtual memory area whose policy is sought
  * @addr: address in @vma for shared policy lookup and interleave policy
-- 
2.5.5

^ permalink raw reply related

* [crypto] [marvell-cesa] Possible regression after Linux 4.7
From: Romain Perier @ 2016-12-05  9:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4573998d-6c5b-03e4-e1fa-7516d5a9e276@free-electrons.com>

Hello,

The issue is reproducible in STD mode too. We have found fixes.
Could you test these patches in STD mode please ?


cover letter: 
https://www.mail-archive.com/linux-crypto at vger.kernel.org/msg22143.html
1/2: https://www.mail-archive.com/linux-crypto at vger.kernel.org/msg22142.html
2/2: https://www.mail-archive.com/linux-crypto at vger.kernel.org/msg22141.html

If you want to turn off DMA, open drivers/crypto/marvell/cesa.c and in 
armada_xp_caps, change .has_tdma to false.  (assuming that you test on a 
armada 38x).

We're still investigating for regressions in DMA mode.

Have a nice day,
Romain

-- 
Romain Perier, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v3 -next 1/2] ARM: sunxi: add support for H2+ SoC
From: Maxime Ripard @ 2016-12-05  9:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161202150513.34691-1-icenowy@aosc.xyz>

On Fri, Dec 02, 2016 at 11:05:12PM +0800, Icenowy Zheng wrote:
> Allwinner H2+ is a quad-core Cortex-A7 SoC.
> 
> It is very like H3, that they share the same SoC ID (0x1680), and H3
> memory maps as well as drivers works well on the SoC.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>

Fixed the alphabetical order in the bindings doc, and applied.

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/364dbd55/attachment.sig>

^ permalink raw reply

* [PATCH] ARM: dts: sunxi: Add num-cs for A20 spi nodes
From: Maxime Ripard @ 2016-12-05  9:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161201112414.62f9b351186115f62c8998a9@bidouilliste.com>

On Thu, Dec 01, 2016 at 11:24:14AM +0100, Emmanuel Vadot wrote:
> > > > >  If num-cs isn't present nothing prevent to start a transfer
> > > > > with a non-valid CS pin, resulting in an error.  num-cs are
> > > > > default property especially made for this and a SPI driver
> > > > > should try to get the property at probe/attach time.
> > > > 
> > > > Yes, but as far as I know, our driver doesn't. I'm all in for
> > > > having support for that in our driver, but without it, that
> > > > patch is kind of useless.
> > > 
> > >  Yes the Linux driver doesn't use it but my upcoming one for FreeBSD
> > > uses it. So it is not useless for downstream user of DTS.
> > 
> > Ah, I didn't know this was for FreeBSD. So you started to use our DTs,
> > or do you have some modifications to it? How does that work?
> 
> Yes we use the DTS from linux from quite some times now. We're
> currently synced with 4.7-ish.  We either use them directly or
> modify them according to our needs and driver support.

Do you have a link to those modifications?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/7f666e1b/attachment-0001.sig>

^ permalink raw reply

* [PATCH v3 0/4] mm: fix the "counter.sh" failure for libhugetlbfs
From: Michal Hocko @ 2016-12-05  9:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1480929431-22348-1-git-send-email-shijie.huang@arm.com>

On Mon 05-12-16 17:17:07, Huang Shijie wrote:
[...]
>    The failure is caused by:
>     1) kernel fails to allocate a gigantic page for the surplus case.
>        And the gather_surplus_pages() will return NULL in the end.
> 
>     2) The condition checks for some functions are wrong:
>         return_unused_surplus_pages()
>         nr_overcommit_hugepages_store()
>         hugetlb_overcommit_handler()

OK, so how is this any different from gigantic (1G) hugetlb pages on
x86_64? Do we need the same functionality or is it just 32MB not being
handled in the same way as 1G?

Thanks!
-- 
Michal Hocko
SUSE Labs

^ permalink raw reply

* [PATCH] arm/arm64: KVM: Check for properly initialized timer on init
From: Christoffer Dall @ 2016-12-05  9:32 UTC (permalink / raw)
  To: linux-arm-kernel

When the arch timer code fails to initialize (for example because the
memory mapped timer doesn't work, which is currently seen with the AEM
model), then KVM just continues happily with a final result that KVM
eventually does a NULL pointer dereference of the uninitialized cycle
counter.

Check directly for this in the init path and give the user a reasonable
error in this case.

Cc: Shih-Wei Li <shihwei@cs.columbia.edu>
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 virt/kvm/arm/arch_timer.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 27a1f63..5c12f53 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -425,6 +425,11 @@ int kvm_timer_hyp_init(void)
 	info = arch_timer_get_kvm_info();
 	timecounter = &info->timecounter;
 
+	if (!timecounter->cc) {
+		kvm_err("arch_timer: uninitialized timecounter\n");
+		return -ENODEV;
+	}
+
 	if (info->virtual_irq <= 0) {
 		kvm_err("kvm_arch_timer: invalid virtual timer IRQ: %d\n",
 			info->virtual_irq);
-- 
2.9.0

^ permalink raw reply related

* [PATCH] ARM: dts: sunxi: Add num-cs for A20 spi nodes
From: Emmanuel Vadot @ 2016-12-05  9:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161205092821.m2fmqxnzalcignly@lukather>

On Mon, 5 Dec 2016 10:28:21 +0100
Maxime Ripard <maxime.ripard@free-electrons.com> wrote:

> On Thu, Dec 01, 2016 at 11:24:14AM +0100, Emmanuel Vadot wrote:
> > > > > >  If num-cs isn't present nothing prevent to start a transfer
> > > > > > with a non-valid CS pin, resulting in an error.  num-cs are
> > > > > > default property especially made for this and a SPI driver
> > > > > > should try to get the property at probe/attach time.
> > > > > 
> > > > > Yes, but as far as I know, our driver doesn't. I'm all in for
> > > > > having support for that in our driver, but without it, that
> > > > > patch is kind of useless.
> > > > 
> > > >  Yes the Linux driver doesn't use it but my upcoming one for FreeBSD
> > > > uses it. So it is not useless for downstream user of DTS.
> > > 
> > > Ah, I didn't know this was for FreeBSD. So you started to use our DTs,
> > > or do you have some modifications to it? How does that work?
> > 
> > Yes we use the DTS from linux from quite some times now. We're
> > currently synced with 4.7-ish.  We either use them directly or
> > modify them according to our needs and driver support.
> 
> Do you have a link to those modifications?
> 
> Thanks,
> Maxime
> 
> -- 
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

 Sure,

 https://svnweb.freebsd.org/base/head/sys/boot/fdt/dts/arm/

-- 
Emmanuel Vadot <manu@bidouilliste.com> <manu@freebsd.org>

^ permalink raw reply

* [PATCH v2 2/3] ARM: dts: sunxi: add support for Orange Pi Zero board
From: Maxime Ripard @ 2016-12-05  9:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161205120021.0GBGtAl4@smtp3m.mail.yandex.net>

On Mon, Dec 05, 2016 at 04:59:44PM +0800, Icenowy Zheng wrote:
> 
> 2016?12?5? 16:52? Maxime Ripard <maxime.ripard@free-electrons.com>???
> >
> > On Fri, Dec 02, 2016 at 10:22:30PM +0800, Icenowy Zheng wrote: 
> > > 
> > > 
> > > 01.12.2016, 17:36, "Maxime Ripard" <maxime.ripard@free-electrons.com>: 
> > > > On Mon, Nov 28, 2016 at 12:29:07AM +0000, Andr? Przywara wrote: 
> > > >> ?> Something more interesting happened. 
> > > >> ?> 
> > > >> ?> Xunlong made a add-on board for Orange Pi Zero, which exposes the 
> > > >> ?> two USB Controllers exported at expansion bus as USB Type-A 
> > > >> ?> connectors. 
> > > >> ?> 
> > > >> ?> Also it exposes a analog A/V jack and a microphone. 
> > > >> ?> 
> > > >> ?> Should I enable {e,o}hci{2.3} in the device tree? 
> > > >> 
> > > >> ?Actually we should do this regardless of this extension board. The USB 
> > > >> ?pins are not multiplexed and are exposed on user accessible pins (just 
> > > >> ?not soldered, but that's a detail), so I think they qualify for DT 
> > > >> ?enablement. And even if a user can't use them, it doesn't hurt to have 
> > > >> ?them (since they are not multiplexed). 
> > > > 
> > > > My main concern about this is that we'll leave regulators enabled by 
> > > > default, for a minority of users. And that minority will prevent to do 
> > > > a proper power management when the times come since we'll have to keep 
> > > > that behaviour forever. 
> > > 
> > > I think these users can add a 'fdt set /xxx/xxx status "disabled" ' . 
> >
> > You can't ask that from the majority of users. These users will take 
> > debian or fedora, install it, and expect everything to work 
> > properly. I would make the opposite argument actually. If someone is 
> > knowledgeable enough to solder the USB pins a connector, then (s)he'll 
> > be able to make that u-boot call. 
> 
> Now (s)he do not need soldering.
> 
> (S)he needs only paying $1.99 more to Xunlong to get the expansion
> board, and insert it on the OPi Zero.

Which is going to require an overlay anyway, so we could have the USB
bits in there too.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161205/194dd5d6/attachment.sig>

^ permalink raw reply

* [PATCH 3/8] rtc: add STM32 RTC driver
From: Amelie DELAUNAY @ 2016-12-05  9:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161202175646.GA3284@linaro.org>

Hi Mathieu,

Thanks for reviewing

On 12/02/2016 06:56 PM, Mathieu Poirier wrote:
 > On Fri, Dec 02, 2016 at 03:09:56PM +0100, Amelie Delaunay wrote:
 >> This patch adds support for the STM32 RTC.
 >
 > Hello Amelie,
 >
 >>
 >> Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
 >> ---
 >>  drivers/rtc/Kconfig     |  10 +
 >>  drivers/rtc/Makefile    |   1 +
 >>  drivers/rtc/rtc-stm32.c | 777 
++++++++++++++++++++++++++++++++++++++++++++++++
 >>  3 files changed, 788 insertions(+)
 >>  create mode 100644 drivers/rtc/rtc-stm32.c
 >>
 >> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
 >> index e859d14..dd8b218 100644
 >> --- a/drivers/rtc/Kconfig
 >> +++ b/drivers/rtc/Kconfig
 >> @@ -1706,6 +1706,16 @@ config RTC_DRV_PIC32
 >>         This driver can also be built as a module. If so, the module
 >>         will be called rtc-pic32
 >>
 >> +config RTC_DRV_STM32
 >> +    tristate "STM32 On-Chip RTC"
 >> +    depends on ARCH_STM32
 >> +    help
 >> +       If you say yes here you get support for the STM32 On-Chip
 >> +       Real Time Clock.
 >> +
 >> +       This driver can also be built as a module, if so, the module
 >> +       will be called "rtc-stm32".
 >> +
 >>  comment "HID Sensor RTC drivers"
 >>
 >>  config RTC_DRV_HID_SENSOR_TIME
 >> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
 >> index 1ac694a..87bd9cc 100644
 >> --- a/drivers/rtc/Makefile
 >> +++ b/drivers/rtc/Makefile
 >> @@ -144,6 +144,7 @@ obj-$(CONFIG_RTC_DRV_SNVS)    += rtc-snvs.o
 >>  obj-$(CONFIG_RTC_DRV_SPEAR)    += rtc-spear.o
 >>  obj-$(CONFIG_RTC_DRV_STARFIRE)    += rtc-starfire.o
 >>  obj-$(CONFIG_RTC_DRV_STK17TA8)    += rtc-stk17ta8.o
 >> +obj-$(CONFIG_RTC_DRV_STM32)     += rtc-stm32.o
 >>  obj-$(CONFIG_RTC_DRV_STMP)    += rtc-stmp3xxx.o
 >>  obj-$(CONFIG_RTC_DRV_ST_LPC)    += rtc-st-lpc.o
 >>  obj-$(CONFIG_RTC_DRV_SUN4V)    += rtc-sun4v.o
 >> diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
 >> new file mode 100644
 >> index 0000000..9e710ff
 >> --- /dev/null
 >> +++ b/drivers/rtc/rtc-stm32.c
 >> @@ -0,0 +1,777 @@
 >> +/*
 >> + * Copyright (C) Amelie Delaunay 2015
 >> + * Author:  Amelie Delaunay <adelaunay.stm32@gmail.com>
 >> + * License terms:  GNU General Public License (GPL), version 2
 >> + */
 >> +
 >> +#include <linux/bcd.h>
 >> +#include <linux/clk.h>
 >> +#include <linux/init.h>
 >> +#include <linux/io.h>
 >> +#include <linux/iopoll.h>
 >> +#include <linux/ioport.h>
 >> +#include <linux/kernel.h>
 >> +#include <linux/mfd/syscon.h>
 >> +#include <linux/module.h>
 >> +#include <linux/of.h>
 >> +#include <linux/of_device.h>
 >> +#include <linux/platform_device.h>
 >> +#include <linux/regmap.h>
 >> +#include <linux/rtc.h>
 >> +#include <linux/spinlock.h>
 >> +
 >> +#define DRIVER_NAME "stm32_rtc"
 >> +
 >> +/* STM32 RTC registers */
 >> +#define STM32_RTC_TR        0x00
 >> +#define STM32_RTC_DR        0x04
 >> +#define STM32_RTC_CR        0x08
 >> +#define STM32_RTC_ISR        0x0C
 >> +#define STM32_RTC_PRER        0x10
 >> +#define STM32_RTC_ALRMAR    0x1C
 >> +#define STM32_RTC_WPR        0x24
 >> +
 >> +/* STM32_RTC_TR bit fields  */
 >> +#define STM32_RTC_TR_SEC_SHIFT        0
 >> +#define STM32_RTC_TR_SEC        GENMASK(6, 0)
 >> +#define STM32_RTC_TR_MIN_SHIFT        8
 >> +#define STM32_RTC_TR_MIN        GENMASK(14, 8)
 >> +#define STM32_RTC_TR_HOUR_SHIFT        16
 >> +#define STM32_RTC_TR_HOUR        GENMASK(21, 16)
 >> +
 >> +/* STM32_RTC_DR bit fields */
 >> +#define STM32_RTC_DR_DATE_SHIFT        0
 >> +#define STM32_RTC_DR_DATE        GENMASK(5, 0)
 >> +#define STM32_RTC_DR_MONTH_SHIFT    8
 >> +#define STM32_RTC_DR_MONTH        GENMASK(11, 8)
 >> +#define STM32_RTC_DR_WDAY_SHIFT        13
 >> +#define STM32_RTC_DR_WDAY        GENMASK(15, 13)
 >> +#define STM32_RTC_DR_YEAR_SHIFT        16
 >> +#define STM32_RTC_DR_YEAR        GENMASK(23, 16)
 >> +
 >> +/* STM32_RTC_CR bit fields */
 >> +#define STM32_RTC_CR_FMT        BIT(6)
 >> +#define STM32_RTC_CR_ALRAE        BIT(8)
 >> +#define STM32_RTC_CR_ALRAIE        BIT(12)
 >> +
 >> +/* STM32_RTC_ISR bit fields */
 >> +#define STM32_RTC_ISR_ALRAWF        BIT(0)
 >> +#define STM32_RTC_ISR_INITS        BIT(4)
 >> +#define STM32_RTC_ISR_RSF        BIT(5)
 >> +#define STM32_RTC_ISR_INITF        BIT(6)
 >> +#define STM32_RTC_ISR_INIT        BIT(7)
 >> +#define STM32_RTC_ISR_ALRAF        BIT(8)
 >> +
 >> +/* STM32_RTC_PRER bit fields */
 >> +#define STM32_RTC_PRER_PRED_S_SHIFT    0
 >> +#define STM32_RTC_PRER_PRED_S        GENMASK(14, 0)
 >> +#define STM32_RTC_PRER_PRED_A_SHIFT    16
 >> +#define STM32_RTC_PRER_PRED_A        GENMASK(22, 16)
 >> +
 >> +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
 >> +#define STM32_RTC_ALRMXR_SEC_SHIFT    0
 >> +#define STM32_RTC_ALRMXR_SEC        GENMASK(6, 0)
 >> +#define STM32_RTC_ALRMXR_SEC_MASK    BIT(7)
 >> +#define STM32_RTC_ALRMXR_MIN_SHIFT    8
 >> +#define STM32_RTC_ALRMXR_MIN        GENMASK(14, 8)
 >> +#define STM32_RTC_ALRMXR_MIN_MASK    BIT(15)
 >> +#define STM32_RTC_ALRMXR_HOUR_SHIFT    16
 >> +#define STM32_RTC_ALRMXR_HOUR        GENMASK(21, 16)
 >> +#define STM32_RTC_ALRMXR_PM        BIT(22)
 >> +#define STM32_RTC_ALRMXR_HOUR_MASK    BIT(23)
 >> +#define STM32_RTC_ALRMXR_DATE_SHIFT    24
 >> +#define STM32_RTC_ALRMXR_DATE        GENMASK(29, 24)
 >> +#define STM32_RTC_ALRMXR_WDSEL        BIT(30)
 >> +#define STM32_RTC_ALRMXR_WDAY_SHIFT    24
 >> +#define STM32_RTC_ALRMXR_WDAY        GENMASK(27, 24)
 >> +#define STM32_RTC_ALRMXR_DATE_MASK    BIT(31)
 >> +
 >> +/* STM32_RTC_WPR key constants */
 >> +#define RTC_WPR_1ST_KEY            0xCA
 >> +#define RTC_WPR_2ND_KEY            0x53
 >> +#define RTC_WPR_WRONG_KEY        0xFF
 >> +
 >> +/*
 >> + * RTC registers are protected agains parasitic write access.
 >> + * PWR_CR_DBP bit must be set to enable write access to RTC registers.
 >> + */
 >> +/* STM32_PWR_CR */
 >> +#define PWR_CR                0x00
 >> +/* STM32_PWR_CR bit field */
 >> +#define PWR_CR_DBP            BIT(8)
 >> +
 >> +static struct regmap *dbp;
 >> +
 >> +struct stm32_rtc {
 >> +    struct rtc_device *rtc_dev;
 >> +    void __iomem *base;
 >> +    struct clk *pclk;
 >> +    struct clk *ck_rtc;
 >> +    unsigned int clksrc;
 >> +    spinlock_t lock; /* Protects registers accesses */
 >> +    int irq_alarm;
 >> +    struct regmap *pwrcr;
 >> +};
 >> +
 >> +static inline unsigned int stm32_rtc_readl(struct stm32_rtc *rtc,
 >> +                       unsigned int offset)
 >> +{
 >> +    return readl_relaxed(rtc->base + offset);
 >> +}
 >> +
 >> +static inline void stm32_rtc_writel(struct stm32_rtc *rtc,
 >> +                    unsigned int offset, unsigned int value)
 >> +{
 >> +    writel_relaxed(value, rtc->base + offset);
 >> +}
 >
 > I'm not sure wrapping the readl/writel_relaxed function does anything 
special
 > other than simply redirecting the reader to another section of the code.
During development phase, it is useful to add debug traces but you're 
right, this can be remove.
 >
 >> +
 >> +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
 >> +{
 >> +//    if (dbp)
 >> +//        regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
 >
 > Did checkpatch let you get away with this?  What did you intend to do 
here?
Hum, as surprising as it may seem, checkpatch didn't complained about 
these comments! But anyway, this has to be removed, it was a tentative 
to enable/disable backup domain write protection any time we have to 
write in a protected RTC register, but it is not functionnal. I have 
commented this just to keep it in mind and forget to remove it before 
sending.
 >
 >> +
 >> +    stm32_rtc_writel(rtc, STM32_RTC_WPR, RTC_WPR_1ST_KEY);
 >> +    stm32_rtc_writel(rtc, STM32_RTC_WPR, RTC_WPR_2ND_KEY);
 >> +}
 >> +
 >> +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
 >> +{
 >> +    stm32_rtc_writel(rtc, STM32_RTC_WPR, RTC_WPR_WRONG_KEY);
 >> +
 >> +//    if (dbp)
 >> +//        regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
 >> +}
 >> +
 >> +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
 >> +{
 >> +    unsigned int isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
 >> +
 >> +    if (!(isr & STM32_RTC_ISR_INITF)) {
 >> +        isr |= STM32_RTC_ISR_INIT;
 >> +        stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
 >> +
 >> +        return readl_relaxed_poll_timeout_atomic(
 >> +                    rtc->base + STM32_RTC_ISR,
 >> +                    isr, (isr & STM32_RTC_ISR_INITF),
 >> +                    10, 100000);
 >
 > When using hard coded numerics please add comments that explains the 
reason
 > behind the selected values.
Sure. It takes around 2 RTCCLK clock cycles to enter in initialization 
phase mode. So it depends on the frequency of the ck_rtc parent clock.
Either I keep parent clock frequency and compute the exact timeout, or I 
use the "best and worst cases": slowest RTCCLK frequency is 32kHz, so it 
can take up to 62us, highest RTCCLK frequency should be 1MHz, so it can 
take only 2us. Polling every 10us with a timeout of 100ms seemed 
reasonable and be a good compromise.
 >
 >> +    }
 >> +
 >> +    return 0;
 >> +}
 >> +
 >> +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
 >> +{
 >> +    unsigned int isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
 >> +
 >> +    isr &= ~STM32_RTC_ISR_INIT;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
 >> +}
 >> +
 >> +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
 >> +{
 >> +    unsigned int isr;
 >> +
 >> +    isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
 >> +
 >> +    isr &= ~STM32_RTC_ISR_RSF;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
 >> +
 >> +    /* Wait the registers to be synchronised */
 >> +    return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
 >> +                         isr,
 >> +                         (isr & STM32_RTC_ISR_RSF),
 >> +                         10, 100000);
 >
 > Shouldn't the break condition be !((isr & STM32_RTC_ISR_RSF) ?  If 
not this
 > probably deserve a better comment.
RSF bit is set by hardware each time the calendar registers are 
synchronized (it takes up to 2 RTCCLK). So the break condition is 
correct: we poll until RSF flag is set or timeout is reached.
 >
 >> +}
 >> +
 >> +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
 >> +{
 >> +    struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
 >> +    unsigned long irqflags, events = 0;
 >> +    unsigned int isr, cr;
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
 >> +    cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
 >> +
 >> +    if ((isr & STM32_RTC_ISR_ALRAF) &&
 >> +        (cr & STM32_RTC_CR_ALRAIE)) {
 >> +        /* Alarm A flag - Alarm interrupt */
 >> +        events |= RTC_IRQF | RTC_AF;
 >> +        isr &= ~STM32_RTC_ISR_ALRAF;
 >> +    }
 >> +
 >> +    /* Clear event irqflags, otherwise new events won't be received */
 >> +    stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    if (events) {
 >> +        dev_info(&rtc->rtc_dev->dev, "Alarm occurred\n");
 >> +
 >> +        /* Pass event to the kernel */
 >> +        rtc_update_irq(rtc->rtc_dev, 1, events);
 >> +        return IRQ_HANDLED;
 >> +    } else {
 >> +        return IRQ_NONE;
 >> +    }
 >> +}
 >> +
 >> +/* Convert rtc_time structure from bin to bcd format */
 >> +static void tm2bcd(struct rtc_time *tm)
 >> +{
 >> +    tm->tm_sec = bin2bcd(tm->tm_sec);
 >> +    tm->tm_min = bin2bcd(tm->tm_min);
 >> +    tm->tm_hour = bin2bcd(tm->tm_hour);
 >> +
 >> +    tm->tm_mday = bin2bcd(tm->tm_mday);
 >> +    tm->tm_mon = bin2bcd(tm->tm_mon + 1);
 >> +    tm->tm_year = bin2bcd(tm->tm_year - 100);
 >> +    /*
 >> +     * Number of days since Sunday
 >> +     * - on kernel side, 0=Sunday...6=Saturday
 >> +     * - on rtc side, 0=invalid,1=Monday...7=Sunday
 >> +     */
 >> +    tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
 >> +}
 >> +
 >> +/* Convert rtc_time structure from bcd to bin format */
 >> +static void bcd2tm(struct rtc_time *tm)
 >> +{
 >> +    tm->tm_sec = bcd2bin(tm->tm_sec);
 >> +    tm->tm_min = bcd2bin(tm->tm_min);
 >> +    tm->tm_hour = bcd2bin(tm->tm_hour);
 >> +
 >> +    tm->tm_mday = bcd2bin(tm->tm_mday);
 >> +    tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
 >> +    tm->tm_year = bcd2bin(tm->tm_year) + 100;
 >> +    /*
 >> +     * Number of days since Sunday
 >> +     * - on kernel side, 0=Sunday...6=Saturday
 >> +     * - on rtc side, 0=invalid,1=Monday...7=Sunday
 >> +     */
 >> +    tm->tm_wday %= 7;
 >> +}
 >> +
 >> +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +    unsigned int tr, dr;
 >> +    unsigned long irqflags;
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    /* Time and Date in BCD format */
 >> +    tr = stm32_rtc_readl(rtc, STM32_RTC_TR);
 >> +    dr = stm32_rtc_readl(rtc, STM32_RTC_DR);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
 >> +    tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
 >> +    tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
 >> +
 >> +    tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
 >> +    tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
 >> +    tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
 >> +    tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
 >> +
 >> +    /* We don't report tm_yday and tm_isdst */
 >> +
 >> +    bcd2tm(tm);
 >> +
 >> +    if (rtc_valid_tm(tm) < 0) {
 >> +        dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
 >> +        return -EINVAL;
 >> +    }
 >> +
 >> +    return 0;
 >> +}
 >> +
 >> +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +    unsigned int tr, dr;
 >> +    unsigned long irqflags;
 >> +    int ret = 0;
 >> +
 >> +    if (rtc_valid_tm(tm) < 0) {
 >> +        dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
 >> +        return -EINVAL;
 >> +    }
 >> +
 >> +    tm2bcd(tm);
 >> +
 >> +    /* Time in BCD format */
 >> +    tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
 >> +         ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
 >> +         ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & 
STM32_RTC_TR_HOUR);
 >> +
 >> +    /* Date in BCD format */
 >> +    dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & 
STM32_RTC_DR_DATE) |
 >> +         ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & 
STM32_RTC_DR_MONTH) |
 >> +         ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & 
STM32_RTC_DR_YEAR) |
 >> +         ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & 
STM32_RTC_DR_WDAY);
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    stm32_rtc_wpr_unlock(rtc);
 >> +
 >> +    ret = stm32_rtc_enter_init_mode(rtc);
 >> +    if (ret) {
 >> +        dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
 >> +        goto end;
 >> +    }
 >> +
 >> +    stm32_rtc_writel(rtc, STM32_RTC_TR, tr);
 >> +    stm32_rtc_writel(rtc, STM32_RTC_DR, dr);
 >> +
 >> +    stm32_rtc_exit_init_mode(rtc);
 >> +
 >> +    ret = stm32_rtc_wait_sync(rtc);
 >> +end:
 >> +    stm32_rtc_wpr_lock(rtc);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    return ret;
 >> +}
 >> +
 >> +static int stm32_rtc_read_alarm(struct device *dev, struct 
rtc_wkalrm *alrm)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +    struct rtc_time *tm = &alrm->time;
 >> +    unsigned int alrmar, cr, isr;
 >> +    unsigned long irqflags;
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    alrmar = stm32_rtc_readl(rtc, STM32_RTC_ALRMAR);
 >> +    cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
 >> +    isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
 >> +        /*
 >> +         * Date/day don't care in Alarm comparison so alarm triggers
 >> +         * every day
 >> +         */
 >> +        tm->tm_mday = -1;
 >> +        tm->tm_wday = -1;
 >> +    } else {
 >> +        if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
 >> +            /* Alarm is set to a day of week */
 >> +            tm->tm_mday = -1;
 >> +            tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
 >> +                      STM32_RTC_ALRMXR_WDAY_SHIFT;
 >> +            tm->tm_wday %= 7;
 >> +        } else {
 >> +            /* Alarm is set to a day of month */
 >> +            tm->tm_wday = -1;
 >> +            tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
 >> +                       STM32_RTC_ALRMXR_DATE_SHIFT;
 >> +        }
 >> +    }
 >> +
 >> +    if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
 >> +        /* Hours don't care in Alarm comparison */
 >> +        tm->tm_hour = -1;
 >> +    } else {
 >> +        tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
 >> +                   STM32_RTC_ALRMXR_HOUR_SHIFT;
 >> +        if (alrmar & STM32_RTC_ALRMXR_PM)
 >> +            tm->tm_hour += 12;
 >> +    }
 >> +
 >> +    if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
 >> +        /* Minutes don't care in Alarm comparison */
 >> +        tm->tm_min = -1;
 >> +    } else {
 >> +        tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
 >> +                  STM32_RTC_ALRMXR_MIN_SHIFT;
 >> +    }
 >> +
 >> +    if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
 >> +        /* Seconds don't care in Alarm comparison */
 >> +        tm->tm_sec = -1;
 >> +    } else {
 >> +        tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
 >> +                  STM32_RTC_ALRMXR_SEC_SHIFT;
 >> +    }
 >> +
 >> +    bcd2tm(tm);
 >> +
 >> +    alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
 >> +    alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0;
 >> +
 >> +    return 0;
 >> +}
 >> +
 >> +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned 
int enabled)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +    unsigned long irqflags;
 >> +    unsigned int isr, cr;
 >> +
 >> +    cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
 >
 > Is the STM32_RTC_CR garanteed to be valid, i.e updated atomically? 
If not this
 > should probably be below the spinlock.
You're right.
 >
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    stm32_rtc_wpr_unlock(rtc);
 >> +
 >> +    /* We expose Alarm A to the kernel */
 >> +    if (enabled)
 >> +        cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
 >> +    else
 >> +        cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
 >> +    stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
 >> +
 >> +    /* Clear event irqflags, otherwise new events won't be received */
 >> +    isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
 >> +    isr &= ~STM32_RTC_ISR_ALRAF;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
 >> +
 >> +    stm32_rtc_wpr_lock(rtc);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    return 0;
 >> +}
 >> +
 >> +static int stm32_rtc_set_alarm(struct device *dev, struct 
rtc_wkalrm *alrm)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +    struct rtc_time *tm = &alrm->time;
 >> +    unsigned long irqflags;
 >> +    unsigned int cr, isr, alrmar;
 >> +    int ret = 0;
 >> +
 >> +    if (rtc_valid_tm(tm)) {
 >> +        dev_err(dev, "Alarm time not valid.\n");
 >> +        return -EINVAL;
 >> +    }
 >> +
 >> +    tm2bcd(tm);
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    stm32_rtc_wpr_unlock(rtc);
 >> +
 >> +    /* Disable Alarm */
 >> +    cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
 >> +    cr &= ~STM32_RTC_CR_ALRAE;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
 >> +
 >> +    /* Poll Alarm write flag to be sure that Alarm update is allowed */
 >> +    ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
 >> +                        isr,
 >> +                        (isr & STM32_RTC_ISR_ALRAWF),
 >> +                        10, 100);
 >> +
 >> +    if (ret) {
 >> +        dev_err(dev, "Alarm update not allowed\n");
 >> +        goto end;
 >> +    }
 >> +
 >> +    alrmar = 0;
 >> +
 >> +    if (tm->tm_mday < 0 && tm->tm_wday < 0) {
 >> +        /*
 >> +         * Date/day don't care in Alarm comparison so alarm triggers
 >> +         * every day
 >> +         */
 >> +        alrmar |= STM32_RTC_ALRMXR_DATE_MASK;
 >> +    } else {
 >> +        if (tm->tm_mday > 0) {
 >> +            /* Date is selected (ignoring wday) */
 >> +            alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
 >> +                  STM32_RTC_ALRMXR_DATE;
 >> +        } else {
 >> +            /* Day of week is selected */
 >> +            int wday = (tm->tm_wday == 0) ? 7 : tm->tm_wday;
 >> +
 >> +            alrmar |= STM32_RTC_ALRMXR_WDSEL;
 >> +            alrmar |= (wday << STM32_RTC_ALRMXR_WDAY_SHIFT) &
 >> +                  STM32_RTC_ALRMXR_WDAY;
 >> +        }
 >> +    }
 >> +
 >> +    if (tm->tm_hour < 0) {
 >> +        /* Hours don't care in Alarm comparison */
 >> +        alrmar |= STM32_RTC_ALRMXR_HOUR_MASK;
 >> +    } else {
 >> +        /* 24-hour format */
 >> +        alrmar &= ~STM32_RTC_ALRMXR_PM;
 >> +        alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
 >> +              STM32_RTC_ALRMXR_HOUR;
 >> +    }
 >> +
 >> +    if (tm->tm_min < 0) {
 >> +        /* Minutes don't care in Alarm comparison */
 >> +        alrmar |= STM32_RTC_ALRMXR_MIN_MASK;
 >> +    } else {
 >> +        alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
 >> +              STM32_RTC_ALRMXR_MIN;
 >> +    }
 >> +
 >> +    if (tm->tm_sec < 0) {
 >> +        /* Seconds don't care in Alarm comparison */
 >> +        alrmar |= STM32_RTC_ALRMXR_SEC_MASK;
 >> +    } else {
 >> +        alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
 >> +              STM32_RTC_ALRMXR_SEC;
 >> +    }
 >> +
 >> +    /* Write to Alarm register */
 >> +    stm32_rtc_writel(rtc, STM32_RTC_ALRMAR, alrmar);
 >> +
 >> +    if (alrm->enabled)
 >> +        stm32_rtc_alarm_irq_enable(dev, 1);
 >> +    else
 >> +        stm32_rtc_alarm_irq_enable(dev, 0);
 >> +
 >> +end:
 >> +    stm32_rtc_wpr_lock(rtc);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    return ret;
 >> +}
 >> +
 >> +static const struct rtc_class_ops stm32_rtc_ops = {
 >> +    .read_time    = stm32_rtc_read_time,
 >> +    .set_time    = stm32_rtc_set_time,
 >> +    .read_alarm    = stm32_rtc_read_alarm,
 >> +    .set_alarm    = stm32_rtc_set_alarm,
 >> +    .alarm_irq_enable = stm32_rtc_alarm_irq_enable,
 >> +};
 >> +
 >> +#ifdef CONFIG_OF
 >> +static const struct of_device_id stm32_rtc_of_match[] = {
 >> +    { .compatible = "st,stm32-rtc" },
 >> +    {}
 >> +};
 >> +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
 >> +#endif
 >> +
 >> +static int stm32_rtc_init(struct platform_device *pdev,
 >> +              struct stm32_rtc *rtc)
 >> +{
 >> +    unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
 >> +    unsigned int rate;
 >> +    unsigned long irqflags;
 >> +    int ret = 0;
 >> +
 >> +    rate = clk_get_rate(rtc->ck_rtc);
 >> +
 >> +    /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
 >> +    pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
 >> +    pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
 >> +
 >> +    for (pred_a = pred_a_max; pred_a >= 0; pred_a--) {
 >> +        pred_s = (rate / (pred_a + 1)) - 1;
 >> +
 >> +        if (((pred_s + 1) * (pred_a + 1)) == rate)
 >> +            break;
 >> +    }
 >> +
 >> +    /*
 >> +     * Can't find a 1Hz, so give priority to RTC power consumption
 >> +     * by choosing the higher possible value for prediv_a
 >> +     */
 >> +    if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
 >> +        pred_a = pred_a_max;
 >> +        pred_s = (rate / (pred_a + 1)) - 1;
 >> +
 >> +        dev_warn(&pdev->dev, "ck_rtc is %s\n",
 >> +             (rate - ((pred_a + 1) * (pred_s + 1)) < 0) ?
 >> +             "fast" : "slow");
 >> +    }
 >> +
 >> +    spin_lock_irqsave(&rtc->lock, irqflags);
 >> +
 >> +    stm32_rtc_wpr_unlock(rtc);
 >> +
 >> +    ret = stm32_rtc_enter_init_mode(rtc);
 >> +    if (ret) {
 >> +        dev_err(&pdev->dev,
 >> +            "Can't enter in init mode. Prescaler config failed.\n");
 >> +        goto end;
 >> +    }
 >> +
 >> +    prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & 
STM32_RTC_PRER_PRED_S;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_PRER, prer);
 >> +    prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & 
STM32_RTC_PRER_PRED_A;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_PRER, prer);
 >> +
 >> +    /* Force 24h time format */
 >> +    cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
 >> +    cr &= ~STM32_RTC_CR_FMT;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
 >> +
 >> +    stm32_rtc_exit_init_mode(rtc);
 >> +
 >> +    ret = stm32_rtc_wait_sync(rtc);
 >> +
 >> +    if (stm32_rtc_readl(rtc, STM32_RTC_ISR) & STM32_RTC_ISR_INITS)
 >> +        dev_warn(&pdev->dev, "Date/Time must be initialized\n");
 >> +end:
 >> +    stm32_rtc_wpr_lock(rtc);
 >> +
 >> +    spin_unlock_irqrestore(&rtc->lock, irqflags);
 >> +
 >> +    return ret;
 >> +}
 >> +
 >> +static int stm32_rtc_probe(struct platform_device *pdev)
 >> +{
 >> +    struct stm32_rtc *rtc;
 >> +    struct resource *res;
 >> +    int ret;
 >> +
 >> +    rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
 >> +    if (!rtc)
 >> +        return -ENOMEM;
 >> +
 >> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 >
 > The value of 'res' should be checked before using it.
res is checked in devm_ioremap_resource just below :
     if (!res || resource_type(res) != IORESOURCE_MEM) {
         dev_err(dev, "invalid resource\n");
         return IOMEM_ERR_PTR(-EINVAL);
     }
That's why it is not checked here.
 >
 >> +    rtc->base = devm_ioremap_resource(&pdev->dev, res);
 >> +    if (IS_ERR(rtc->base))
 >> +        return PTR_ERR(rtc->base);
 >> +
 >> +    dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 
"st,syscfg");
 >> +    if (IS_ERR(dbp)) {
 >> +        dev_err(&pdev->dev, "no st,syscfg\n");
 >> +        return PTR_ERR(dbp);
 >> +    }
 >> +
 >> +    spin_lock_init(&rtc->lock);
 >> +
 >> +    rtc->ck_rtc = devm_clk_get(&pdev->dev, "ck_rtc");
 >> +    if (IS_ERR(rtc->ck_rtc)) {
 >> +        dev_err(&pdev->dev, "no ck_rtc clock");
 >> +        return PTR_ERR(rtc->ck_rtc);
 >> +    }
 >> +
 >> +    ret = clk_prepare_enable(rtc->ck_rtc);
 >> +    if (ret)
 >> +        return ret;
 >> +
 >> +    if (dbp)
 >> +        regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
 >
 > The code above exits if there is a problem with the dbp, there is no 
point in
 > checking again.
You're right.
 >
 >> +
 >> +    ret = stm32_rtc_init(pdev, rtc);
 >> +    if (ret)
 >> +        goto err;
 >> +
 >> +    rtc->irq_alarm = platform_get_irq_byname(pdev, "alarm");
 >> +    if (rtc->irq_alarm <= 0) {
 >> +        dev_err(&pdev->dev, "no alarm irq\n");
 >> +        ret = -ENOENT;
 >> +        goto err;
 >> +    }
 >> +
 >> +    platform_set_drvdata(pdev, rtc);
 >> +
 >> +    device_init_wakeup(&pdev->dev, true);
 >
 > What happens if device_init_wakeup() returns an error?
It means that RTC won't be able to wake up the board with RTC alarm. I 
can add a warning for the user in this case ?
 >
 >> +
 >> +    rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
 >> +            &stm32_rtc_ops, THIS_MODULE);
 >> +    if (IS_ERR(rtc->rtc_dev)) {
 >> +        ret = PTR_ERR(rtc->rtc_dev);
 >> +        dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
 >> +            ret);
 >> +        goto err;
 >> +    }
 >> +
 >> +    /* Handle RTC alarm interrupts */
 >> +    ret = devm_request_irq(&pdev->dev, rtc->irq_alarm,
 >> +                   stm32_rtc_alarm_irq, IRQF_TRIGGER_RISING,
 >> +                   dev_name(&rtc->rtc_dev->dev), rtc);
 >> +    if (ret) {
 >> +        dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already 
claimed\n",
 >> +            rtc->irq_alarm);
 >> +        goto err;
 >> +    }
 >> +
 >> +    return 0;
 >> +err:
 >> +    clk_disable_unprepare(rtc->ck_rtc);
 >> +
 >> +    if (dbp)
 >> +        regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
 >
 > Same comment as above.
OK.
 >
 >> +
 >> +    device_init_wakeup(&pdev->dev, false);
 >> +
 >> +    return ret;
 >> +}
 >> +
 >> +static int __exit stm32_rtc_remove(struct platform_device *pdev)
 >> +{
 >> +    struct stm32_rtc *rtc = platform_get_drvdata(pdev);
 >> +    unsigned int cr;
 >> +
 >> +    /* Disable interrupts */
 >> +    stm32_rtc_wpr_unlock(rtc);
 >> +    cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
 >> +    cr &= ~STM32_RTC_CR_ALRAIE;
 >> +    stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
 >> +    stm32_rtc_wpr_lock(rtc);
 >> +
 >> +    clk_disable_unprepare(rtc->ck_rtc);
 >> +
 >> +    /* Enable backup domain write protection */
 >> +    if (dbp)
 >> +        regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
 >> +
 >> +    device_init_wakeup(&pdev->dev, false);
 >> +
 >> +    return 0;
 >> +}
 >> +
 >> +#ifdef CONFIG_PM_SLEEP
 >> +static int stm32_rtc_suspend(struct device *dev)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +
 >> +    if (device_may_wakeup(dev))
 >> +        return enable_irq_wake(rtc->irq_alarm);
 >> +
 >> +    return 0;
 >> +}
 >> +
 >> +static int stm32_rtc_resume(struct device *dev)
 >> +{
 >> +    struct stm32_rtc *rtc = dev_get_drvdata(dev);
 >> +    int ret = 0;
 >> +
 >> +    ret = stm32_rtc_wait_sync(rtc);
 >> +    if (ret < 0)
 >> +        return ret;
 >> +
 >> +    if (device_may_wakeup(dev))
 >> +        return disable_irq_wake(rtc->irq_alarm);
 >> +
 >> +    return ret;
 >> +}
 >> +#endif
 >> +
 >> +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops,
 >> +             stm32_rtc_suspend, stm32_rtc_resume);
 >> +
 >> +static struct platform_driver stm32_rtc_driver = {
 >> +    .probe        = stm32_rtc_probe,
 >> +    .remove        = stm32_rtc_remove,
 >> +    .driver        = {
 >> +        .name    = DRIVER_NAME,
 >> +        .pm    = &stm32_rtc_pm_ops,
 >> +        .of_match_table = stm32_rtc_of_match,
 >> +    },
 >> +};
 >> +
 >> +module_platform_driver(stm32_rtc_driver);
 >> +
 >> +MODULE_ALIAS("platform:" DRIVER_NAME);
 >> +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
 >> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
 >> +MODULE_LICENSE("GPL v2");
 >> --
 >> 1.9.1
 >>

Best regards,
Amelie

^ permalink raw reply

* [PATCH 3/8] rtc: add STM32 RTC driver
From: Amelie DELAUNAY @ 2016-12-05  9:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161202180527.GB3284@linaro.org>

On 12/02/2016 07:05 PM, Mathieu Poirier wrote:
> On Fri, Dec 02, 2016 at 03:09:56PM +0100, Amelie Delaunay wrote:
>> This patch adds support for the STM32 RTC.
>>
>> Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
>> ---
>>  drivers/rtc/Kconfig     |  10 +
>>  drivers/rtc/Makefile    |   1 +
>>  drivers/rtc/rtc-stm32.c | 777 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 788 insertions(+)
>>  create mode 100644 drivers/rtc/rtc-stm32.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index e859d14..dd8b218 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -1706,6 +1706,16 @@ config RTC_DRV_PIC32
>>  	   This driver can also be built as a module. If so, the module
>>  	   will be called rtc-pic32
>>
>> +config RTC_DRV_STM32
>> +	tristate "STM32 On-Chip RTC"
>> +	depends on ARCH_STM32
>> +	help
>> +	   If you say yes here you get support for the STM32 On-Chip
>> +	   Real Time Clock.
>> +
>> +	   This driver can also be built as a module, if so, the module
>> +	   will be called "rtc-stm32".
>> +
>>  comment "HID Sensor RTC drivers"
>>
>>  config RTC_DRV_HID_SENSOR_TIME
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 1ac694a..87bd9cc 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -144,6 +144,7 @@ obj-$(CONFIG_RTC_DRV_SNVS)	+= rtc-snvs.o
>>  obj-$(CONFIG_RTC_DRV_SPEAR)	+= rtc-spear.o
>>  obj-$(CONFIG_RTC_DRV_STARFIRE)	+= rtc-starfire.o
>>  obj-$(CONFIG_RTC_DRV_STK17TA8)	+= rtc-stk17ta8.o
>> +obj-$(CONFIG_RTC_DRV_STM32) 	+= rtc-stm32.o
>>  obj-$(CONFIG_RTC_DRV_STMP)	+= rtc-stmp3xxx.o
>>  obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
>>  obj-$(CONFIG_RTC_DRV_SUN4V)	+= rtc-sun4v.o
>> diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
>> new file mode 100644
>> index 0000000..9e710ff
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-stm32.c
>> @@ -0,0 +1,777 @@
>> +/*
>> + * Copyright (C) Amelie Delaunay 2015
>> + * Author:  Amelie Delaunay <adelaunay.stm32@gmail.com>
>> + * License terms:  GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/bcd.h>
>> +#include <linux/clk.h>
>> +#include <linux/init.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/ioport.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/rtc.h>
>> +#include <linux/spinlock.h>
>> +
>> +#define DRIVER_NAME "stm32_rtc"
>> +
>> +/* STM32 RTC registers */
>> +#define STM32_RTC_TR		0x00
>> +#define STM32_RTC_DR		0x04
>> +#define STM32_RTC_CR		0x08
>> +#define STM32_RTC_ISR		0x0C
>> +#define STM32_RTC_PRER		0x10
>> +#define STM32_RTC_ALRMAR	0x1C
>> +#define STM32_RTC_WPR		0x24
>> +
>> +/* STM32_RTC_TR bit fields  */
>> +#define STM32_RTC_TR_SEC_SHIFT		0
>> +#define STM32_RTC_TR_SEC		GENMASK(6, 0)
>> +#define STM32_RTC_TR_MIN_SHIFT		8
>> +#define STM32_RTC_TR_MIN		GENMASK(14, 8)
>> +#define STM32_RTC_TR_HOUR_SHIFT		16
>> +#define STM32_RTC_TR_HOUR		GENMASK(21, 16)
>> +
>> +/* STM32_RTC_DR bit fields */
>> +#define STM32_RTC_DR_DATE_SHIFT		0
>> +#define STM32_RTC_DR_DATE		GENMASK(5, 0)
>> +#define STM32_RTC_DR_MONTH_SHIFT	8
>> +#define STM32_RTC_DR_MONTH		GENMASK(11, 8)
>> +#define STM32_RTC_DR_WDAY_SHIFT		13
>> +#define STM32_RTC_DR_WDAY		GENMASK(15, 13)
>> +#define STM32_RTC_DR_YEAR_SHIFT		16
>> +#define STM32_RTC_DR_YEAR		GENMASK(23, 16)
>> +
>> +/* STM32_RTC_CR bit fields */
>> +#define STM32_RTC_CR_FMT		BIT(6)
>> +#define STM32_RTC_CR_ALRAE		BIT(8)
>> +#define STM32_RTC_CR_ALRAIE		BIT(12)
>> +
>> +/* STM32_RTC_ISR bit fields */
>> +#define STM32_RTC_ISR_ALRAWF		BIT(0)
>> +#define STM32_RTC_ISR_INITS		BIT(4)
>> +#define STM32_RTC_ISR_RSF		BIT(5)
>> +#define STM32_RTC_ISR_INITF		BIT(6)
>> +#define STM32_RTC_ISR_INIT		BIT(7)
>> +#define STM32_RTC_ISR_ALRAF		BIT(8)
>> +
>> +/* STM32_RTC_PRER bit fields */
>> +#define STM32_RTC_PRER_PRED_S_SHIFT	0
>> +#define STM32_RTC_PRER_PRED_S		GENMASK(14, 0)
>> +#define STM32_RTC_PRER_PRED_A_SHIFT	16
>> +#define STM32_RTC_PRER_PRED_A		GENMASK(22, 16)
>> +
>> +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
>> +#define STM32_RTC_ALRMXR_SEC_SHIFT	0
>> +#define STM32_RTC_ALRMXR_SEC		GENMASK(6, 0)
>> +#define STM32_RTC_ALRMXR_SEC_MASK	BIT(7)
>> +#define STM32_RTC_ALRMXR_MIN_SHIFT	8
>> +#define STM32_RTC_ALRMXR_MIN		GENMASK(14, 8)
>> +#define STM32_RTC_ALRMXR_MIN_MASK	BIT(15)
>> +#define STM32_RTC_ALRMXR_HOUR_SHIFT	16
>> +#define STM32_RTC_ALRMXR_HOUR		GENMASK(21, 16)
>> +#define STM32_RTC_ALRMXR_PM		BIT(22)
>> +#define STM32_RTC_ALRMXR_HOUR_MASK	BIT(23)
>> +#define STM32_RTC_ALRMXR_DATE_SHIFT	24
>> +#define STM32_RTC_ALRMXR_DATE		GENMASK(29, 24)
>> +#define STM32_RTC_ALRMXR_WDSEL		BIT(30)
>> +#define STM32_RTC_ALRMXR_WDAY_SHIFT	24
>> +#define STM32_RTC_ALRMXR_WDAY		GENMASK(27, 24)
>> +#define STM32_RTC_ALRMXR_DATE_MASK	BIT(31)
>> +
>> +/* STM32_RTC_WPR key constants */
>> +#define RTC_WPR_1ST_KEY			0xCA
>> +#define RTC_WPR_2ND_KEY			0x53
>> +#define RTC_WPR_WRONG_KEY		0xFF
>> +
>> +/*
>> + * RTC registers are protected agains parasitic write access.
>> + * PWR_CR_DBP bit must be set to enable write access to RTC registers.
>> + */
>> +/* STM32_PWR_CR */
>> +#define PWR_CR				0x00
>> +/* STM32_PWR_CR bit field */
>> +#define PWR_CR_DBP			BIT(8)
>> +
>> +static struct regmap *dbp;
>> +
>> +struct stm32_rtc {
>> +	struct rtc_device *rtc_dev;
>> +	void __iomem *base;
>> +	struct clk *pclk;
>> +	struct clk *ck_rtc;
>> +	unsigned int clksrc;
>> +	spinlock_t lock; /* Protects registers accesses */
>> +	int irq_alarm;
>> +	struct regmap *pwrcr;
>> +};
>
> One more thing... What did you want to do with pclk, clksrc and pwrcr?  They
> aren't used in the driver.  If this is for future enhancement I suggest you
> introduce those when you submit the patches.
You're right.
>
>> +
>> +static inline unsigned int stm32_rtc_readl(struct stm32_rtc *rtc,
>> +					   unsigned int offset)
>> +{
>> +	return readl_relaxed(rtc->base + offset);
>> +}
>> +
>> +static inline void stm32_rtc_writel(struct stm32_rtc *rtc,
>> +				    unsigned int offset, unsigned int value)
>> +{
>> +	writel_relaxed(value, rtc->base + offset);
>> +}
>> +
>> +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
>> +{
>> +//	if (dbp)
>> +//		regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
>> +
>> +	stm32_rtc_writel(rtc, STM32_RTC_WPR, RTC_WPR_1ST_KEY);
>> +	stm32_rtc_writel(rtc, STM32_RTC_WPR, RTC_WPR_2ND_KEY);
>> +}
>> +
>> +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
>> +{
>> +	stm32_rtc_writel(rtc, STM32_RTC_WPR, RTC_WPR_WRONG_KEY);
>> +
>> +//	if (dbp)
>> +//		regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
>> +}
>> +
>> +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
>> +{
>> +	unsigned int isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
>> +
>> +	if (!(isr & STM32_RTC_ISR_INITF)) {
>> +		isr |= STM32_RTC_ISR_INIT;
>> +		stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
>> +
>> +		return readl_relaxed_poll_timeout_atomic(
>> +					rtc->base + STM32_RTC_ISR,
>> +					isr, (isr & STM32_RTC_ISR_INITF),
>> +					10, 100000);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
>> +{
>> +	unsigned int isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
>> +
>> +	isr &= ~STM32_RTC_ISR_INIT;
>> +	stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
>> +}
>> +
>> +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
>> +{
>> +	unsigned int isr;
>> +
>> +	isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
>> +
>> +	isr &= ~STM32_RTC_ISR_RSF;
>> +	stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
>> +
>> +	/* Wait the registers to be synchronised */
>> +	return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
>> +						 isr,
>> +						 (isr & STM32_RTC_ISR_RSF),
>> +						 10, 100000);
>> +}
>> +
>> +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
>> +{
>> +	struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
>> +	unsigned long irqflags, events = 0;
>> +	unsigned int isr, cr;
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
>> +	cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
>> +
>> +	if ((isr & STM32_RTC_ISR_ALRAF) &&
>> +	    (cr & STM32_RTC_CR_ALRAIE)) {
>> +		/* Alarm A flag - Alarm interrupt */
>> +		events |= RTC_IRQF | RTC_AF;
>> +		isr &= ~STM32_RTC_ISR_ALRAF;
>> +	}
>> +
>> +	/* Clear event irqflags, otherwise new events won't be received */
>> +	stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	if (events) {
>> +		dev_info(&rtc->rtc_dev->dev, "Alarm occurred\n");
>> +
>> +		/* Pass event to the kernel */
>> +		rtc_update_irq(rtc->rtc_dev, 1, events);
>> +		return IRQ_HANDLED;
>> +	} else {
>> +		return IRQ_NONE;
>> +	}
>> +}
>> +
>> +/* Convert rtc_time structure from bin to bcd format */
>> +static void tm2bcd(struct rtc_time *tm)
>> +{
>> +	tm->tm_sec = bin2bcd(tm->tm_sec);
>> +	tm->tm_min = bin2bcd(tm->tm_min);
>> +	tm->tm_hour = bin2bcd(tm->tm_hour);
>> +
>> +	tm->tm_mday = bin2bcd(tm->tm_mday);
>> +	tm->tm_mon = bin2bcd(tm->tm_mon + 1);
>> +	tm->tm_year = bin2bcd(tm->tm_year - 100);
>> +	/*
>> +	 * Number of days since Sunday
>> +	 * - on kernel side, 0=Sunday...6=Saturday
>> +	 * - on rtc side, 0=invalid,1=Monday...7=Sunday
>> +	 */
>> +	tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
>> +}
>> +
>> +/* Convert rtc_time structure from bcd to bin format */
>> +static void bcd2tm(struct rtc_time *tm)
>> +{
>> +	tm->tm_sec = bcd2bin(tm->tm_sec);
>> +	tm->tm_min = bcd2bin(tm->tm_min);
>> +	tm->tm_hour = bcd2bin(tm->tm_hour);
>> +
>> +	tm->tm_mday = bcd2bin(tm->tm_mday);
>> +	tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
>> +	tm->tm_year = bcd2bin(tm->tm_year) + 100;
>> +	/*
>> +	 * Number of days since Sunday
>> +	 * - on kernel side, 0=Sunday...6=Saturday
>> +	 * - on rtc side, 0=invalid,1=Monday...7=Sunday
>> +	 */
>> +	tm->tm_wday %= 7;
>> +}
>> +
>> +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +	unsigned int tr, dr;
>> +	unsigned long irqflags;
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	/* Time and Date in BCD format */
>> +	tr = stm32_rtc_readl(rtc, STM32_RTC_TR);
>> +	dr = stm32_rtc_readl(rtc, STM32_RTC_DR);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
>> +	tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
>> +	tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
>> +
>> +	tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
>> +	tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
>> +	tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
>> +	tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
>> +
>> +	/* We don't report tm_yday and tm_isdst */
>> +
>> +	bcd2tm(tm);
>> +
>> +	if (rtc_valid_tm(tm) < 0) {
>> +		dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +	unsigned int tr, dr;
>> +	unsigned long irqflags;
>> +	int ret = 0;
>> +
>> +	if (rtc_valid_tm(tm) < 0) {
>> +		dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	tm2bcd(tm);
>> +
>> +	/* Time in BCD format */
>> +	tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
>> +	     ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
>> +	     ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR);
>> +
>> +	/* Date in BCD format */
>> +	dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) |
>> +	     ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) |
>> +	     ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) |
>> +	     ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY);
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	stm32_rtc_wpr_unlock(rtc);
>> +
>> +	ret = stm32_rtc_enter_init_mode(rtc);
>> +	if (ret) {
>> +		dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
>> +		goto end;
>> +	}
>> +
>> +	stm32_rtc_writel(rtc, STM32_RTC_TR, tr);
>> +	stm32_rtc_writel(rtc, STM32_RTC_DR, dr);
>> +
>> +	stm32_rtc_exit_init_mode(rtc);
>> +
>> +	ret = stm32_rtc_wait_sync(rtc);
>> +end:
>> +	stm32_rtc_wpr_lock(rtc);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +	struct rtc_time *tm = &alrm->time;
>> +	unsigned int alrmar, cr, isr;
>> +	unsigned long irqflags;
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	alrmar = stm32_rtc_readl(rtc, STM32_RTC_ALRMAR);
>> +	cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
>> +	isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
>> +		/*
>> +		 * Date/day don't care in Alarm comparison so alarm triggers
>> +		 * every day
>> +		 */
>> +		tm->tm_mday = -1;
>> +		tm->tm_wday = -1;
>> +	} else {
>> +		if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
>> +			/* Alarm is set to a day of week */
>> +			tm->tm_mday = -1;
>> +			tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
>> +				      STM32_RTC_ALRMXR_WDAY_SHIFT;
>> +			tm->tm_wday %= 7;
>> +		} else {
>> +			/* Alarm is set to a day of month */
>> +			tm->tm_wday = -1;
>> +			tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
>> +				       STM32_RTC_ALRMXR_DATE_SHIFT;
>> +		}
>> +	}
>> +
>> +	if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
>> +		/* Hours don't care in Alarm comparison */
>> +		tm->tm_hour = -1;
>> +	} else {
>> +		tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
>> +			       STM32_RTC_ALRMXR_HOUR_SHIFT;
>> +		if (alrmar & STM32_RTC_ALRMXR_PM)
>> +			tm->tm_hour += 12;
>> +	}
>> +
>> +	if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
>> +		/* Minutes don't care in Alarm comparison */
>> +		tm->tm_min = -1;
>> +	} else {
>> +		tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
>> +			      STM32_RTC_ALRMXR_MIN_SHIFT;
>> +	}
>> +
>> +	if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
>> +		/* Seconds don't care in Alarm comparison */
>> +		tm->tm_sec = -1;
>> +	} else {
>> +		tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
>> +			      STM32_RTC_ALRMXR_SEC_SHIFT;
>> +	}
>> +
>> +	bcd2tm(tm);
>> +
>> +	alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
>> +	alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0;
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +	unsigned long irqflags;
>> +	unsigned int isr, cr;
>> +
>> +	cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	stm32_rtc_wpr_unlock(rtc);
>> +
>> +	/* We expose Alarm A to the kernel */
>> +	if (enabled)
>> +		cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
>> +	else
>> +		cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
>> +	stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
>> +
>> +	/* Clear event irqflags, otherwise new events won't be received */
>> +	isr = stm32_rtc_readl(rtc, STM32_RTC_ISR);
>> +	isr &= ~STM32_RTC_ISR_ALRAF;
>> +	stm32_rtc_writel(rtc, STM32_RTC_ISR, isr);
>> +
>> +	stm32_rtc_wpr_lock(rtc);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +	struct rtc_time *tm = &alrm->time;
>> +	unsigned long irqflags;
>> +	unsigned int cr, isr, alrmar;
>> +	int ret = 0;
>> +
>> +	if (rtc_valid_tm(tm)) {
>> +		dev_err(dev, "Alarm time not valid.\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	tm2bcd(tm);
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	stm32_rtc_wpr_unlock(rtc);
>> +
>> +	/* Disable Alarm */
>> +	cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
>> +	cr &= ~STM32_RTC_CR_ALRAE;
>> +	stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
>> +
>> +	/* Poll Alarm write flag to be sure that Alarm update is allowed */
>> +	ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
>> +						isr,
>> +						(isr & STM32_RTC_ISR_ALRAWF),
>> +						10, 100);
>> +
>> +	if (ret) {
>> +		dev_err(dev, "Alarm update not allowed\n");
>> +		goto end;
>> +	}
>> +
>> +	alrmar = 0;
>> +
>> +	if (tm->tm_mday < 0 && tm->tm_wday < 0) {
>> +		/*
>> +		 * Date/day don't care in Alarm comparison so alarm triggers
>> +		 * every day
>> +		 */
>> +		alrmar |= STM32_RTC_ALRMXR_DATE_MASK;
>> +	} else {
>> +		if (tm->tm_mday > 0) {
>> +			/* Date is selected (ignoring wday) */
>> +			alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
>> +				  STM32_RTC_ALRMXR_DATE;
>> +		} else {
>> +			/* Day of week is selected */
>> +			int wday = (tm->tm_wday == 0) ? 7 : tm->tm_wday;
>> +
>> +			alrmar |= STM32_RTC_ALRMXR_WDSEL;
>> +			alrmar |= (wday << STM32_RTC_ALRMXR_WDAY_SHIFT) &
>> +				  STM32_RTC_ALRMXR_WDAY;
>> +		}
>> +	}
>> +
>> +	if (tm->tm_hour < 0) {
>> +		/* Hours don't care in Alarm comparison */
>> +		alrmar |= STM32_RTC_ALRMXR_HOUR_MASK;
>> +	} else {
>> +		/* 24-hour format */
>> +		alrmar &= ~STM32_RTC_ALRMXR_PM;
>> +		alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
>> +			  STM32_RTC_ALRMXR_HOUR;
>> +	}
>> +
>> +	if (tm->tm_min < 0) {
>> +		/* Minutes don't care in Alarm comparison */
>> +		alrmar |= STM32_RTC_ALRMXR_MIN_MASK;
>> +	} else {
>> +		alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
>> +			  STM32_RTC_ALRMXR_MIN;
>> +	}
>> +
>> +	if (tm->tm_sec < 0) {
>> +		/* Seconds don't care in Alarm comparison */
>> +		alrmar |= STM32_RTC_ALRMXR_SEC_MASK;
>> +	} else {
>> +		alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
>> +			  STM32_RTC_ALRMXR_SEC;
>> +	}
>> +
>> +	/* Write to Alarm register */
>> +	stm32_rtc_writel(rtc, STM32_RTC_ALRMAR, alrmar);
>> +
>> +	if (alrm->enabled)
>> +		stm32_rtc_alarm_irq_enable(dev, 1);
>> +	else
>> +		stm32_rtc_alarm_irq_enable(dev, 0);
>> +
>> +end:
>> +	stm32_rtc_wpr_lock(rtc);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct rtc_class_ops stm32_rtc_ops = {
>> +	.read_time	= stm32_rtc_read_time,
>> +	.set_time	= stm32_rtc_set_time,
>> +	.read_alarm	= stm32_rtc_read_alarm,
>> +	.set_alarm	= stm32_rtc_set_alarm,
>> +	.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
>> +};
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id stm32_rtc_of_match[] = {
>> +	{ .compatible = "st,stm32-rtc" },
>> +	{}
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
>> +#endif
>> +
>> +static int stm32_rtc_init(struct platform_device *pdev,
>> +			  struct stm32_rtc *rtc)
>> +{
>> +	unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
>> +	unsigned int rate;
>> +	unsigned long irqflags;
>> +	int ret = 0;
>> +
>> +	rate = clk_get_rate(rtc->ck_rtc);
>> +
>> +	/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
>> +	pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
>> +	pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
>> +
>> +	for (pred_a = pred_a_max; pred_a >= 0; pred_a--) {
>> +		pred_s = (rate / (pred_a + 1)) - 1;
>> +
>> +		if (((pred_s + 1) * (pred_a + 1)) == rate)
>> +			break;
>> +	}
>> +
>> +	/*
>> +	 * Can't find a 1Hz, so give priority to RTC power consumption
>> +	 * by choosing the higher possible value for prediv_a
>> +	 */
>> +	if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
>> +		pred_a = pred_a_max;
>> +		pred_s = (rate / (pred_a + 1)) - 1;
>> +
>> +		dev_warn(&pdev->dev, "ck_rtc is %s\n",
>> +			 (rate - ((pred_a + 1) * (pred_s + 1)) < 0) ?
>> +			 "fast" : "slow");
>> +	}
>> +
>> +	spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> +	stm32_rtc_wpr_unlock(rtc);
>> +
>> +	ret = stm32_rtc_enter_init_mode(rtc);
>> +	if (ret) {
>> +		dev_err(&pdev->dev,
>> +			"Can't enter in init mode. Prescaler config failed.\n");
>> +		goto end;
>> +	}
>> +
>> +	prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
>> +	stm32_rtc_writel(rtc, STM32_RTC_PRER, prer);
>> +	prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
>> +	stm32_rtc_writel(rtc, STM32_RTC_PRER, prer);
>> +
>> +	/* Force 24h time format */
>> +	cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
>> +	cr &= ~STM32_RTC_CR_FMT;
>> +	stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
>> +
>> +	stm32_rtc_exit_init_mode(rtc);
>> +
>> +	ret = stm32_rtc_wait_sync(rtc);
>> +
>> +	if (stm32_rtc_readl(rtc, STM32_RTC_ISR) & STM32_RTC_ISR_INITS)
>> +		dev_warn(&pdev->dev, "Date/Time must be initialized\n");
>> +end:
>> +	stm32_rtc_wpr_lock(rtc);
>> +
>> +	spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_rtc_probe(struct platform_device *pdev)
>> +{
>> +	struct stm32_rtc *rtc;
>> +	struct resource *res;
>> +	int ret;
>> +
>> +	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
>> +	if (!rtc)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	rtc->base = devm_ioremap_resource(&pdev->dev, res);
>> +	if (IS_ERR(rtc->base))
>> +		return PTR_ERR(rtc->base);
>> +
>> +	dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "st,syscfg");
>> +	if (IS_ERR(dbp)) {
>> +		dev_err(&pdev->dev, "no st,syscfg\n");
>> +		return PTR_ERR(dbp);
>> +	}
>> +
>> +	spin_lock_init(&rtc->lock);
>> +
>> +	rtc->ck_rtc = devm_clk_get(&pdev->dev, "ck_rtc");
>> +	if (IS_ERR(rtc->ck_rtc)) {
>> +		dev_err(&pdev->dev, "no ck_rtc clock");
>> +		return PTR_ERR(rtc->ck_rtc);
>> +	}
>> +
>> +	ret = clk_prepare_enable(rtc->ck_rtc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (dbp)
>> +		regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
>> +
>> +	ret = stm32_rtc_init(pdev, rtc);
>> +	if (ret)
>> +		goto err;
>> +
>> +	rtc->irq_alarm = platform_get_irq_byname(pdev, "alarm");
>> +	if (rtc->irq_alarm <= 0) {
>> +		dev_err(&pdev->dev, "no alarm irq\n");
>> +		ret = -ENOENT;
>> +		goto err;
>> +	}
>> +
>> +	platform_set_drvdata(pdev, rtc);
>> +
>> +	device_init_wakeup(&pdev->dev, true);
>> +
>> +	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
>> +			&stm32_rtc_ops, THIS_MODULE);
>> +	if (IS_ERR(rtc->rtc_dev)) {
>> +		ret = PTR_ERR(rtc->rtc_dev);
>> +		dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
>> +			ret);
>> +		goto err;
>> +	}
>> +
>> +	/* Handle RTC alarm interrupts */
>> +	ret = devm_request_irq(&pdev->dev, rtc->irq_alarm,
>> +			       stm32_rtc_alarm_irq, IRQF_TRIGGER_RISING,
>> +			       dev_name(&rtc->rtc_dev->dev), rtc);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
>> +			rtc->irq_alarm);
>> +		goto err;
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	clk_disable_unprepare(rtc->ck_rtc);
>> +
>> +	if (dbp)
>> +		regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
>> +
>> +	device_init_wakeup(&pdev->dev, false);
>> +
>> +	return ret;
>> +}
>> +
>> +static int __exit stm32_rtc_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_rtc *rtc = platform_get_drvdata(pdev);
>> +	unsigned int cr;
>> +
>> +	/* Disable interrupts */
>> +	stm32_rtc_wpr_unlock(rtc);
>> +	cr = stm32_rtc_readl(rtc, STM32_RTC_CR);
>> +	cr &= ~STM32_RTC_CR_ALRAIE;
>> +	stm32_rtc_writel(rtc, STM32_RTC_CR, cr);
>> +	stm32_rtc_wpr_lock(rtc);
>> +
>> +	clk_disable_unprepare(rtc->ck_rtc);
>> +
>> +	/* Enable backup domain write protection */
>> +	if (dbp)
>> +		regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
>> +
>> +	device_init_wakeup(&pdev->dev, false);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int stm32_rtc_suspend(struct device *dev)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +
>> +	if (device_may_wakeup(dev))
>> +		return enable_irq_wake(rtc->irq_alarm);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_rtc_resume(struct device *dev)
>> +{
>> +	struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +	int ret = 0;
>> +
>> +	ret = stm32_rtc_wait_sync(rtc);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if (device_may_wakeup(dev))
>> +		return disable_irq_wake(rtc->irq_alarm);
>> +
>> +	return ret;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops,
>> +			 stm32_rtc_suspend, stm32_rtc_resume);
>> +
>> +static struct platform_driver stm32_rtc_driver = {
>> +	.probe		= stm32_rtc_probe,
>> +	.remove		= stm32_rtc_remove,
>> +	.driver		= {
>> +		.name	= DRIVER_NAME,
>> +		.pm	= &stm32_rtc_pm_ops,
>> +		.of_match_table = stm32_rtc_of_match,
>> +	},
>> +};
>> +
>> +module_platform_driver(stm32_rtc_driver);
>> +
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 1.9.1
>>

Best regards,
Amelie

^ permalink raw reply

* [PATCH v9 07/16] drivers: acpi: implement acpi_dma_configure
From: Sricharan @ 2016-12-05  9:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJZ5v0j5aMMyNsYpnbGXbJdDrbHFN5u03mQi+W6WK97iCdB2HA@mail.gmail.com>

Hi Lorenzo,

>
>On Sat, Dec 3, 2016 at 11:39 AM, Lorenzo Pieralisi
><lorenzo.pieralisi@arm.com> wrote:
>> On Sat, Dec 03, 2016 at 03:11:09AM +0100, Rafael J. Wysocki wrote:
>>> On Fri, Dec 2, 2016 at 4:38 PM, Lorenzo Pieralisi
>>> <lorenzo.pieralisi@arm.com> wrote:
>>> > Rafael, Mark, Suravee,
>>> >
>>> > On Mon, Nov 21, 2016 at 10:01:39AM +0000, Lorenzo Pieralisi wrote:
>>> >> On DT based systems, the of_dma_configure() API implements DMA
>>> >> configuration for a given device. On ACPI systems an API equivalent to
>>> >> of_dma_configure() is missing which implies that it is currently not
>>> >> possible to set-up DMA operations for devices through the ACPI generic
>>> >> kernel layer.
>>> >>
>>> >> This patch fills the gap by introducing acpi_dma_configure/deconfigure()
>>> >> calls that for now are just wrappers around arch_setup_dma_ops() and
>>> >> arch_teardown_dma_ops() and also updates ACPI and PCI core code to use
>>> >> the newly introduced acpi_dma_configure/acpi_dma_deconfigure functions.
>>> >>
>>> >> Since acpi_dma_configure() is used to configure DMA operations, the
>>> >> function initializes the dma/coherent_dma masks to sane default values
>>> >> if the current masks are uninitialized (also to keep the default values
>>> >> consistent with DT systems) to make sure the device has a complete
>>> >> default DMA set-up.
>>> >
>>> > I spotted a niggle that unfortunately was hard to spot (and should not
>>> > be a problem per se but better safe than sorry) and I am not comfortable
>>> > with it.
>>> >
>>> > Following commit d0562674838c ("ACPI / scan: Parse _CCA and setup
>>> > device coherency") in acpi_bind_one() we check if the acpi_device
>>> > associated with a device just added supports DMA, first it was
>>> > done with acpi_check_dma() and then commit 1831eff876bd ("device
>>> > property: ACPI: Make use of the new DMA Attribute APIs") changed
>>> > it to acpi_get_dma_attr().
>>> >
>>> > The subsequent check (attr != DEV_DMA_NOT_SUPPORTED) is always true
>>> > on _any_ acpi device we pass to acpi_bind_one() on x86, which was
>>> > fine because we used it to call arch_setup_dma_ops(), which is a nop
>>> > on x86. On ARM64 a _CCA method is required to define if a device
>>> > supports DMA so (attr != DEV_DMA_NOT_SUPPORTED) may well be false.
>>> >
>>> > Now, acpi_bind_one() is used to bind an acpi_device to its physical
>>> > node also for pseudo-devices like cpus and memory nodes. For those
>>> > objects, on x86, attr will always be != DEV_DMA_NOT_SUPPORTED.
>>> >
>>> > So far so good, because on x86 arch_setup_dma_ops() is empty code.
>>> >
>>> > With this patch, I use the (attr != DEV_DMA_NOT_SUPPORTED) check
>>> > to call acpi_dma_configure() which is basically a nop on x86 except
>>> > that it sets up the dma_mask/coherent_dma_mask to a sane default value
>>> > (after all we are setting up DMA for the device so it makes sense to
>>> > initialize the masks there if they were unset since we are configuring
>>> > DMA for the device in question) for the given device.
>>> >
>>> > Problem is, as per the explanation above, we are also setting the
>>> > default dma masks for pseudo-devices (eg CPUs) that were previously
>>> > untouched, it should not be a problem per-se but I am not comfortable
>>> > with that, honestly it does not make much sense.
>>> >
>>> > An easy "fix" would be to move the default dma masks initialization out
>>> > of acpi_dma_configure() (as it was in previous patch versions of this
>>> > series - I moved it to acpi_dma_configure() just a consolidation point
>>> > for initializing the masks instead of scattering them in every
>>> > acpi_dma_configure caller) I can send this as a fix-up patch to Joerg if
>>> > we think that's the right thing to do (or I can send it to Rafael later
>>> > when the code is in the merged depending on the timing) just let me
>>> > know please.
>>>
>>> Why can't arch_setup_dma_ops() set those masks too?
>>
>> Because the dma masks set-up is done by the caller (see
>> of_dma_configure()) according to firmware configuration or
>> platform data knowledge. I wanted to replicate the of_dma_configure()
>> interface on ACPI for obvious reasons (on ARM systems), I stopped
>> short of adding ACPI code to mirror of_dma_get_range() equivalent
>> (through the _DMA object) but I am really really nervous about changing
>> the code path on x86 because in theory all is fine, in practice even
>> just setting the masks to sane values can have unexpected consequences,
>> I just can't know (that's why I wasn't doing it in the first iterations
>> of this series).
>>
>> Side note: DT with of_dma_configure() and ACPI with
>> acpi_create_platform_device() set the default dma mask for all
>> platform devices already _regardless_ of what they really are, though
>> arguably acpi_bind_one() touches ways more devices.
>>
>> I really think that removing the default dma masks settings from
>> acpi_dma_configure() is the safer thing to do for the time being (or
>> moving acpi_dma_configure() to acpi_create_platform_device(), where the
>> DMA masks are set-up by default by core ACPI. Mark, Suravee, what was
>> the rationale behind calling arch_setup_dma_ops() in acpi_bind_one() ?)
>
>Alternatively, you can add one more arch wrapper that will be a no-op
>on x86 and that will set up the default masks and call
>arch_setup_dma_ops() on ARM.  Then, you can invoke that from
>acpi_dma_configure().
>
>Or make the definition of acpi_dma_configure() itself depend on the
>architecture.
>

So is it better that either removing the masks from acpi_dma_configure (or)
creating the wrapper as Rafael mentioned, than moving
acpi_dma_configure itself , because with something like iommu probe
deferral that is tried, acpi_dma_configure is getting invoked from a device's 
really_probe, a different path again ?

Regards,
 Sricharan

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox