* Re: [PATCH v3 4/7] PWM: add pwm driver for stm32 plaftorm
From: Thierry Reding @ 2016-12-05 11:30 UTC (permalink / raw)
To: Benjamin Gaignard
Cc: Mark Rutland, devicetree, Lars-Peter Clausen, alexandre.torgue,
linux-pwm, linux-iio, Linus Walleij, Arnaud Pouliquen,
Linux Kernel Mailing List, robh+dt, linux-arm-kernel,
Peter Meerwald-Stadler, knaack.h, Gerald Baeza, Fabrice Gasnier,
Lee Jones, Linaro Kernel Mailman List, Jonathan Cameron,
Benjamin Gaignard
In-Reply-To: <CA+M3ks7_utdw1c0A0m+RzuPTfvFAv5d8HoQvQDzXwHR8M6RKJA@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 4037 bytes --]
On Mon, Dec 05, 2016 at 12:02:45PM +0100, Benjamin Gaignard wrote:
> 2016-12-05 8:23 GMT+01:00 Thierry Reding <thierry.reding@gmail.com>:
> > On Fri, Dec 02, 2016 at 11:17:19AM +0100, Benjamin Gaignard wrote:
> >> This driver add support for pwm driver on stm32 platform.
> >
> > "adds". Also please use PWM in prose because it's an abbreviation.
> >
> >> The SoC have multiple instances of the hardware IP and each
> >> of them could have small differences: number of channels,
> >> complementary output, counter register size...
> >> Use DT parameters to handle those differentes configuration
> >
> > "different configurations"
>
> ok
>
> >
> >>
> >> version 2:
> >> - only keep one comptatible
> >> - use DT paramaters to discover hardware block configuration
> >
> > "parameters"
>
> ok
>
> >
> >>
> >> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
> >> ---
> >> drivers/pwm/Kconfig | 8 ++
> >> drivers/pwm/Makefile | 1 +
> >> drivers/pwm/pwm-stm32.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++
> >> 3 files changed, 294 insertions(+)
> >> create mode 100644 drivers/pwm/pwm-stm32.c
> >>
> >> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> >> index bf01288..a89fdba 100644
> >> --- a/drivers/pwm/Kconfig
> >> +++ b/drivers/pwm/Kconfig
> >> @@ -388,6 +388,14 @@ config PWM_STI
> >> To compile this driver as a module, choose M here: the module
> >> will be called pwm-sti.
> >>
> >> +config PWM_STM32
> >> + bool "STMicroelectronics STM32 PWM"
> >> + depends on ARCH_STM32
> >> + depends on OF
> >> + select MFD_STM32_GP_TIMER
> >
> > Should that be a "depends on"?
>
> I think select is fine because the wanted feature is PWM not the mfd part
I think in that case you may want to hide the MFD Kconfig option. See
Documentation/kbuild/kconfig-language.txt (notes about select) for the
details.
[...]
> >> +};
> >> +
> >> +#define to_stm32_pwm_dev(x) container_of(chip, struct stm32_pwm_dev, chip)
> >
> > Please turn this into a static inline.
>
> with putting struct pwm_chip as first filed I will just cast the structure
Don't do that, please. container_of() is still preferred because it is
correct and won't break even if you (or somebody else) changes the order
in the future. The fact that it might be optimized away is a detail, or
a micro-optimization. Force-casting is a bad idea because it won't catch
errors if for some reason the field doesn't remain in the first position
forever.
> >> +static void stm32_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> >> +{
> >> + u32 mask;
> >> + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
> >> +
> >> + /* Disable channel */
> >> + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4);
> >> + if (dev->caps & CAP_COMPLEMENTARY)
> >> + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4);
> >> +
> >> + regmap_update_bits(dev->regmap, TIM_CCER, mask, 0);
> >> +
> >> + /* When all channels are disabled, we can disable the controller */
> >> + if (!__active_channels(dev))
> >> + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, 0);
> >> +
> >> + clk_disable(dev->clk);
> >> +}
> >
> > All of the above can be folded into the ->apply() hook for atomic PWM
> > support.
> >
> > Also, in the above you use clk_enable() in the ->enable() callback and
> > clk_disable() in ->disable(). If you need the clock to access registers
> > you'll have to enabled it in the others as well because there are no
> > guarantees that configuration will only happen between ->enable() and
> > ->disable(). Atomic PWM simplifies this a bit, but you still need to be
> > careful about when to enable the clock in your ->apply() hook.
>
> I have used regmap functions enable/disable clk for me when accessing to
> the registers.
> I only have to take care of clk enable/disable when PWM state change.
Okay, that's fine then.
Thierry
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 3/7] PWM: add pwm-stm32 DT bindings
From: Thierry Reding @ 2016-12-05 11:23 UTC (permalink / raw)
To: Benjamin Gaignard
Cc: Lee Jones, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Mark Rutland,
alexandre.torgue-qxv4g6HH51o, devicetree-u79uwXL29TY76Z2rM5mHXA,
Linux Kernel Mailing List, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
Jonathan Cameron, knaack.h-Mmb7MZpHnFY, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Fabrice Gasnier, Gerald Baeza, Arnaud Pouliquen, Linus Walleij,
Linaro Kernel Mailman List, Benjamin Gaignard
In-Reply-To: <CA+M3ks4-JQNGYtdiXNj1J701x0njKD0_bf4yN1mLSjEcT2Z=6A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 5365 bytes --]
On Mon, Dec 05, 2016 at 12:08:32PM +0100, Benjamin Gaignard wrote:
> 2016-12-05 7:53 GMT+01:00 Thierry Reding <thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>:
> > On Fri, Dec 02, 2016 at 11:17:18AM +0100, Benjamin Gaignard wrote:
> >> Define bindings for pwm-stm32
> >>
> >> version 2:
> >> - use parameters instead of compatible of handle the hardware configuration
> >>
> >> Signed-off-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
> >> ---
> >> .../devicetree/bindings/pwm/pwm-stm32.txt | 38 ++++++++++++++++++++++
> >> 1 file changed, 38 insertions(+)
> >> create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
> >>
> >> diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
> >> new file mode 100644
> >> index 0000000..575b9fb
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
> >> @@ -0,0 +1,38 @@
> >> +STMicroelectronics PWM driver bindings for STM32
> >
> > Technically this bindings describe devices, so "driver binding" is a
> > somewhat odd wording. Perhaps:
> >
> > STMicroelectronics STM32 General Purpose Timer PWM bindings
> >
> > ?
> done
>
> >
> >> +
> >> +Must be a sub-node of STM32 general purpose timer driver
> >> +Parent node properties are describe in ../mfd/stm32-general-purpose-timer.txt
> >
> > Again, "driver parent node" is odd. Perhaps:
> >
> > Must be a sub-node of an STM32 General Purpose Timer device tree
> > node. See ../mfd/stm32-general-purpose-timer.txt for details about
> > the parent node.
> >
> > ?
>
> done
>
> >
> >> +Required parameters:
> >> +- compatible: Must be "st,stm32-pwm"
> >> +- pinctrl-names: Set to "default".
> >> +- pinctrl-0: List of phandles pointing to pin configuration nodes
> >> + for PWM module.
> >> + For Pinctrl properties, please refer to [1].
> >
> > Your indentation and capitalization are inconsistent. Also, please refer
> > to the pinctrl bindings by relative path and inline, rather than as a
> > footnote reference.
>
> OK
>
> >
> >> +
> >> +Optional parameters:
> >> +- st,breakinput: Set if the hardware have break input capabilities
> >> +- st,breakinput-polarity: Set break input polarity. Default is 0
> >> + The value define the active polarity:
> >> + - 0 (active LOW)
> >> + - 1 (active HIGH)
> >
> > Could we fold these into a single property? If st,breakinput-polarity is
> > not present it could simply mean that there is no break input, and if it
> > is present you don't have to rely on a default.
>
> I need to know if I have to activate breakinput feature and on which level
> I will rewrite it like that:
> Optional parameters:
> - st,breakinput-polarity-high: Set if break input polarity is active
> on high level.
> - st,breakinput-polarity-high: Set if break input polarity is active
> on low level.
How is that different from a single property:
Optional properties:
- st,breakinput-polarity: If present, a break input is available
for the channel. In that case the property value denotes the
polarity of the break input:
- 0: active low
- 1: active high
?
> > The pwm- prefix is rather redundant since the node is already named pwm.
> > Why not simply st,channels? Or simply channels, since it's not really
> > anything specific to this hardware.
> >
> > Come to think of it, might be worth having a discussion with our DT
> > gurus about what their stance is on using the # as prefix for numbers
> > (such as in #address-cells or #size-cells). This could be #channels to
> > mark it more explicitly as representing a count.
> >
> >> +- st,32bits-counter: Set if the hardware have a 32 bits counter
> >> +- st,complementary: Set if the hardware have complementary output channels
> >
> > "hardware has" and also maybe mention explicitly that this is a boolean
> > property. Otherwise people might be left wondering what it should be set
> > to. Or maybe word this differently to imply that it's boolean:
> >
> > - st,32bits-counter: if present, the hardware has a 32 bit counter
> > - st,complementary: if present, the hardware has a complementary
> > output channel
>
> I found a way to detect, at probe time, the number of channels, counter size,
> break input capability and complementary channels so I will remove
> "st,breakinput", "st,32bits-counter", "st,complementary" and "st,pwm-num-chan"
> parameters
Oh hey, that's very neat. I suppose in that case my comment above about
the break input polarity is somewhat obsoleted. Still I think you won't
need two properties. Instead you can follow what other similar
properties have done: choose a default (often low-active) and have a
single optional property to override the default (often high-active).
In your case:
- st,breakinput-active-high: Some channels have a break input,
whose polarity will be active low by default. If this
property is present, the channel will be configured with an
active high polarity for the break input.
Thierry
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH v3 3/7] PWM: add pwm-stm32 DT bindings
From: Benjamin Gaignard @ 2016-12-05 11:08 UTC (permalink / raw)
To: Thierry Reding
Cc: Lee Jones, robh+dt, Mark Rutland, alexandre.torgue, devicetree,
Linux Kernel Mailing List, linux-pwm, Jonathan Cameron, knaack.h,
Lars-Peter Clausen, Peter Meerwald-Stadler, linux-iio,
linux-arm-kernel, Fabrice Gasnier, Gerald Baeza, Arnaud Pouliquen,
Linus Walleij, Linaro Kernel Mailman List, Benjamin Gaignard
In-Reply-To: <20161205065350.GA18069@ulmo.ba.sec>
2016-12-05 7:53 GMT+01:00 Thierry Reding <thierry.reding@gmail.com>:
> On Fri, Dec 02, 2016 at 11:17:18AM +0100, Benjamin Gaignard wrote:
>> Define bindings for pwm-stm32
>>
>> version 2:
>> - use parameters instead of compatible of handle the hardware configuration
>>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
>> ---
>> .../devicetree/bindings/pwm/pwm-stm32.txt | 38 ++++++++++++++++++++++
>> 1 file changed, 38 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>>
>> diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>> new file mode 100644
>> index 0000000..575b9fb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>> @@ -0,0 +1,38 @@
>> +STMicroelectronics PWM driver bindings for STM32
>
> Technically this bindings describe devices, so "driver binding" is a
> somewhat odd wording. Perhaps:
>
> STMicroelectronics STM32 General Purpose Timer PWM bindings
>
> ?
done
>
>> +
>> +Must be a sub-node of STM32 general purpose timer driver
>> +Parent node properties are describe in ../mfd/stm32-general-purpose-timer.txt
>
> Again, "driver parent node" is odd. Perhaps:
>
> Must be a sub-node of an STM32 General Purpose Timer device tree
> node. See ../mfd/stm32-general-purpose-timer.txt for details about
> the parent node.
>
> ?
done
>
>> +Required parameters:
>> +- compatible: Must be "st,stm32-pwm"
>> +- pinctrl-names: Set to "default".
>> +- pinctrl-0: List of phandles pointing to pin configuration nodes
>> + for PWM module.
>> + For Pinctrl properties, please refer to [1].
>
> Your indentation and capitalization are inconsistent. Also, please refer
> to the pinctrl bindings by relative path and inline, rather than as a
> footnote reference.
OK
>
>> +
>> +Optional parameters:
>> +- st,breakinput: Set if the hardware have break input capabilities
>> +- st,breakinput-polarity: Set break input polarity. Default is 0
>> + The value define the active polarity:
>> + - 0 (active LOW)
>> + - 1 (active HIGH)
>
> Could we fold these into a single property? If st,breakinput-polarity is
> not present it could simply mean that there is no break input, and if it
> is present you don't have to rely on a default.
I need to know if I have to activate breakinput feature and on which level
I will rewrite it like that:
Optional parameters:
- st,breakinput-polarity-high: Set if break input polarity is active
on high level.
- st,breakinput-polarity-high: Set if break input polarity is active
on low level.
>> +- st,pwm-num-chan: Number of available PWM channels. Default is 0.
>
> The pwm- prefix is rather redundant since the node is already named pwm.
> Why not simply st,channels? Or simply channels, since it's not really
> anything specific to this hardware.
>
> Come to think of it, might be worth having a discussion with our DT
> gurus about what their stance is on using the # as prefix for numbers
> (such as in #address-cells or #size-cells). This could be #channels to
> mark it more explicitly as representing a count.
>
>> +- st,32bits-counter: Set if the hardware have a 32 bits counter
>> +- st,complementary: Set if the hardware have complementary output channels
>
> "hardware has" and also maybe mention explicitly that this is a boolean
> property. Otherwise people might be left wondering what it should be set
> to. Or maybe word this differently to imply that it's boolean:
>
> - st,32bits-counter: if present, the hardware has a 32 bit counter
> - st,complementary: if present, the hardware has a complementary
> output channel
I found a way to detect, at probe time, the number of channels, counter size,
break input capability and complementary channels so I will remove
"st,breakinput", "st,32bits-counter", "st,complementary" and "st,pwm-num-chan"
parameters
>
> Thierry
^ permalink raw reply
* Re: [PATCH v3 4/7] PWM: add pwm driver for stm32 plaftorm
From: Benjamin Gaignard @ 2016-12-05 11:02 UTC (permalink / raw)
To: Thierry Reding
Cc: Lee Jones, robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Mark Rutland,
alexandre.torgue-qxv4g6HH51o, devicetree-u79uwXL29TY76Z2rM5mHXA,
Linux Kernel Mailing List, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
Jonathan Cameron, knaack.h-Mmb7MZpHnFY, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Fabrice Gasnier, Gerald Baeza, Arnaud Pouliquen, Linus Walleij,
Linaro Kernel Mailman List, Benjamin Gaignard
In-Reply-To: <20161205072357.GB18069-EkSeR96xj6Pcmrwk2tT4+A@public.gmane.org>
2016-12-05 8:23 GMT+01:00 Thierry Reding <thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>:
> On Fri, Dec 02, 2016 at 11:17:19AM +0100, Benjamin Gaignard wrote:
>> This driver add support for pwm driver on stm32 platform.
>
> "adds". Also please use PWM in prose because it's an abbreviation.
>
>> The SoC have multiple instances of the hardware IP and each
>> of them could have small differences: number of channels,
>> complementary output, counter register size...
>> Use DT parameters to handle those differentes configuration
>
> "different configurations"
ok
>
>>
>> version 2:
>> - only keep one comptatible
>> - use DT paramaters to discover hardware block configuration
>
> "parameters"
ok
>
>>
>> Signed-off-by: Benjamin Gaignard <benjamin.gaignard-qxv4g6HH51o@public.gmane.org>
>> ---
>> drivers/pwm/Kconfig | 8 ++
>> drivers/pwm/Makefile | 1 +
>> drivers/pwm/pwm-stm32.c | 285 ++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 294 insertions(+)
>> create mode 100644 drivers/pwm/pwm-stm32.c
>>
>> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
>> index bf01288..a89fdba 100644
>> --- a/drivers/pwm/Kconfig
>> +++ b/drivers/pwm/Kconfig
>> @@ -388,6 +388,14 @@ config PWM_STI
>> To compile this driver as a module, choose M here: the module
>> will be called pwm-sti.
>>
>> +config PWM_STM32
>> + bool "STMicroelectronics STM32 PWM"
>> + depends on ARCH_STM32
>> + depends on OF
>> + select MFD_STM32_GP_TIMER
>
> Should that be a "depends on"?
I think select is fine because the wanted feature is PWM not the mfd part
>
>> + help
>> + Generic PWM framework driver for STM32 SoCs.
>> +
>> config PWM_STMPE
>> bool "STMPE expander PWM export"
>> depends on MFD_STMPE
>> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
>> index 1194c54..5aa9308 100644
>> --- a/drivers/pwm/Makefile
>> +++ b/drivers/pwm/Makefile
>> @@ -37,6 +37,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
>> obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
>> obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o
>> obj-$(CONFIG_PWM_STI) += pwm-sti.o
>> +obj-$(CONFIG_PWM_STM32) += pwm-stm32.o
>> obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o
>> obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o
>> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o
>> diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
>> new file mode 100644
>> index 0000000..a362f63
>> --- /dev/null
>> +++ b/drivers/pwm/pwm-stm32.c
>> @@ -0,0 +1,285 @@
>> +/*
>> + * Copyright (C) STMicroelectronics 2016
>> + * Author: Gerald Baeza <gerald.baeza-qxv4g6HH51o@public.gmane.org>
>
> Could use a blank line between the above. Also, please use a single
> space after : for consistency.
ok
>
>> + * License terms: GNU General Public License (GPL), version 2
>
> Here too.
OK
>
>> + *
>> + * Inspired by timer-stm32.c from Maxime Coquelin
>> + * pwm-atmel.c from Bo Shen
>> + */
>> +
>> +#include <linux/of.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pwm.h>
>
> Please sort these alphabetically.
ok
>> +
>> +#include <linux/mfd/stm32-gptimer.h>
>> +
>> +#define DRIVER_NAME "stm32-pwm"
>> +
>> +#define CAP_COMPLEMENTARY BIT(0)
>> +#define CAP_32BITS_COUNTER BIT(1)
>> +#define CAP_BREAKINPUT BIT(2)
>> +#define CAP_BREAKINPUT_POLARITY BIT(3)
>
> Just make these boolean. Makes the conditionals a lot simpler to read.
I will do that for v4
>> +
>> +struct stm32_pwm_dev {
>
> No need for the _dev suffix.
>
>> + struct device *dev;
>> + struct clk *clk;
>> + struct regmap *regmap;
>> + struct pwm_chip chip;
>
> It's slightly more efficient to put this as first field because then
> to_stm32_pwm() becomes a no-op.
Ok I will remove it
>
>> + int caps;
>> + int npwm;
>
> unsigned int, please.
>
>> + u32 polarity;
>
> Maybe use a prefix here to stress that it is the polarity of the
> complementary output. Otherwise one might take it for the PWM signal's
> polarity that's already part of the PWM state.
I will rename it
>
>> +};
>> +
>> +#define to_stm32_pwm_dev(x) container_of(chip, struct stm32_pwm_dev, chip)
>
> Please turn this into a static inline.
with putting struct pwm_chip as first filed I will just cast the structure
>> +
>> +static u32 __active_channels(struct stm32_pwm_dev *pwm_dev)
>
> No need for a __ prefix.
I wlll remove it
>
>> +{
>> + u32 ccer;
>> +
>> + regmap_read(pwm_dev->regmap, TIM_CCER, &ccer);
>> +
>> + return ccer & TIM_CCER_CCXE;
>> +}
>> +
>> +static int write_ccrx(struct stm32_pwm_dev *dev, struct pwm_device *pwm,
>> + u32 ccr)
>
> u32 value, perhaps? I first mistook this to be a register offset.
OK
>
>> +{
>> + switch (pwm->hwpwm) {
>> + case 0:
>> + return regmap_write(dev->regmap, TIM_CCR1, ccr);
>> + case 1:
>> + return regmap_write(dev->regmap, TIM_CCR2, ccr);
>> + case 2:
>> + return regmap_write(dev->regmap, TIM_CCR3, ccr);
>> + case 3:
>> + return regmap_write(dev->regmap, TIM_CCR4, ccr);
>> + }
>> + return -EINVAL;
>> +}
>> +
>> +static int stm32_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
>> + int duty_ns, int period_ns)
>
> Please implement this as an atomic PWM driver, I don't want new drivers
> to use the legacy callbacks.
Ok I will just use .apply ops.
>
>> +{
>> + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
>
> I think something like "stm", or "priv" would be more appropriate here.
> If you ever need access to a struct device, you'll be hard-pressed to
> find a good name for it.
I will use priv
>
>> + unsigned long long prd, div, dty;
>> + int prescaler = 0;
>
> If this can never be negative, please make it unsigned int.
>
>> + u32 max_arr = 0xFFFF, ccmr, mask, shift, bdtr;
>> +
>> + if (dev->caps & CAP_32BITS_COUNTER)
>> + max_arr = 0xFFFFFFFF;
>
> I prefer lower-case hexadecimal digits.
I have found a way to remove this code
>
>> +
>> + /* Period and prescaler values depends of clock rate */
>
> "depend on"
fixed
>
>> + div = (unsigned long long)clk_get_rate(dev->clk) * period_ns;
>> +
>> + do_div(div, NSEC_PER_SEC);
>> + prd = div;
>> +
>> + while (div > max_arr) {
>> + prescaler++;
>> + div = prd;
>> + do_div(div, (prescaler + 1));
>> + }
>> + prd = div;
>
> Blank line after blocks, please.
fixed
>
>> +
>> + if (prescaler > MAX_TIM_PSC) {
>> + dev_err(chip->dev, "prescaler exceeds the maximum value\n");
>> + return -EINVAL;
>> + }
>> +
>> + /* All channels share the same prescaler and counter so
>> + * when two channels are active at the same we can't change them
>> + */
>
> This isn't proper block comment style. Also, please use all of the
> columns at your disposal.
done
>
>> + if (__active_channels(dev) & ~(1 << pwm->hwpwm * 4)) {
>> + u32 psc, arr;
>> +
>> + regmap_read(dev->regmap, TIM_PSC, &psc);
>> + regmap_read(dev->regmap, TIM_ARR, &arr);
>> +
>> + if ((psc != prescaler) || (arr != prd - 1))
>> + return -EINVAL;
>
> Maybe -EBUSY to differentiate from other error cases?
done
>
>> + }
>> +
>> + regmap_write(dev->regmap, TIM_PSC, prescaler);
>> + regmap_write(dev->regmap, TIM_ARR, prd - 1);
>> + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
>> +
>> + /* Calculate the duty cycles */
>> + dty = prd * duty_ns;
>> + do_div(dty, period_ns);
>> +
>> + write_ccrx(dev, pwm, dty);
>> +
>> + /* Configure output mode */
>> + shift = (pwm->hwpwm & 0x1) * 8;
>> + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift;
>> + mask = 0xFF << shift;
>> +
>> + if (pwm->hwpwm & 0x2)
>
> This looks as though TIM_CCMR1 is used for channels 0 and 1, while
> TIM_CCMR2 is used for channels 2 and 3. Wouldn't it be more natural to
> make the conditional above:
>
> if (pwm->hwpwm >= 2)
>
> ? Or perhaps better yet:
>
> if (pwm->hwpwm < 2)
> /* update TIM_CCMR1 */
> else
> /* update TIM_CCMR2 */
I will code it that, thanks.
>
> The other alternative, which might make the code slightly more readable,
> would be to get rid of all these conditionals by parameterizing the
> offsets per PWM channel. The PWM subsystem has a means of storing per-
> channel chip-specific data (see pwm_{set,get}_chip_data()). It might be
> a little overkill for this particular driver, given that the number of
> conditionals is fairly small.
>
>> + regmap_update_bits(dev->regmap, TIM_CCMR2, mask, ccmr);
>> + else
>> + regmap_update_bits(dev->regmap, TIM_CCMR1, mask, ccmr);
>> +
>> + if (!(dev->caps & CAP_BREAKINPUT))
>> + return 0;
>
> If you make these capabilities boolean, it becomes much more readable,
> especially for negations:
Yes I will fix that
>
> if (!dev->has_breakinput)
>
>> +
>> + bdtr = TIM_BDTR_MOE | TIM_BDTR_AOE;
>> +
>> + if (dev->caps & CAP_BREAKINPUT_POLARITY)
>> + bdtr |= TIM_BDTR_BKE;
>> +
>> + if (dev->polarity)
>> + bdtr |= TIM_BDTR_BKP;
>> +
>> + regmap_update_bits(dev->regmap, TIM_BDTR,
>> + TIM_BDTR_MOE | TIM_BDTR_AOE |
>> + TIM_BDTR_BKP | TIM_BDTR_BKE,
>> + bdtr);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
>> + enum pwm_polarity polarity)
>> +{
>> + u32 mask;
>> + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
>> +
>> + mask = TIM_CCER_CC1P << (pwm->hwpwm * 4);
>> + if (dev->caps & CAP_COMPLEMENTARY)
>> + mask |= TIM_CCER_CC1NP << (pwm->hwpwm * 4);
>> +
>> + regmap_update_bits(dev->regmap, TIM_CCER, mask,
>> + polarity == PWM_POLARITY_NORMAL ? 0 : mask);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
>> +{
>> + u32 mask;
>> + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
>> +
>> + clk_enable(dev->clk);
>> +
>> + /* Enable channel */
>> + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4);
>> + if (dev->caps & CAP_COMPLEMENTARY)
>> + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4);
>> +
>> + regmap_update_bits(dev->regmap, TIM_CCER, mask, mask);
>> +
>> + /* Make sure that registers are updated */
>> + regmap_update_bits(dev->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
>> +
>> + /* Enable controller */
>> + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
>> +
>> + return 0;
>> +}
>> +
>> +static void stm32_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
>> +{
>> + u32 mask;
>> + struct stm32_pwm_dev *dev = to_stm32_pwm_dev(chip);
>> +
>> + /* Disable channel */
>> + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4);
>> + if (dev->caps & CAP_COMPLEMENTARY)
>> + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4);
>> +
>> + regmap_update_bits(dev->regmap, TIM_CCER, mask, 0);
>> +
>> + /* When all channels are disabled, we can disable the controller */
>> + if (!__active_channels(dev))
>> + regmap_update_bits(dev->regmap, TIM_CR1, TIM_CR1_CEN, 0);
>> +
>> + clk_disable(dev->clk);
>> +}
>
> All of the above can be folded into the ->apply() hook for atomic PWM
> support.
>
> Also, in the above you use clk_enable() in the ->enable() callback and
> clk_disable() in ->disable(). If you need the clock to access registers
> you'll have to enabled it in the others as well because there are no
> guarantees that configuration will only happen between ->enable() and
> ->disable(). Atomic PWM simplifies this a bit, but you still need to be
> careful about when to enable the clock in your ->apply() hook.
I have used regmap functions enable/disable clk for me when accessing to
the registers.
I only have to take care of clk enable/disable when PWM state change.
>
>> +
>> +static const struct pwm_ops stm32pwm_ops = {
>> + .config = stm32_pwm_config,
>> + .set_polarity = stm32_pwm_set_polarity,
>> + .enable = stm32_pwm_enable,
>> + .disable = stm32_pwm_disable,
>> +};
>
> You need to set the .owner field as well.
OK and I will remove legacy ops to use only apply.
>
>> +
>> +static int stm32_pwm_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct device_node *np = dev->of_node;
>> + struct stm32_gptimer_dev *mfd = dev_get_drvdata(pdev->dev.parent);
>> + struct stm32_pwm_dev *pwm;
>> + int ret;
>> +
>> + pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
>> + if (!pwm)
>> + return -ENOMEM;
>> +
>> + pwm->regmap = mfd->regmap;
>> + pwm->clk = mfd->clk;
>> +
>> + if (!pwm->regmap || !pwm->clk)
>> + return -EINVAL;
>> +
>> + if (of_property_read_bool(np, "st,complementary"))
>> + pwm->caps |= CAP_COMPLEMENTARY;
>> +
>> + if (of_property_read_bool(np, "st,32bits-counter"))
>> + pwm->caps |= CAP_32BITS_COUNTER;
>> +
>> + if (of_property_read_bool(np, "st,breakinput"))
>> + pwm->caps |= CAP_BREAKINPUT;
>> +
>> + if (!of_property_read_u32(np, "st,breakinput-polarity", &pwm->polarity))
>> + pwm->caps |= CAP_BREAKINPUT_POLARITY;
>> +
>> + of_property_read_u32(np, "st,pwm-num-chan", &pwm->npwm);
>> +
>> + pwm->chip.base = -1;
>> + pwm->chip.dev = dev;
>> + pwm->chip.ops = &stm32pwm_ops;
>> + pwm->chip.npwm = pwm->npwm;
>> +
>> + ret = pwmchip_add(&pwm->chip);
>> + if (ret < 0)
>> + return ret;
>> +
>> + platform_set_drvdata(pdev, pwm);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_pwm_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_pwm_dev *pwm = platform_get_drvdata(pdev);
>> + int i;
>
> unsigned int, please.
OK
>
>> +
>> + for (i = 0; i < pwm->npwm; i++)
>> + pwm_disable(&pwm->chip.pwms[i]);
>> +
>> + pwmchip_remove(&pwm->chip);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id stm32_pwm_of_match[] = {
>> + {
>> + .compatible = "st,stm32-pwm",
>> + },
>
> The above can be collapsed into a single line.
OK
>
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
>> +
>> +static struct platform_driver stm32_pwm_driver = {
>> + .probe = stm32_pwm_probe,
>> + .remove = stm32_pwm_remove,
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .of_match_table = stm32_pwm_of_match,
>> + },
>> +};
>
> Please don't use tabs for padding within the structure definition since
> it doesn't align properly anyway.
OK
>
>> +module_platform_driver(stm32_pwm_driver);
>> +
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 PWM driver");
>> +MODULE_LICENSE("GPL");
>
> According to the header comment this should be "GPL v2".
done
>
> Thanks,
> Thierry
^ permalink raw reply
* Re: [PATCH v2 2/3] ARM: dts: sunxi: add support for Orange Pi Zero board
From: Icenowy Zheng @ 2016-12-05 11:01 UTC (permalink / raw)
To: Maxime Ripard
Cc: Mark Rutland, devicetree@vger.kernel.org, Vishnu Patekar,
Arnd Bergmann, linux-doc@vger.kernel.org, André Przywara,
Jonathan Corbet, linux-kernel, Russell King, Hans de Goede,
Chen-Yu Tsai, "linux-arm-kernel@lists.infradead.org"
In-Reply-To: <20161205094023.mtymfvpmca4x3ohh@lukather>
05.12.2016, 17:40, "Maxime Ripard" <maxime.ripard@free-electrons.com>:
> 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.
If so, I think the [PATCH -next v3 2/2] is ready to be merged ;-)
>
> Maxime
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] i2c: rk3x: keep i2c irq ON in suspend
From: Heiko Stuebner @ 2016-12-05 10:54 UTC (permalink / raw)
To: David Wu
Cc: wsa-z923LK4zBo2bacvFa/9K2g, dianders-F7+t8E8rja9g9hUCZPvPmw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480924979-13450-1-git-send-email-david.wu-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Hi David,
Am Montag, 5. Dezember 2016, 16:02:59 CET schrieb David Wu:
> During suspend there may still be some i2c access happening.
> And if we don't keep i2c irq ON, there may be i2c access timeout if
> i2c is in irq mode of operation.
can you describe the issue you're trying to fix a bit more please?
I.e. I'd think the i2c-core does suspend i2c-client devices first, so that
these should be able to finish up their ongoing transfers and not start any
new ones instead?
Your irq can still happen slightly after the system started going to actually
sleep, so to me it looks like you just widened the window where irqs can be
handled. Especially as your irq could also just simply stem from the start
state, so you cannot even be sure if your transaction actually is finished.
So to me it looks like the i2c-connected device driver should be fixed instead?
In the past I solved this for example in the zforce_ts driver [0] to
prevent i2c transfers from happening during suspend.
Heiko
[0] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/input/touchscreen/zforce_ts.c
>
> Signed-off-by: David Wu <david.wu-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
> drivers/i2c/busses/i2c-rk3x.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
> index df22066..67af32a 100644
> --- a/drivers/i2c/busses/i2c-rk3x.c
> +++ b/drivers/i2c/busses/i2c-rk3x.c
> @@ -1261,7 +1261,7 @@ static int rk3x_i2c_probe(struct platform_device
> *pdev) }
>
> ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
> - 0, dev_name(&pdev->dev), i2c);
> + IRQF_NO_SUSPEND, dev_name(&pdev->dev), i2c);
> if (ret < 0) {
> dev_err(&pdev->dev, "cannot request IRQ\n");
> return ret;
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 3/7] PWM: add pwm-stm32 DT bindings
From: Thierry Reding @ 2016-12-05 10:50 UTC (permalink / raw)
To: Lee Jones
Cc: mark.rutland, devicetree, linaro-kernel, lars, alexandre.torgue,
linux-pwm, linux-iio, pmeerw, arnaud.pouliquen, linux-kernel,
robh+dt, linux-arm-kernel, Benjamin Gaignard, knaack.h,
gerald.baeza, fabrice.gasnier, linus.walleij, jic23,
Benjamin Gaignard
In-Reply-To: <20161205083535.GB14004@dell>
[-- Attachment #1.1: Type: text/plain, Size: 4619 bytes --]
On Mon, Dec 05, 2016 at 08:35:35AM +0000, Lee Jones wrote:
> On Mon, 05 Dec 2016, Thierry Reding wrote:
>
> > On Fri, Dec 02, 2016 at 11:17:18AM +0100, Benjamin Gaignard wrote:
> > > Define bindings for pwm-stm32
> > >
> > > version 2:
> > > - use parameters instead of compatible of handle the hardware configuration
> > >
> > > Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
> > > ---
> > > .../devicetree/bindings/pwm/pwm-stm32.txt | 38 ++++++++++++++++++++++
> > > 1 file changed, 38 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
> > >
> > > diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
> > > new file mode 100644
> > > index 0000000..575b9fb
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
> > > @@ -0,0 +1,38 @@
> > > +STMicroelectronics PWM driver bindings for STM32
> >
> > Technically this bindings describe devices, so "driver binding" is a
> > somewhat odd wording. Perhaps:
> >
> > STMicroelectronics STM32 General Purpose Timer PWM bindings
> >
> > ?
> >
> > > +
> > > +Must be a sub-node of STM32 general purpose timer driver
> > > +Parent node properties are describe in ../mfd/stm32-general-purpose-timer.txt
> >
> > Again, "driver parent node" is odd. Perhaps:
> >
> > Must be a sub-node of an STM32 General Purpose Timer device tree
> > node. See ../mfd/stm32-general-purpose-timer.txt for details about
> > the parent node.
> >
> > ?
> >
> > > +Required parameters:
> > > +- compatible: Must be "st,stm32-pwm"
> > > +- pinctrl-names: Set to "default".
> > > +- pinctrl-0: List of phandles pointing to pin configuration nodes
> > > + for PWM module.
> > > + For Pinctrl properties, please refer to [1].
> >
> > Your indentation and capitalization are inconsistent. Also, please refer
> > to the pinctrl bindings by relative path and inline, rather than as a
> > footnote reference.
> >
> > > +
> > > +Optional parameters:
> > > +- st,breakinput: Set if the hardware have break input capabilities
> > > +- st,breakinput-polarity: Set break input polarity. Default is 0
> > > + The value define the active polarity:
> > > + - 0 (active LOW)
> > > + - 1 (active HIGH)
> >
> > Could we fold these into a single property? If st,breakinput-polarity is
> > not present it could simply mean that there is no break input, and if it
> > is present you don't have to rely on a default.
> >
> > > +- st,pwm-num-chan: Number of available PWM channels. Default is 0.
> >
> > The pwm- prefix is rather redundant since the node is already named pwm.
> > Why not simply st,channels? Or simply channels, since it's not really
> > anything specific to this hardware.
> >
> > Come to think of it, might be worth having a discussion with our DT
> > gurus about what their stance is on using the # as prefix for numbers
> > (such as in #address-cells or #size-cells). This could be #channels to
> > mark it more explicitly as representing a count.
>
> Unfortunately that ship has sailed.
>
> st,pwm-num-chan already exists (with your blessing). It's usually
I think I did at the time object, though very mildly. The property here
is somewhat different, though. For one this is a PWM specific node, so
the pwm- prefix is completely redundant. Also for pwm-sti where you had
introduced st,pwm-num-chan, the property denoted how many PWM channels
vs. capture channels (st,capture-num-chan) the device was supposed to
use. Here there are only one type of channels.
> suggested to reuse exiting properties when writing new bindings.
Given the above I think this case is different. Further my understanding
is that the desire to reuse existing properties is primarily for generic
properties. Vendor specific properties are always going to have to be
defined in the specific bindings, so it doesn't matter very much whether
they are reused or not.
Lastly, I think st,pwm-num-chan is not optimal, and while it isn't very
bad either, I do believe that when we see ways of improving things then
we should do so, regardless of whether existing ways to describe things
already exist. Especially if it comes at no additional cost.
All of that said, this is my opinion and if everybody thinks that the
st,pwm-num-chan is the better choice I'll merge it. Anyway, we'll need
the Acked-by from one of the device tree bindings maintainers and I'd
like to see at least an attempt at a discussion.
Thierry
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 06/11] ARM: dts: imx: Add imx6sll EVK board dts support
From: Fabio Estevam @ 2016-12-05 10:25 UTC (permalink / raw)
To: Bai Ping
Cc: Mark Rutland, devicetree@vger.kernel.org, Philipp Zabel,
Michael Turquette, Daniel Lezcano, Stephen Boyd, linux-clk,
linux-gpio@vger.kernel.org, robh+dt@kernel.org, Sascha Hauer,
Fabio Estevam, Thomas Gleixner, Shawn Guo, Linus Walleij,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <1480660774-25055-7-git-send-email-ping.bai@nxp.com>
On Fri, Dec 2, 2016 at 4:39 AM, Bai Ping <ping.bai@nxp.com> wrote:
> Add basic dts support for imx6sll EVK baoard.
s/baord/board
> + battery: max8903@0 {
> + compatible = "fsl,max8903-charger";
> + pinctrl-names = "default";
> + dok_input = <&gpio4 13 1>;
> + uok_input = <&gpio4 13 1>;
> + chg_input = <&gpio4 15 1>;
> + flt_input = <&gpio4 14 1>;
> + fsl,dcm_always_high;
> + fsl,dc_valid;
> + fsl,adc_disable;
These properties do not exist in mainline, please remove them.
> + status = "okay";
> + };
> +
> + pxp_v4l2_out {
> + compatible = "fsl,imx6sl-pxp-v4l2";
> + status = "okay";
> + };
We don't have pxp support in mainline kernel, please remove it.
> +
> + regulators {
> + compatible = "simple-bus";
> + #address-cells = <1>;
> + #size-cells = <0>;
Please remove it and place the regulator nodes directly as below:
> +
> + reg_usb_otg1_vbus: regulator@0 {
> + compatible = "regulator-fixed";
> + reg = <0>;
> + regulator-name = "usb_otg1_vbus";
> + regulator-min-microvolt = <5000000>;
> + regulator-max-microvolt = <5000000>;
> + gpio = <&gpio4 0 GPIO_ACTIVE_HIGH>;
> + enable-active-high;
> + };
reg_usb_otg1_vbus: regulator-usb-otg1-vbus {
compatible = "regulator-fixed";
regulator-name = "usb_otg1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&gpio4 0 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
> +&cpu0 {
> + arm-supply = <&sw1a_reg>;
> + soc-supply = <&sw1c_reg>;
> +};
This is only for LDO bypass mode, right? We don't support LDO-bypass
in mainline.
> +&gpc {
> + fsl,ldo-bypass = <1>;
We don't support ldo-bypass in mainline, please remove it.
> +&pxp {
> + status = "okay";
> +};
We don't support PXP in mainline, please remove it.
^ permalink raw reply
* Re: [RFC PATCH 2/2] Documentation: devictree: Add macb mdio bindings
From: Nicolas Ferre @ 2016-12-05 10:23 UTC (permalink / raw)
To: Harini Katakam, Rob Herring
Cc: Harini Katakam, David Miller, Pawel Moll, Mark Rutland,
ijc+devicetree@hellion.org.uk, Kumar Gala, Boris Brezillon,
alexandre.belloni, netdev, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, michals@xilinx.com
In-Reply-To: <CAFcVECJRbgAxQMbu7jABHjkiicD1Ysp7WmDMoUzs2P5qn26uqw@mail.gmail.com>
Le 05/12/2016 à 03:55, Harini Katakam a écrit :
> Hi Rob,
>
>
> Thanks for the review.
> On Sun, Dec 4, 2016 at 3:05 AM, Rob Herring <robh@kernel.org> wrote:
>> On Mon, Nov 28, 2016 at 03:19:27PM +0530, Harini Katakam wrote:
> <snip>
>>> +Required properties:
>>> +- compatible: Should be "cdns,macb-mdio"
>>
>> Only one version ever? This needs more specific compatible strings.
>>
>
> This is part of the Cadence MAC in a way.
> I can use revision number from the Cadence spec I was working with.
> But it is not necessarily specific that version.
Yes it is:
you must specify the first precise SoC needing/implementing it:
"cdns,zynqmp-gem-mdio" or "xlnx,zynq-gem-mdio" or anything looking like
this.
So, if an Atmel implementation is slightly different, we can still use
our own "atmel,sama5d2-gem-mdio" or "cdns,sama5d2-gem-mdio"
compatibility string.
On the other hand, if it's strictly the same, we can use the
"xlnx,zynq-gem-mdio" compatibility without any problem...
> I'll take care of the other comments in the next version.
>
> Regards,
> Harini
>
--
Nicolas Ferre
^ permalink raw reply
* Re: [PATCH v1 2/2] crypto: mediatek - add DT bindings documentation
From: Matthias Brugger @ 2016-12-05 10:18 UTC (permalink / raw)
To: Ryder Lee, Herbert Xu, David S. Miller
Cc: devicetree, linux-mediatek, linux-kernel, linux-crypto,
linux-arm-kernel, Sean Wang, Roy Luo
In-Reply-To: <1480921284-45827-3-git-send-email-ryder.lee@mediatek.com>
On 05/12/16 08:01, Ryder Lee wrote:
> Add DT bindings documentation for the crypto driver
>
> Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
> ---
> .../devicetree/bindings/crypto/mediatek-crypto.txt | 32 ++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/crypto/mediatek-crypto.txt
>
> diff --git a/Documentation/devicetree/bindings/crypto/mediatek-crypto.txt b/Documentation/devicetree/bindings/crypto/mediatek-crypto.txt
> new file mode 100644
> index 0000000..8b1db08
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/crypto/mediatek-crypto.txt
> @@ -0,0 +1,32 @@
> +MediaTek cryptographic accelerators
> +
> +Required properties:
> +- compatible: Should be "mediatek,mt7623-crypto"
Do you know how big the difference is between the crypto engine for
mt7623/mt2701/mt8521p in comparison, let's say mt8173 or mt6797?
Do this SoCs have a crypot engine? If so and they are quite similar, we
might think of adding a mtk-crypto binding and add soc specific bindings.
Regards,
Matthias
> +- reg: Address and length of the register set for the device
> +- interrupts: Should contain the five crypto engines interrupts in numeric
> + order. These are global system and four descriptor rings.
> +- clocks: the clock used by the core
> +- clock-names: the names of the clock listed in the clocks property. These are
> + "ethif", "cryp"
> +- power-domains: Must contain a reference to the PM domain.
> +
> +
> +Optional properties:
> +- interrupt-parent: Should be the phandle for the interrupt controller
> + that services interrupts for this device
> +
> +
> +Example:
> + crypto: crypto@1b240000 {
> + compatible = "mediatek,mt7623-crypto";
> + reg = <0 0x1b240000 0 0x20000>;
> + interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_LOW>,
> + <GIC_SPI 83 IRQ_TYPE_LEVEL_LOW>,
> + <GIC_SPI 84 IRQ_TYPE_LEVEL_LOW>,
> + <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>,
> + <GIC_SPI 97 IRQ_TYPE_LEVEL_LOW>;
> + clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
> + <ðsys CLK_ETHSYS_CRYPTO>;
> + clock-names = "ethif","cryp";
> + power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
> + };
>
^ permalink raw reply
* Re: [PATCH 2/8] dt-bindings: document the STM32 RTC bindings
From: Amelie DELAUNAY @ 2016-12-05 10:14 UTC (permalink / raw)
To: Alexandre Belloni
Cc: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
Alexandre TORGUE, linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org,
rtc-linux-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Gabriel FERNANDEZ
In-Reply-To: <20161205100630.n3gldznf524pucjm-m++hUPXGwpdeoWH0uzbU5w@public.gmane.org>
Hi Alexandre,
Thanks for reviewing
On 12/05/2016 11:06 AM, Alexandre Belloni wrote:
> Hi,
>
> On 02/12/2016 at 15:09:55 +0100, Amelie Delaunay wrote :
>> This patch adds documentation of device tree bindings for the STM32 RTC.
>>
>> Signed-off-by: Amelie Delaunay <amelie.delaunay-qxv4g6HH51o@public.gmane.org>
>> ---
>> .../devicetree/bindings/rtc/st,stm32-rtc.txt | 31 ++++++++++++++++++++++
>> 1 file changed, 31 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>> new file mode 100644
>> index 0000000..4578838
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>> @@ -0,0 +1,31 @@
>> +STM32 Real Time Clock
>> +
>> +Required properties:
>> +- compatible: "st,stm32-rtc".
>> +- reg: address range of rtc register set.
>> +- clocks: reference to the clock entry ck_rtc.
>> +- clock-names: name of the clock used. Should be "ck_rtc".
>
> Is this name really useful?
You're right, not useful.
>
>> +- interrupt-parent: phandle for the interrupt controller.
>> +- interrupts: rtc alarm interrupt.
>> +- interrupt-names: rtc alarm interrupt name, should be "alarm".
>
> Same comment, is this name really useful?
Ditto.
>
>
Best regards,
Amelie
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* [PATCH v2] v4l: async: make v4l2 coexist with devicetree nodes in a dt overlay
From: Javi Merino @ 2016-12-05 10:09 UTC (permalink / raw)
To: Javier Martinez Canillas, Sakari Ailus
Cc: linux-media, linux-kernel, devicetree, Javi Merino,
Mauro Carvalho Chehab
In asds configured with V4L2_ASYNC_MATCH_OF, the v4l2 subdev can be
part of a devicetree overlay, for example:
&media_bridge {
...
my_port: port@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
ep: endpoint@0 {
remote-endpoint = <&camera0>;
};
};
};
/ {
fragment@0 {
target = <&i2c0>;
__overlay__ {
my_cam {
compatible = "foo,bar";
port {
camera0: endpoint {
remote-endpoint = <&my_port>;
...
};
};
};
};
};
};
Each time the overlay is applied, its of_node pointer will be
different. We are not interested in matching the pointer, what we
want to match is that the path is the one we are expecting. Change to
use of_node_cmp() so that we continue matching after the overlay has
been removed and reapplied.
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: Javier Martinez Canillas <javier@osg.samsung.com>
Cc: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Javi Merino <javi.merino@kernel.org>
---
drivers/media/v4l2-core/v4l2-async.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 5bada20..d33a17c 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -42,7 +42,8 @@ static bool match_devname(struct v4l2_subdev *sd,
static bool match_of(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
{
- return sd->of_node == asd->match.of.node;
+ return !of_node_cmp(of_node_full_name(sd->of_node),
+ of_node_full_name(asd->match.of.node));
}
static bool match_custom(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd)
--
2.1.4
^ permalink raw reply related
* Re: [PATCHv4 11/15] clk: ti: clockdomain: add clock provider support to clockdomains
From: Tero Kristo @ 2016-12-05 10:08 UTC (permalink / raw)
To: Tony Lindgren, Michael Turquette
Cc: devicetree, Stephen Boyd, Rob Herring, linux-omap, linux-clk,
linux-arm-kernel
In-Reply-To: <20161203001852.GI4705@atomide.com>
On 03/12/16 02:18, Tony Lindgren wrote:
> * Michael Turquette <mturquette@baylibre.com> [161202 15:52]:
>> Quoting Tony Lindgren (2016-12-02 15:12:40)
>>> * Michael Turquette <mturquette@baylibre.com> [161202 14:34]:
>>>> Quoting Tony Lindgren (2016-10-28 16:54:48)
>>>>> * Stephen Boyd <sboyd@codeaurora.org> [161028 16:37]:
>>>>>> On 10/28, Tony Lindgren wrote:
>>>>>>> * Tero Kristo <t-kristo@ti.com> [161028 00:43]:
>>>>>>>> On 28/10/16 03:50, Stephen Boyd wrote:
>>>>>>>>> I suppose a PRCM is
>>>>>>>>> like an MFD that has clocks and resets under it? On other
>>>>>>>>> platforms we've combined that all into one node and just had
>>>>>>>>> #clock-cells and #reset-cells in that node. Is there any reason
>>>>>>>>> we can't do that here?
>>>>>>>>
>>>>>>>> For OMAPs, there are typically multiple instances of the PRCM around; OMAP4
>>>>>>>> for example has:
>>>>>>>>
>>>>>>>> cm1 @ 0x4a004000 (clocks + clockdomains)
>>>>>>>> cm2 @ 0x4a008000 (clocks + clockdomains)
>>>>>>>> prm @ 0x4a306000 (few clocks + resets + power state handling)
>>>>>>>> scrm @ 0x4a30a000 (few external clocks + plenty of misc stuff)
>>>>>>>>
>>>>>>>> These instances are also under different power/voltage domains which means
>>>>>>>> their PM behavior is different.
>>>>>>>>
>>>>>>>> The idea behind having a clockdomain as a provider was mostly to have the
>>>>>>>> topology visible : prcm-instance -> clockdomain -> clocks
>>>>>>>
>>>>>>> Yeah that's needed to get the interconnect hierarchy right for
>>>>>>> genpd :)
>>>>>>>
>>>>>>>> ... but basically I think it would be possible to drop the clockdomain
>>>>>>>> representation and just mark the prcm-instance as a clock provider. Tony,
>>>>>>>> any thoughts on that?
>>>>>>>
>>>>>>> No let's not drop the clockdomains as those will be needed when we
>>>>>>> move things into proper hierarchy within the interconnect instances.
>>>>>>> This will then help with getting things right with genpd.
>>>>>>>
>>>>>>> In the long run we just want to specify clockdomain and the offset of
>>>>>>> the clock instance within the clockdomain in the dts files.
>>>>>>>
>>>>>>
>>>>>> Sorry, I have very little idea how OMAP hardware works. Do you
>>>>>> mean that you will have different nodes for each clockdomain so
>>>>>> that genpd can map 1:1 to the node in dts? But in hardware
>>>>>> there's a prcm that allows us to control many clock domains
>>>>>> through register read/writes? How is the interconnect involved?
>>>>>
>>>>> There are multiple clockdomains, at least one for each interconnect
>>>>> instance. Once a clockdomain is idle, the related interconnect can
>>>>> idle too. So yeah genpd pretty much maps 1:1 with the clockdomains.
>>>>>
>>>>> There's more info in for example omap4 TRM section "3.4.1 Device
>>>>> Power-Management Layout" that shows the voltage/power/clock domains.
>>>>> The interconnect instances are mostly named there too looking at
>>>>> the L4/L3 naming.
>>>>
>>>> I'm confused on two points:
>>>>
>>>> 1) why are the clkdm's acting as clock providers? I've always hated the
>>>> name "clock domain" since those bits are for managing module state, not
>>>> clock state. The PRM, CM1 and CM2 provide the clocks, not the
>>>> clockdomains.
>>>
>>> The clock domains have multiple clock inputs that are routed to multiple
>>> child clocks. So it is a clock :)
>>>
>>> See for example omap4430 TRM "3.6.4 CD_WKUP Clock Domain" on page
>>> 393 in my revision here.
>>>
>>> On that page "Figure 3-48" shows CD_WKUP with the four input clocks.
>>> And then "Table 3-84. CD_WKUP Control and Status Parameters" shows
>>> the CD_WKUP clock domain specific registers. These registers show
>>> the status, I think they are all read-only registers. Then CD_WKUP
>>> has multiple child clocks with configurable registers.
>>>
>>> From hardware register point of view, each clock domain has:
>>>
>>> - Read-only clockdomain status registers in the beginning of
>>> the address space
>>>
>>> - Multiple similar clock instances register instances each
>>> mapping to a specific interconnect target module
>>>
>>> These are documented in "3.11.16.1 WKUP_CM Register Summary".
>>
>> Oh, this is because you are treating the MODULEMODE bits like gate
>> clocks. I never really figured out if this was the best way to model
>> those bits since they do more than control a line toggling at a rate.
>> For instance this bit will affect the master/slave IDLE protocol between
>> the module and the PRCM.
>
> Yes seems like there is some negotiation going on there with the
> target module. But from practical point of view the CLKCTRL
> register is the gate for a module functional clock.
There's some confusion on this, clockdomain is effectively a collection
of clocks, and can be used to force control that collection if needed.
Chapter "3.1.1.1.3 Clock Domain" in some OMAP4 TRM shows the
relationship neatly.
>
>>> From hardware point of view, we ideally want to map interconnect
>>> target modules to the clock instance offset from the clock domain
>>> for that interconnect segment. For example gptimer1 clocks would
>>> be just:
>>>
>>> clocks = <&cd_wkup 0x40>;
>>>
>>>> 2) why aren't the clock domains modeled as genpds with their associated
>>>> devices attached to them? Note that it is possible to "nest" genpd
>>>> objects. This would also allow for the "Clockdomain Dependency"
>>>> relationships to be properly modeled (see section 3.1.1.1.7 Clock Domain
>>>> Dependency in the OMAP4 TRM).
>>>
>>> Clock domains only route clocks to child clocks. Power domains
>>> are different registers. The power domains map roughly to
>>> interconnect instances, there we have registers to disable the
>>> whole interconnect when idle.
>>
>> I'm not talking about power islands at all, but the genpd object in
>> Linux. For instance, if we treat each clock domain like a clock
>> provider, how could the functional dependency between clkdm_A and
>> clkdm_B be asserted?
>
> To me it seems that some output of a clockdomain is just a input
> of another clockdomain? So it's just the usual parent child
> relationship once we treat a clockdomain just as a clock. Tero
> probably has some input here.
A clockdomain should be modelled as a genpd, that I agree. However, it
doesn't prevent it from being a clock provider also, or does it?
>> There is certainly no API for that in the clock framework, but for genpd
>> your runtime_pm_get() callback for clkdm_A could call runtime_pm_get
>> against clkdm_B, which would satisfy the requirement. See section
>> 3.1.1.1.7 Clock Domain Dependency in the OMAP4 TRM, version AB.
For static dependencies the apis genpd_add/remove_subdomain could
probably be used.
> To me it seems the API is just clk_get() :) Do you have some
> specific example we can use to check? My guess is that the
> TRM "Clock Domain Dependency" is just the usual parent child
> relationship between clocks that are the clockdomains..
>
> If there is something more magical there certainly that should
> be considered though.
The hwmods could be transformed to individual genpds also I guess. On DT
level though, we would still need a clock pointer to the main clock and
a genpd pointer in addition to that.
Tony, any thoughts on that? Would this break up the plans for the
interconnect completely?
-Tero
^ permalink raw reply
* Re: [PATCH 2/8] dt-bindings: document the STM32 RTC bindings
From: Alexandre Belloni @ 2016-12-05 10:06 UTC (permalink / raw)
To: Amelie Delaunay
Cc: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
gabriel.fernandez-qxv4g6HH51o
In-Reply-To: <1480687801-19525-4-git-send-email-amelie.delaunay-qxv4g6HH51o@public.gmane.org>
Hi,
On 02/12/2016 at 15:09:55 +0100, Amelie Delaunay wrote :
> This patch adds documentation of device tree bindings for the STM32 RTC.
>
> Signed-off-by: Amelie Delaunay <amelie.delaunay-qxv4g6HH51o@public.gmane.org>
> ---
> .../devicetree/bindings/rtc/st,stm32-rtc.txt | 31 ++++++++++++++++++++++
> 1 file changed, 31 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
>
> diff --git a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
> new file mode 100644
> index 0000000..4578838
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt
> @@ -0,0 +1,31 @@
> +STM32 Real Time Clock
> +
> +Required properties:
> +- compatible: "st,stm32-rtc".
> +- reg: address range of rtc register set.
> +- clocks: reference to the clock entry ck_rtc.
> +- clock-names: name of the clock used. Should be "ck_rtc".
Is this name really useful?
> +- interrupt-parent: phandle for the interrupt controller.
> +- interrupts: rtc alarm interrupt.
> +- interrupt-names: rtc alarm interrupt name, should be "alarm".
Same comment, is this name really useful?
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH 3/8] rtc: add STM32 RTC driver
From: Amelie DELAUNAY @ 2016-12-05 9:45 UTC (permalink / raw)
To: Mathieu Poirier
Cc: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
Alexandre TORGUE, linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org,
rtc-linux-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Gabriel FERNANDEZ
In-Reply-To: <20161202180527.GB3284-QSEj5FYQhm4dnm+yROfE0A@public.gmane.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-qxv4g6HH51o@public.gmane.org>
>> ---
>> 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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + * 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-qxv4g6HH51o@public.gmane.org>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 1.9.1
>>
Best regards,
Amelie
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH 3/8] rtc: add STM32 RTC driver
From: Amelie DELAUNAY @ 2016-12-05 9:43 UTC (permalink / raw)
To: Mathieu Poirier
Cc: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ@public.gmane.org,
alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
Alexandre TORGUE, linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org,
rtc-linux-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Gabriel FERNANDEZ
In-Reply-To: <20161202175646.GA3284-QSEj5FYQhm4dnm+yROfE0A@public.gmane.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-qxv4g6HH51o@public.gmane.org>
>> ---
>> 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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + * 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-qxv4g6HH51o@public.gmane.org>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 1.9.1
>>
Best regards,
Amelie
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [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: Icenowy Zheng
Cc: Mark Rutland, devicetree@vger.kernel.org, Vishnu Patekar,
Arnd Bergmann, linux-doc@vger.kernel.org, André Przywara,
Jonathan Corbet, linux-kernel, Russell King, Hans de Goede,
Chen-Yu Tsai, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20161205120021.0GBGtAl4@smtp3m.mail.yandex.net>
[-- Attachment #1.1: Type: text/plain, Size: 2277 bytes --]
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
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] ARM: dts: sunxi: Add num-cs for A20 spi nodes
From: Emmanuel Vadot @ 2016-12-05 9:39 UTC (permalink / raw)
To: Maxime Ripard
Cc: mark.rutland, devicetree, linux, linux-kernel, wens, robh+dt,
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
* Re: [PATCH] ARM: dts: sunxi: Add num-cs for A20 spi nodes
From: Maxime Ripard @ 2016-12-05 9:28 UTC (permalink / raw)
To: Emmanuel Vadot
Cc: mark.rutland, devicetree, linux, linux-kernel, wens, robh+dt,
linux-arm-kernel
In-Reply-To: <20161201112414.62f9b351186115f62c8998a9@bidouilliste.com>
[-- Attachment #1: Type: text/plain, Size: 1187 bytes --]
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
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply
* Re: [PATCH v3 -next 1/2] ARM: sunxi: add support for H2+ SoC
From: Maxime Ripard @ 2016-12-05 9:19 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Chen-Yu Tsai, Rob Herring, Russell King, Andre Przywara,
Hans de Goede, Arnd Bergmann, Vishnu Patekar,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20161202150513.34691-1-icenowy-ymACFijhrKM@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 490 bytes --]
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-ymACFijhrKM@public.gmane.org>
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
^ permalink raw reply
* Re: [PATCH v6 2/2] mtd: nand: Add support for Arasan Nand Flash Controller
From: kbuild test robot @ 2016-12-05 9:16 UTC (permalink / raw)
To: Punnaiah Choudary Kalluri
Cc: mark.rutland, boris.brezillon, devicetree, richard, linux-kernel,
Punnaiah Choudary Kalluri, marek.vasut, robh+dt, linux-mtd,
kbuild-all, kalluripunnaiahchoudary, kpc528, michals,
cyrille.pitchen, computersforpeace, dwmw2
In-Reply-To: <1480911066-26157-2-git-send-email-punnaia@xilinx.com>
[-- Attachment #1: Type: text/plain, Size: 3489 bytes --]
Hi Punnaiah,
[auto build test ERROR on mtd/master]
[also build test ERROR on v4.9-rc8 next-20161205]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Punnaiah-Choudary-Kalluri/mtd-arasan-Add-device-tree-binding-documentation/20161205-162929
base: git://git.infradead.org/linux-mtd.git master
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
drivers/mtd/nand/arasan_nand.c: In function 'anfc_read_buf':
>> drivers/mtd/nand/arasan_nand.c:348:3: error: implicit declaration of function 'readsl' [-Werror=implicit-function-declaration]
readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
^~~~~~
drivers/mtd/nand/arasan_nand.c: In function 'anfc_write_buf':
>> drivers/mtd/nand/arasan_nand.c:402:3: error: implicit declaration of function 'writesl' [-Werror=implicit-function-declaration]
writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
^~~~~~~
cc1: some warnings being treated as errors
vim +/readsl +348 drivers/mtd/nand/arasan_nand.c
342 anfc_wait_for_event(nfc);
343 buf_rd_cnt++;
344
345 if (buf_rd_cnt == pktcount)
346 anfc_enable_intrs(nfc, XFER_COMPLETE);
347
> 348 readsl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
349 bufptr += (pktsize / 4);
350
351 if (buf_rd_cnt < pktcount)
352 anfc_enable_intrs(nfc, (READ_READY | eccintr));
353 }
354
355 anfc_wait_for_event(nfc);
356 }
357
358 static void anfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
359 {
360 u32 pktcount, pktsize;
361 unsigned int buf_wr_cnt = 0;
362 u32 *bufptr = (u32 *)buf;
363 struct nand_chip *chip = mtd_to_nand(mtd);
364 struct anfc_nand_chip *achip = to_anfc_nand(chip);
365 struct anfc *nfc = to_anfc(chip->controller);
366 dma_addr_t paddr;
367
368 if (nfc->iswriteoob) {
369 pktsize = len;
370 pktcount = 1;
371 } else {
372 pktsize = achip->pktsize;
373 pktcount = mtd->writesize / pktsize;
374 }
375
376 anfc_setpktszcnt(nfc, pktsize, pktcount);
377
378 if (nfc->dma) {
379 paddr = dma_map_single(nfc->dev, (void *)buf, len,
380 DMA_TO_DEVICE);
381 if (dma_mapping_error(nfc->dev, paddr)) {
382 dev_err(nfc->dev, "Write buffer mapping error");
383 return;
384 }
385 lo_hi_writeq(paddr, nfc->base + DMA_ADDR0_OFST);
386 anfc_enable_intrs(nfc, XFER_COMPLETE);
387 writel(PROG_PGPROG, nfc->base + PROG_OFST);
388 anfc_wait_for_event(nfc);
389 dma_unmap_single(nfc->dev, paddr, len, DMA_TO_DEVICE);
390 return;
391 }
392
393 anfc_enable_intrs(nfc, WRITE_READY);
394 writel(PROG_PGPROG, nfc->base + PROG_OFST);
395
396 while (buf_wr_cnt < pktcount) {
397 anfc_wait_for_event(nfc);
398 buf_wr_cnt++;
399 if (buf_wr_cnt == pktcount)
400 anfc_enable_intrs(nfc, XFER_COMPLETE);
401
> 402 writesl(nfc->base + DATA_PORT_OFST, bufptr, pktsize/4);
403 bufptr += (pktsize / 4);
404
405 if (buf_wr_cnt < pktcount)
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 56586 bytes --]
[-- Attachment #3: Type: text/plain, Size: 144 bytes --]
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* Re: [PATCH 2/2] HID: i2c-hid: support regulator power on/off
From: Benjamin Tissoires @ 2016-12-05 9:14 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Brian Norris, Jiri Kosina, Caesar Wang,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring,
linux-input-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mark Rutland, Doug Anderson
In-Reply-To: <20161202230013.GA18268@dtor-ws>
On Dec 02 2016 or thereabouts, Dmitry Torokhov wrote:
> On Fri, Dec 02, 2016 at 02:19:00PM -0800, Brian Norris wrote:
> > On some boards, we need to enable a regulator before using the HID, and
> > it's also nice to save power in suspend by disabling it. Support an
> > optional "vdd-supply" and a companion initialization delay.
> >
> > Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> > Signed-off-by: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> > Cc: Jiri Kosina <jikos-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > ---
> > v2:
> > * support compatible property for wacom, with specific "vdd-supply"
> > * name
> > * support the 100ms delay needed for this digitizer
> > * target regulator support only at specific device
> >
> > v3:
> > * drop Wacom specifics and allow this to be used generically
> > * add "init-delay-ms" property support
> >
> > v4:
> > * use devm_regulator_get() (with a 'dummy' regulator for most cases)
> > instead of _optional() version, to make code less conditional (Dmitry)
> > * fix but where 'init_delay_ms' wasn't getting assigned properly
> > * disable regulator in probe() failure path
> > ---
> > drivers/hid/i2c-hid/i2c-hid.c | 44 +++++++++++++++++++++++++++++++++++++++++--
> > include/linux/i2c/i2c-hid.h | 6 ++++++
> > 2 files changed, 48 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
> > index b3ec4f2de875..5c6e037613d7 100644
> > --- a/drivers/hid/i2c-hid/i2c-hid.c
> > +++ b/drivers/hid/i2c-hid/i2c-hid.c
> > @@ -38,6 +38,7 @@
> > #include <linux/acpi.h>
> > #include <linux/of.h>
> > #include <linux/gpio/consumer.h>
> > +#include <linux/regulator/consumer.h>
> >
> > #include <linux/i2c/i2c-hid.h>
> >
> > @@ -937,6 +938,10 @@ static int i2c_hid_of_probe(struct i2c_client *client,
> > }
> > pdata->hid_descriptor_address = val;
> >
> > + ret = of_property_read_u32(dev->of_node, "init-delay-ms", &val);
> > + if (!ret)
> > + pdata->init_delay_ms = val;
> > +
> > return 0;
> > }
> >
> > @@ -983,6 +988,15 @@ static int i2c_hid_probe(struct i2c_client *client,
> > ihid->pdata = *platform_data;
> > }
> >
> > + ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd");
>
> Oh, haveni't noticed that the rest of the driver does not use devm.
> Well, I'll leave it up to hid-i2c folks to decide if they are OK with
> mixing up the managed and non-managed resources, it seems safe to me in
> this case.
Yeah, it looks good to me as well. However, I think that will be the
trigger to request a devm conversion of the i2c-hid driver. Something
I'll add to my list of things to consider for v4.11 then :)
Acked-by: Benjamin Tissoires <benjamin.tissoires-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Cheers,
Benjamin
>
> Reviewed-by: Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> Thanks.
>
> --
> Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v2 6/6] net: smmac: allow configuring lower pbl values
From: Niklas Cassel @ 2016-12-05 9:12 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Jonathan Corbet, Giuseppe Cavallaro,
Alexandre Torgue, David S. Miller, Phil Reid, Niklas Cassel,
Eric Engestrom, Pavel Machek, Joachim Eastwood, Gabriel Fernandez,
Vincent Palatin
Cc: netdev, devicetree, linux-kernel, linux-doc
In-Reply-To: <1480929155-20462-1-git-send-email-niklass@axis.com>
From: Niklas Cassel <niklas.cassel@axis.com>
The driver currently always sets the PBLx8/PBLx4 bit, which means that
the pbl values configured via the pbl/txpbl/rxpbl DT properties are
always multiplied by 8/4 in the hardware.
In order to allow the DT to configure lower pbl values, while at the
same time not changing behavior of any existing device trees using the
pbl/txpbl/rxpbl settings, add a property to disable the multiplication
of the pbl by 8/4 in the hardware.
Suggested-by: Rabin Vincent <rabinv@axis.com>
Signed-off-by: Niklas Cassel <niklas.cassel@axis.com>
---
Documentation/devicetree/bindings/net/stmmac.txt | 2 ++
Documentation/networking/stmmac.txt | 5 ++++-
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 3 ++-
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 3 ++-
drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 2 ++
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 +
include/linux/stmmac.h | 1 +
7 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index a3dc1453dffb..1fb3c309c558 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -39,6 +39,8 @@ Optional properties:
If set, DMA tx will use this value rather than snps,pbl.
- snps,rxpbl Rx Programmable Burst Length. Only for GMAC and newer.
If set, DMA rx will use this value rather than snps,pbl.
+- snps,no-pbl-x8 Don't multiply the pbl/txpbl/rxpbl values by 8.
+ For core rev < 3.50, don't multiply the values by 4.
- snps,aal Address-Aligned Beats
- snps,fixed-burst Program the DMA to use the fixed burst mode
- snps,mixed-burst Program the DMA to use the mixed burst mode
diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt
index 6add57374f70..2bb07078f535 100644
--- a/Documentation/networking/stmmac.txt
+++ b/Documentation/networking/stmmac.txt
@@ -152,8 +152,9 @@ Where:
o dma_cfg: internal DMA parameters
o pbl: the Programmable Burst Length is maximum number of beats to
be transferred in one DMA transaction.
- GMAC also enables the 4xPBL by default.
+ GMAC also enables the 4xPBL by default. (8xPBL for GMAC 3.50 and newer)
o txpbl/rxpbl: GMAC and newer supports independent DMA pbl for tx/rx.
+ o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default.
o fixed_burst/mixed_burst/aal
o clk_csr: fixed CSR Clock range selection.
o has_gmac: uses the GMAC core.
@@ -208,6 +209,7 @@ struct stmmac_dma_cfg {
int pbl;
int txpbl;
int rxpbl;
+ bool pblx8;
int fixed_burst;
int mixed_burst;
bool aal;
@@ -219,6 +221,7 @@ Where:
If set, DMA tx will use this value rather than pbl.
o rxpbl: Receive Programmable Burst Length. Only for GMAC and newer.
If set, DMA rx will use this value rather than pbl.
+ o pblx8: Enable 8xPBL (4xPBL for core rev < 3.50). Enabled by default.
o fixed_burst: program the DMA to use the fixed burst mode
o mixed_burst: program the DMA to use the mixed burst mode
o aal: Address-Aligned Beats
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 1dd34fb4c1a9..1d313af647b4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -96,7 +96,8 @@ static void dwmac1000_dma_init(void __iomem *ioaddr,
* Note: before stmmac core 3.50 this mode bit was 4xPBL, and
* post 3.5 mode bit acts as 8*PBL.
*/
- value |= DMA_BUS_MODE_MAXPBL;
+ if (dma_cfg->pblx8)
+ value |= DMA_BUS_MODE_MAXPBL;
value |= DMA_BUS_MODE_USP;
value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK);
value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 0bf47825bfeb..0f7110d19a4a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -82,7 +82,8 @@ static void dwmac4_dma_init_channel(void __iomem *ioaddr,
* on each channel
*/
value = readl(ioaddr + DMA_CHAN_CONTROL(channel));
- value = value | DMA_BUS_MODE_PBL;
+ if (dma_cfg->pblx8)
+ value = value | DMA_BUS_MODE_PBL;
writel(value, ioaddr + DMA_CHAN_CONTROL(channel));
value = readl(ioaddr + DMA_CHAN_TX_CONTROL(channel));
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
index 56c8a2342c14..a2831773431a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
@@ -81,6 +81,7 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat)
plat->mdio_bus_data->phy_mask = 0;
plat->dma_cfg->pbl = 32;
+ plat->dma_cfg->pblx8 = true;
/* TODO: AXI */
/* Set default value for multicast hash bins */
@@ -115,6 +116,7 @@ static int quark_default_data(struct plat_stmmacenet_data *plat,
plat->mdio_bus_data->phy_mask = 0;
plat->dma_cfg->pbl = 16;
+ plat->dma_cfg->pblx8 = true;
plat->dma_cfg->fixed_burst = 1;
/* AXI (TODO) */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 55cac48897f6..5f1d0ade643f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -312,6 +312,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
of_property_read_u32(np, "snps,txpbl", &dma_cfg->txpbl);
of_property_read_u32(np, "snps,rxpbl", &dma_cfg->rxpbl);
+ dma_cfg->pblx8 = !of_property_read_bool(np, "snps,no-pbl-x8");
dma_cfg->aal = of_property_read_bool(np, "snps,aal");
dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst");
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index e6d7a5940819..266dab9ad782 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -90,6 +90,7 @@ struct stmmac_dma_cfg {
int pbl;
int txpbl;
int rxpbl;
+ bool pblx8;
int fixed_burst;
int mixed_burst;
bool aal;
--
2.1.4
^ permalink raw reply related
* [PATCH v2 5/6] net: stmmac: add support for independent DMA pbl for tx/rx
From: Niklas Cassel @ 2016-12-05 9:12 UTC (permalink / raw)
To: Rob Herring, Mark Rutland, Jonathan Corbet, Giuseppe Cavallaro,
Alexandre Torgue, David S. Miller, Niklas Cassel, Phil Reid,
Eric Engestrom, Pavel Machek, Joachim Eastwood, Gabriel Fernandez,
Vincent Palatin
Cc: netdev, devicetree, linux-kernel, linux-doc
From: Niklas Cassel <niklas.cassel@axis.com>
GMAC and newer supports independent programmable burst lengths for
DMA tx/rx. Add new optional devicetree properties representing this.
To be backwards compatible, snps,pbl will still be valid, but
snps,txpbl/snps,rxpbl will override the value in snps,pbl if set.
If the IP is synthesized to use the AXI interface, there is a register
and a matching DT property inside the optional stmmac-axi-config DT node
for controlling burst lengths, named snps,blen.
However, using this register, it is not possible to control tx and rx
independently. Also, this register is not available if the IP was
synthesized with, e.g., the AHB interface.
Signed-off-by: Niklas Cassel <niklas.cassel@axis.com>
---
Documentation/devicetree/bindings/net/stmmac.txt | 6 +++++-
Documentation/networking/stmmac.txt | 19 +++++++++++++------
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 12 ++++++------
drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 12 +++++++-----
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 ++
include/linux/stmmac.h | 2 ++
6 files changed, 35 insertions(+), 18 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt
index 41b49e6075f5..a3dc1453dffb 100644
--- a/Documentation/devicetree/bindings/net/stmmac.txt
+++ b/Documentation/devicetree/bindings/net/stmmac.txt
@@ -34,7 +34,11 @@ Optional properties:
platforms.
- tx-fifo-depth: See ethernet.txt file in the same directory
- rx-fifo-depth: See ethernet.txt file in the same directory
-- snps,pbl Programmable Burst Length
+- snps,pbl Programmable Burst Length (tx and rx)
+- snps,txpbl Tx Programmable Burst Length. Only for GMAC and newer.
+ If set, DMA tx will use this value rather than snps,pbl.
+- snps,rxpbl Rx Programmable Burst Length. Only for GMAC and newer.
+ If set, DMA rx will use this value rather than snps,pbl.
- snps,aal Address-Aligned Beats
- snps,fixed-burst Program the DMA to use the fixed burst mode
- snps,mixed-burst Program the DMA to use the mixed burst mode
diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt
index 014f4f756cb7..6add57374f70 100644
--- a/Documentation/networking/stmmac.txt
+++ b/Documentation/networking/stmmac.txt
@@ -153,7 +153,8 @@ Where:
o pbl: the Programmable Burst Length is maximum number of beats to
be transferred in one DMA transaction.
GMAC also enables the 4xPBL by default.
- o fixed_burst/mixed_burst/burst_len
+ o txpbl/rxpbl: GMAC and newer supports independent DMA pbl for tx/rx.
+ o fixed_burst/mixed_burst/aal
o clk_csr: fixed CSR Clock range selection.
o has_gmac: uses the GMAC core.
o enh_desc: if sets the MAC will use the enhanced descriptor structure.
@@ -205,16 +206,22 @@ tuned according to the HW capabilities.
struct stmmac_dma_cfg {
int pbl;
+ int txpbl;
+ int rxpbl;
int fixed_burst;
- int burst_len_supported;
+ int mixed_burst;
+ bool aal;
};
Where:
- o pbl: Programmable Burst Length
+ o pbl: Programmable Burst Length (tx and rx)
+ o txpbl: Transmit Programmable Burst Length. Only for GMAC and newer.
+ If set, DMA tx will use this value rather than pbl.
+ o rxpbl: Receive Programmable Burst Length. Only for GMAC and newer.
+ If set, DMA rx will use this value rather than pbl.
o fixed_burst: program the DMA to use the fixed burst mode
- o burst_len: this is the value we put in the register
- supported values are provided as macros in
- linux/stmmac.h header file.
+ o mixed_burst: program the DMA to use the mixed burst mode
+ o aal: Address-Aligned Beats
---
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 01d0d0f315e5..1dd34fb4c1a9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -87,20 +87,20 @@ static void dwmac1000_dma_init(void __iomem *ioaddr,
u32 dma_tx, u32 dma_rx, int atds)
{
u32 value = readl(ioaddr + DMA_BUS_MODE);
+ int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
+ int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl;
/*
* Set the DMA PBL (Programmable Burst Length) mode.
*
* Note: before stmmac core 3.50 this mode bit was 4xPBL, and
* post 3.5 mode bit acts as 8*PBL.
- *
- * This configuration doesn't take care about the Separate PBL
- * so only the bits: 13-8 are programmed with the PBL passed from the
- * platform.
*/
value |= DMA_BUS_MODE_MAXPBL;
- value &= ~DMA_BUS_MODE_PBL_MASK;
- value |= (dma_cfg->pbl << DMA_BUS_MODE_PBL_SHIFT);
+ value |= DMA_BUS_MODE_USP;
+ value &= ~(DMA_BUS_MODE_PBL_MASK | DMA_BUS_MODE_RPBL_MASK);
+ value |= (txpbl << DMA_BUS_MODE_PBL_SHIFT);
+ value |= (rxpbl << DMA_BUS_MODE_RPBL_SHIFT);
/* Set the Fixed burst mode */
if (dma_cfg->fixed_burst)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 0946546d6dcd..0bf47825bfeb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -69,11 +69,14 @@ static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
writel(value, ioaddr + DMA_SYS_BUS_MODE);
}
-static void dwmac4_dma_init_channel(void __iomem *ioaddr, int pbl,
+static void dwmac4_dma_init_channel(void __iomem *ioaddr,
+ struct stmmac_dma_cfg *dma_cfg,
u32 dma_tx_phy, u32 dma_rx_phy,
u32 channel)
{
u32 value;
+ int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
+ int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl;
/* set PBL for each channels. Currently we affect same configuration
* on each channel
@@ -83,11 +86,11 @@ static void dwmac4_dma_init_channel(void __iomem *ioaddr, int pbl,
writel(value, ioaddr + DMA_CHAN_CONTROL(channel));
value = readl(ioaddr + DMA_CHAN_TX_CONTROL(channel));
- value = value | (pbl << DMA_BUS_MODE_PBL_SHIFT);
+ value = value | (txpbl << DMA_BUS_MODE_PBL_SHIFT);
writel(value, ioaddr + DMA_CHAN_TX_CONTROL(channel));
value = readl(ioaddr + DMA_CHAN_RX_CONTROL(channel));
- value = value | (pbl << DMA_BUS_MODE_RPBL_SHIFT);
+ value = value | (rxpbl << DMA_BUS_MODE_RPBL_SHIFT);
writel(value, ioaddr + DMA_CHAN_RX_CONTROL(channel));
/* Mask interrupts by writing to CSR7 */
@@ -118,8 +121,7 @@ static void dwmac4_dma_init(void __iomem *ioaddr,
writel(value, ioaddr + DMA_SYS_BUS_MODE);
for (i = 0; i < DMA_CHANNEL_NB_MAX; i++)
- dwmac4_dma_init_channel(ioaddr, dma_cfg->pbl,
- dma_tx, dma_rx, i);
+ dwmac4_dma_init_channel(ioaddr, dma_cfg, dma_tx, dma_rx, i);
}
static void _dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 channel)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 59e1740479fc..55cac48897f6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -310,6 +310,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
plat->dma_cfg = dma_cfg;
of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
+ of_property_read_u32(np, "snps,txpbl", &dma_cfg->txpbl);
+ of_property_read_u32(np, "snps,rxpbl", &dma_cfg->rxpbl);
dma_cfg->aal = of_property_read_bool(np, "snps,aal");
dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst");
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 3537fb33cc90..e6d7a5940819 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -88,6 +88,8 @@ struct stmmac_mdio_bus_data {
struct stmmac_dma_cfg {
int pbl;
+ int txpbl;
+ int rxpbl;
int fixed_burst;
int mixed_burst;
bool aal;
--
2.1.4
^ permalink raw reply related
* Re: [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: Icenowy Zheng
Cc: Jernej Skrabec, linux-sunxi, arnd-r2nGTMty4D4,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
vishnupatekar0510-Re5JQEeQqe8AvxtiuMwx3w,
andre.przywara-5wv7dgnIgG8,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, hdegoede-H+wXaHxf7aLQT0dZR+AlfA,
wens-jdAy2FN1RRM, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <20161203162455.OlDm27ch-jDEamKawf7I0PDqKvflMoHmW9unr2Ajn@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1099 bytes --]
Hi Icenowy,
On Sat, Dec 03, 2016 at 09:24:19PM +0800, Icenowy Zheng wrote:
> <p dir="ltr"><br>
> 2016年12月3日 下午5:43于 Jernej Skrabec <jernej.skrabec-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>写道:<br>
> ><br>
> > Hi,<br>
> ><br>
> > Dne petek, 02. december 2016 17.42.04 UTC+1 je oseba Chen-Yu Tsai napisala:<br>
> >><br>
> >> Hi, <br>
> >><br>
> >> On Fri, Dec 2, 2016 at 11:05 PM, Icenowy Zheng <ice...-ymACFijhrKM@public.gmane.org> wrote: <br>
> >> > Orange Pi Zero is a board that came with the new Allwinner H2+ SoC and a <br>
> >> > SDIO Wi-Fi chip by Allwinner (XR819). <br>
> >> > <br>
> >> > Add a device tree file for it. <br>
> >> > <br>
> >> > Signed-off-by: Icenowy Zheng <ice...-ymACFijhrKM@public.gmane.org> <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
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox