pwm: add support for atmel-hlcdc-pwm device
Subsystems (3)
v9 cover pwm: add support for atmel-hlcdc-pwm device
Hi Thierry,
This patch series adds support for the atmel-hlcdc-pwm device provided by the atmel-hlcdc MFD driver.
It depends on this series [1] implementing the MFD driver.
Best Regards,
Boris
[1]http://www.mail-archive.com/devicetree at vger.kernel.org/msg44979.html
Changes since v8:
- rework the enable/disable waiting loop
- check return code of regmap functions
- fix DT binding doc
Changes since v7:
- addressed several coding style issues
Boris Brezillon (2):
pwm: add support for atmel-hlcdc-pwm device
pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
.../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt | 29 +++ drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atmel-hlcdc.c | 259 +++++++++++++++++++++ 4 files changed, 298 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
Signature · 1 line
1.9.1
PATCH v9 1/2 pwm: add support for atmel-hlcdc-pwm device
The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12, at91sam9x5 family or sama5d3 family) provide a PWM device.
This driver add support for a PWM chip exposing a single PWM device (which will most likely be used to drive a backlight device).
--- drivers/pwm/Kconfig | 9 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atmel-hlcdc.c | 259 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
drivers/pwm/Kconfig +9 -0
@@ -50,6 +50,15 @@ config PWM_ATMEL To compile this driver as a module, choose M here: the module will be called pwm-atmel. +config PWM_ATMEL_HLCDC_PWM + tristate "Atmel HLCDC PWM support" + select MFD_ATMEL_HLCDC + help + Generic PWM framework driver for Atmel HLCDC PWM. + + To compile this driver as a module, choose M here: the module + will be called pwm-atmel-hlcdc. + config PWM_ATMEL_TCB tristate "Atmel TC Block PWM support" depends on ATMEL_TCLIB && OF
drivers/pwm/Makefile +1 -0
@@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o +obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
drivers/pwm/pwm-atmel-hlcdc.c new file +259 -0
259 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH v9 1/2 pwm: add support for atmel-hlcdc-pwm device
On 07/10/2014 12:14, Boris Brezillon :
Quoted 1 line
The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
What is this sam9x5i.e. ? I would say instead:
The HLCDC IP available in some Atmel SoCs (i.e. at91sam9x5, at91sam9n12 or sama5d3 families for instance) provides a PWM device.
Quoted 9 lines
at91sam9x5 family or sama5d3 family) provide a PWM device.
This driver add support for a PWM chip exposing a single PWM device (which will most likely be used to drive a backlight device).
Signed-off-by Boris Brezillon <[email protected]>Tested-by Anthony Harivel <[email protected]>Tested-by Ludovic Desroches <[email protected]>Acked-by Thierry Reding <[email protected]>
See the little nitpicking...
But otherwise:
Quoted 12 lines
--- drivers/pwm/Kconfig | 9 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atmel-hlcdc.c | 259 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 drivers/pwm/pwm-atmel-hlcdc.cdrivers/pwm/Kconfig +0
-0
drivers/pwm/Kconfig config PWM_ATMEL · line 57
Quoted 8 lines
drivers/pwm/Kconfig +5
-0@@ -50,6 +50,15 @@ config PWM_ATMEL To compile this driver as a module, choose M here: the module will be called pwm-atmel. +config PWM_ATMEL_HLCDC_PWM + tristate "Atmel HLCDC PWM support" + select MFD_ATMEL_HLCDC + help + Generic PWM framework driver for Atmel HLCDC PWM.
What about:
"Generic PWM framework driver for the PWM output of the HLCDC (Atmel High-end LCD Controller). This PWM output is mainly used to control the LCD backlight.
Quoted 285 lines
drivers/pwm/Kconfig +4
-0+ + To compile this driver as a module, choose M here: the module + will be called pwm-atmel-hlcdc. + config PWM_ATMEL_TCB tristate "Atmel TC Block PWM support" depends on ATMEL_TCLIB && OFdrivers/pwm/Makefile +1
-0@@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o +obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.odrivers/pwm/pwm-atmel-hlcdc.c new file +259
-0@@ -0,0 +1,259 @@ +/* +* Copyright (C) 2014 Free Electrons +* Copyright (C) 2014 Atmel +* +* Author: Boris BREZILLON <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 as published by +* the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/mfd/atmel-hlcdc.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/regmap.h> + +#define ATMEL_HLCDC_PWMCVAL_MASK GENMASK(15, 8) +#define ATMEL_HLCDC_PWMCVAL (x) (((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK) +#define ATMEL_HLCDC_PWMPOL BIT(4) +#define ATMEL_HLCDC_PWMPS_MASK GENMASK(2, 0) +#define ATMEL_HLCDC_PWMPS_MAX 0x6 +#define ATMEL_HLCDC_PWMPS (x) ((x) & ATMEL_HLCDC_PWMPS_MASK) + +struct atmel_hlcdc_pwm { +struct pwm_chip chip; +struct atmel_hlcdc *hlcdc; +struct clk *cur_clk; +}; + +static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm (struct pwm_chip *chip) +{ + return container_of(chip, struct atmel_hlcdc_pwm, chip); +} + +static int atmel_hlcdc_pwm_config (struct pwm_chip *c, +struct pwm_device *pwm, +int duty_ns,int period_ns) +{ +struct atmel_hlcdc_pwm *chip =to_atmel_hlcdc_pwm (c); +struct atmel_hlcdc *hlcdc = chip->hlcdc; +struct clk *new_clk = hlcdc->slow_clk; +u64 pwmcval = duty_ns *256 ; +unsigned long clk_freq; +u64 clk_period_ns; +u32 pwmcfg; +int pres; + + clk_freq =clk_get_rate (new_clk); + clk_period_ns = (u64 )NSEC_PER_SEC *256 ; +do_div (clk_period_ns, clk_freq); + +if (clk_period_ns > period_ns) { + new_clk = hlcdc->sys_clk; + clk_freq =clk_get_rate (new_clk); + clk_period_ns = (u64 )NSEC_PER_SEC *256 ; +do_div (clk_period_ns, clk_freq); + } + +for (pres =0 ; pres <=ATMEL_HLCDC_PWMPS_MAX ; pres++) +if ((clk_period_ns << pres) >= period_ns) +break ; + +if (pres >ATMEL_HLCDC_PWMPS_MAX ) +return -EINVAL ; + + pwmcfg =ATMEL_HLCDC_PWMPS (pres); + +if (new_clk != chip->cur_clk) { +u32 gencfg =0 ; +int ret; + + ret =clk_prepare_enable (new_clk); +if (ret) +return ret; + +clk_disable_unprepare (chip->cur_clk); + chip->cur_clk = new_clk; + +if (new_clk == hlcdc->sys_clk) + gencfg =ATMEL_HLCDC_CLKPWMSEL ; + + ret =regmap_update_bits (hlcdc->regmap,ATMEL_HLCDC_CFG (0 ), +ATMEL_HLCDC_CLKPWMSEL , gencfg); +if (ret) +return ret; + } + +do_div (pwmcval, period_ns); + +/* +* The PWM duty cycle is configurable from 0/256 to 255/256 of the +* period cycle. Hence we can't set a duty cycle occupying the +* whole period cycle if we're asked to. +* Set it to 255 if pwmcval is greater than 256. +*/ +if (pwmcval >255 ) + pwmcval =255 ; + + pwmcfg |=ATMEL_HLCDC_PWMCVAL (pwmcval); + +return regmap_update_bits (hlcdc->regmap,ATMEL_HLCDC_CFG (6 ), +ATMEL_HLCDC_PWMCVAL_MASK | +ATMEL_HLCDC_PWMPS_MASK , + pwmcfg); +} + +static int atmel_hlcdc_pwm_set_polarity (struct pwm_chip *c, +struct pwm_device *pwm, +enum pwm_polarity polarity) +{ +struct atmel_hlcdc_pwm *chip =to_atmel_hlcdc_pwm (c); +struct atmel_hlcdc *hlcdc = chip->hlcdc; +u32 cfg =0 ; + +if (polarity ==PWM_POLARITY_NORMAL ) + cfg =ATMEL_HLCDC_PWMPOL ; + +return regmap_update_bits (hlcdc->regmap,ATMEL_HLCDC_CFG (6 ), +ATMEL_HLCDC_PWMPOL , cfg); +} + +static int atmel_hlcdc_pwm_enable (struct pwm_chip *c,struct pwm_device *pwm) +{ +struct atmel_hlcdc_pwm *chip =to_atmel_hlcdc_pwm (c); +struct atmel_hlcdc *hlcdc = chip->hlcdc; +u32 status; +int ret; + + ret =regmap_write (hlcdc->regmap,ATMEL_HLCDC_EN ,ATMEL_HLCDC_PWM ); +if (ret) +return ret; + +while (true) { + ret =regmap_read (hlcdc->regmap,ATMEL_HLCDC_SR , &status); +if (ret) +return ret; + +if ((status &ATMEL_HLCDC_PWM ) !=0 ) +break ; + +usleep_range (1 ,10 ); + } + +return 0 ; +} + +static void atmel_hlcdc_pwm_disable (struct pwm_chip *c, +struct pwm_device *pwm) +{ +struct atmel_hlcdc_pwm *chip =to_atmel_hlcdc_pwm (c); +struct atmel_hlcdc *hlcdc = chip->hlcdc; +u32 status; +int ret; + + ret =regmap_write (hlcdc->regmap,ATMEL_HLCDC_DIS ,ATMEL_HLCDC_PWM ); +if (ret) +return ; + +while (true) { + ret =regmap_read (hlcdc->regmap,ATMEL_HLCDC_SR , &status); +if (ret) +return ; + +if ((status &ATMEL_HLCDC_PWM ) ==0 ) +break ; + +usleep_range (1 ,10 ); + } +} + +static const struct pwm_ops atmel_hlcdc_pwm_ops = { + .config = atmel_hlcdc_pwm_config, + .set_polarity = atmel_hlcdc_pwm_set_polarity, + .enable = atmel_hlcdc_pwm_enable, + .disable = atmel_hlcdc_pwm_disable, + .owner =THIS_MODULE , +}; + +static int atmel_hlcdc_pwm_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct atmel_hlcdc_pwm *chip; +struct atmel_hlcdc *hlcdc; +int ret; + + hlcdc =dev_get_drvdata (dev->parent); + + chip =devm_kzalloc (dev,sizeof (*chip),GFP_KERNEL ); +if (!chip) +return -ENOMEM ; + + ret =clk_prepare_enable (hlcdc->periph_clk); +if (ret) +return ret; + + chip->hlcdc = hlcdc; + chip->chip.ops = &atmel_hlcdc_pwm_ops; + chip->chip.dev = dev; + chip->chip.base =-1 ; + chip->chip.npwm =1 ; + chip->chip.of_xlate = of_pwm_xlate_with_flags; + chip->chip.of_pwm_n_cells =3 ; + chip->chip.can_sleep =1 ; + + ret =pwmchip_add (&chip->chip); +if (ret) { +clk_disable_unprepare (hlcdc->periph_clk); +return ret; + } + +platform_set_drvdata (pdev, chip); + +return 0 ; +} + +static int atmel_hlcdc_pwm_remove (struct platform_device *pdev) +{ +struct atmel_hlcdc_pwm *chip =platform_get_drvdata (pdev); +int ret; + + ret =pwmchip_remove (&chip->chip); +if (ret) +return ret; + +clk_disable_unprepare (chip->hlcdc->periph_clk); + +return 0 ; +} + +static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = { + { .compatible ="atmel,hlcdc-pwm" }, + {/* sentinel */ }, +}; + +static struct platform_driver atmel_hlcdc_pwm_driver = { + .driver = { + .name ="atmel-hlcdc-pwm" , + .of_match_table = atmel_hlcdc_pwm_dt_ids, + }, + .probe = atmel_hlcdc_pwm_probe, + .remove = atmel_hlcdc_pwm_remove, +}; +module_platform_driver (atmel_hlcdc_pwm_driver); + +MODULE_ALIAS ("platform:atmel-hlcdc-pwm" ); +MODULE_AUTHOR ("Boris Brezillon <[email protected]>" ); +MODULE_DESCRIPTION ("Atmel HLCDC PWM driver" ); +MODULE_LICENSE ("GPL v2" );
Signature · 1 line
Nicolas Ferre
PATCH v9 2/2 pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
In reply to Boris BrezillonThe HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12, at91sam9x5 family or sama5d3 family) provide a PWM device.
The DT bindings used for this PWM device is following the default 3 cells bindings described in Documentation/devicetree/bindings/pwm/pwm.txt.
--- .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt new file +29 -0
@@ -0,0 +1,29 @@ +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver + +The Atmel HLCDC PWM is subdevice of the HLCDC MFD device. +See ../mfd/atmel-hlcdc.txt for more details. + +Required properties: + - compatible: value should be one of the following: + "atmel,hlcdc-pwm" + - pinctr-names: the pin control state names. Should contain "default". + - pinctrl-0: should contain the pinctrl states described by pinctrl + default. + - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells + bindings defined in pwm.txt in this directory. + +Example: + + hlcdc: hlcdc at f0030000 { + compatible = "atmel,sama5d3-hlcdc"; + reg = <0xf0030000 0x2000>; + clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>; + clock-names = "periph_clk","sys_clk", "slow_clk"; + + hlcdc_pwm: hlcdc-pwm { + compatible = "atmel,hlcdc-pwm"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd_pwm>; + #pwm-cells = <3>; + }; + };
Signature · 1 line
1.9.1
PATCH v9 2/2 pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
On 07/10/2014 12:14, Boris Brezillon :
Quoted 1 line
The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
Ditto: sam9x5 doesn't exist.
Quoted 17 lines
at91sam9x5 family or sama5d3 family) provide a PWM device.
The DT bindings used for this PWM device is following the default 3 cells bindings described in
Documentation/devicetree/bindings/pwm/pwm.txt.Signed-off-by Boris Brezillon <[email protected]>--- .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txtDocumentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt new file +0
-0
Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt line 1
Quoted 1 line
Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt new file +1
-0@@ -0,0 +1,29 @@ +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver
s/High/High-end/
Quoted 19 lines
Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt new file +18
-0+ +The Atmel HLCDC PWM is subdevice of the HLCDC MFD device. +See ../mfd/atmel-hlcdc.txt for more details. + +Required properties: + - compatible: value should be one of the following: + "atmel,hlcdc-pwm" + - pinctr-names: the pin control state names. Should contain "default". + - pinctrl-0: should contain the pinctrl states described by pinctrl + default. + - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells + bindings defined in pwm.txt in this directory. + +Example: + + hlcdc: hlcdc at f0030000 { + compatible = "atmel,sama5d3-hlcdc"; + reg = <0xf0030000 0x2000>;
Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt line 29
Quoted 10 lines
Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt new file +10
-0+ clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>; + clock-names = "periph_clk","sys_clk", "slow_clk"; + + hlcdc_pwm: hlcdc-pwm { + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd_pwm>; + #pwm-cells = <3>; + }; + };
otherwise,
Thanks.
Signature · 1 line
Nicolas Ferre