On 06/09/2014 06:04 PM, Boris BREZILLON wrote:
> The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
> family or sama5d3 family) exposes 2 subdevices:
> - a display controller (controlled by a DRM driver)
> - a PWM chip
> 
> Add support for the MFD device which will just retrieve HLCDC clocks and
> create a regmap so that subdevices can access the HLCDC register range
> concurrently.
> 
> Signed-off-by: Boris BREZILLON <boris.brezillon at free-electrons.com>
> ---
>  .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  41 ++++++++
>  drivers/mfd/Kconfig                                |  11 ++
>  drivers/mfd/Makefile                               |   1 +
>  drivers/mfd/atmel-hlcdc.c                          | 116 
> +++++++++++++++++++++
>  include/linux/mfd/atmel-hlcdc.h                    |  78 ++++++++++++++
>  5 files changed, 247 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
>  create mode 100644 drivers/mfd/atmel-hlcdc.c
>  create mode 100644 include/linux/mfd/atmel-hlcdc.h
> 
> diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt 
> b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
> new file mode 100644
> index 0000000..f5b69cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
> @@ -0,0 +1,41 @@
> +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
> +
> +Required properties:
> + - compatible: value should be one of the following:
> +   "atmel,sama5d3-hlcdc"
> + - reg: base address and size of the HLCDC device registers.
> + - clock-names: the name of the 3 clocks requested by the HLCDC device.
> +   Should contain "periph_clk", "sys_clk" and "slow_clk".
> + - clocks: should contain the 3 clocks requested by the HLCDC device.
> +
> +The HLCDC IP exposes two subdevices:
> + - a PWM chip: see Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
> + - a Display Controller: see 
> Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
> +
> +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";
> +             status = "disabled";
> +
> +             hlcdc-display-controller {
> +                     compatible = "atmel,hlcdc-dc";
> +                     interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
> +                     pinctrl-names = "default", "rgb-444", "rgb-565", 
> "rgb-666", "rgb-888";
> +                     pinctrl-0 = <&pinctrl_lcd_base>;
> +                     pinctrl-1 = <&pinctrl_lcd_base &pinctrl_lcd_rgb444>;
> +                     pinctrl-2 = <&pinctrl_lcd_base &pinctrl_lcd_rgb565>;
> +                     pinctrl-3 = <&pinctrl_lcd_base &pinctrl_lcd_rgb666>;
> +                     pinctrl-4 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
> +             };
> +
> +             hlcdc_pwm: hlcdc-pwm {
> +                     compatible = "atmel,hlcdc-pwm";
> +                     pinctrl-names = "default";
> +                     pinctrl-0 = <&pinctrl_lcd_pwm>;
> +                     #pwm-cells = <3>;
> +             };
> +     };
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index ee8204c..82777f6 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -59,6 +59,17 @@ config MFD_AAT2870_CORE
>         additional drivers must be enabled in order to use the
>         functionality of the device.
>  
> +config MFD_ATMEL_HLCDC
> +     tristate "Atmel HLCDC (High LCD Controller)"
> +     select MFD_CORE
> +     select REGMAP_MMIO
> +     help
> +       Choose this option if you have an ATMEL SoC with an HLCDC (High
> +       LCD Controller) IP (i.e. at91sam9n12, at91sam9x5 family or sama5d3
> +       family).
> +       This MFD device exposes two subdevices: a PWM chip and a Display
> +       Controller.
> +
>  config MFD_BCM590XX
>       tristate "Broadcom BCM590xx PMUs"
>       select MFD_CORE
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 8afedba..5f25b0d 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -156,6 +156,7 @@ obj-$(CONFIG_MFD_PM8921_CORE)     += pm8921-core.o ssbi.o
>  obj-$(CONFIG_TPS65911_COMPARATOR)    += tps65911-comparator.o
>  obj-$(CONFIG_MFD_TPS65090)   += tps65090.o
>  obj-$(CONFIG_MFD_AAT2870_CORE)       += aat2870-core.o
> +obj-$(CONFIG_MFD_ATMEL_HLCDC)        += atmel-hlcdc.o
>  obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
>  obj-$(CONFIG_MFD_PALMAS)     += palmas.o
>  obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
> diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
> new file mode 100644
> index 0000000..e4636e8
> --- /dev/null
> +++ b/drivers/mfd/atmel-hlcdc.c
> @@ -0,0 +1,116 @@
> +/*
> + * Copyright (C) 2014 Free Electrons
> + * Copyright (C) 2014 Atmel
> + *
> + * Author: Boris BREZILLON <boris.brezillon at free-electrons.com>
> + *
> + * 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/mfd/atmel-hlcdc.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +static const struct mfd_cell atmel_hlcdc_cells[] = {
> +     {
> +             .name = "atmel-hlcdc-pwm",
> +             .of_compatible = "atmel,hlcdc-pwm",
> +     },
> +     {
> +             .name = "atmel-hlcdc-dc",
> +             .of_compatible = "atmel,hlcdc-dc",
> +     },
> +};
> +
> +static int atmel_hlcdc_probe(struct platform_device *pdev)
> +{
> +     struct device *dev = &pdev->dev;
> +     struct regmap_config config;
> +     struct atmel_hlcdc *hlcdc;
> +     struct resource *res;
> +     void __iomem *regs;
> +
> +     hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
> +     if (!hlcdc)
> +             return -ENOMEM;
> +
> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     regs = devm_ioremap_resource(dev, res);
> +     if (IS_ERR(regs))
> +             return PTR_ERR(regs);
> +
> +     hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
> +     if (IS_ERR(hlcdc->periph_clk)) {
> +             dev_err(dev, "failed to get functional clock\n");
> +             return PTR_ERR(hlcdc->periph_clk);
> +     }
> +
> +     hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
> +     if (IS_ERR(hlcdc->sys_clk)) {
> +             dev_err(dev, "failed to get functional clock\n");
> +             return PTR_ERR(hlcdc->sys_clk);
> +     }
> +
> +     hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
> +     if (IS_ERR(hlcdc->slow_clk)) {
> +             dev_err(dev, "failed to get slow clock\n");
> +             return PTR_ERR(hlcdc->slow_clk);
> +     }
> +
> +     memset(&config, 0, sizeof(config));
> +     config.reg_bits = 32;
> +     config.val_bits = 32;
> +     config.reg_stride = 4;
> +     config.max_register = (resource_size(res) / 4) - 1;
> +     hlcdc->regmap = devm_regmap_init_mmio_clk(dev, "periph_clk", regs,
> +                                               &config);
I don't think it's necessary to use "periph_clk" here. This clock will
always be running because the HLCDC needs it to work (it's not just an
interface clock). In the end it's just some extra work for each register
access.
> +     if (IS_ERR(hlcdc->regmap))
> +             return PTR_ERR(hlcdc->regmap);
> +
> +     dev_set_drvdata(dev, hlcdc);
> +
> +     return mfd_add_devices(dev, -1, atmel_hlcdc_cells,
> +                            ARRAY_SIZE(atmel_hlcdc_cells),
> +                            NULL, 0, NULL);
> +}
> +
> +static int atmel_hlcdc_remove(struct platform_device *pdev)
> +{
> +     mfd_remove_devices(&pdev->dev);
> +
> +     dev_set_drvdata(&pdev->dev, NULL);
> +
> +     return 0;
> +}
> +
> +static const struct of_device_id atmel_hlcdc_match[] = {
> +     { .compatible = "atmel,sama5d3-hlcdc" },
> +     { },
> +};
> +
> +static struct platform_driver atmel_hlcdc_driver = {
> +     .probe = atmel_hlcdc_probe,
> +     .remove = atmel_hlcdc_remove,
> +     .driver = {
> +             .name = "atmel-hlcdc",
> +             .owner = THIS_MODULE,
> +             .of_match_table = atmel_hlcdc_match,
> +     },
> +};
> +module_platform_driver(atmel_hlcdc_driver);
> +
> +MODULE_ALIAS("platform:atmel-hlcdc");
> +MODULE_AUTHOR("Boris Brezillon <boris.brezillon at free-electrons.com>");
> +MODULE_DESCRIPTION("Atmel HLCDC driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
> new file mode 100644
> index 0000000..d7a5589
> --- /dev/null
> +++ b/include/linux/mfd/atmel-hlcdc.h
> @@ -0,0 +1,78 @@
> +/*
> + * Copyright (C) 2014 Free Electrons
> + * Copyright (C) 2014 Atmel
> + *
> + * Author: Boris BREZILLON <boris.brezillon at free-electrons.com>
> + *
> + * 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/>.
> + */
> +
> +#ifndef __LINUX_MFD_HLCDC_H
> +#define __LINUX_MFD_HLCDC_H
> +
> +#include <linux/clk.h>
> +#include <linux/regmap.h>
> +
> +#define ATMEL_HLCDC_CFG(i)           ((i) * 0x4)
> +#define ATMEL_HLCDC_SIG_CFG          LCDCFG(5)
> +#define              ATMEL_HLCDC_HSPOL               BIT(0)
> +#define              ATMEL_HLCDC_VSPOL               BIT(1)
> +#define              ATMEL_HLCDC_VSPDLYS             BIT(2)
> +#define              ATMEL_HLCDC_VSPDLYE             BIT(3)
> +#define              ATMEL_HLCDC_DISPPOL             BIT(4)
> +#define              ATMEL_HLCDC_DITHER              BIT(6)
> +#define              ATMEL_HLCDC_DISPDLY             BIT(7)
> +#define              ATMEL_HLCDC_MODE_MASK           0x300
> +#define              ATMEL_HLCDC_PP                  BIT(10)
> +#define              ATMEL_HLCDC_VSPSU               BIT(12)
> +#define              ATMEL_HLCDC_VSPHO               BIT(13)
> +#define              ATMEL_HLCDC_GUARDTIME_MASK      0x1f0000
> +
> +#define ATMEL_HLCDC_EN                       0x20
> +#define ATMEL_HLCDC_DIS                      0x24
> +#define ATMEL_HLCDC_SR                       0x28
> +#define ATMEL_HLCDC_IER                      0x2c
> +#define ATMEL_HLCDC_IDR                      0x30
> +#define ATMEL_HLCDC_IMR                      0x34
> +#define ATMEL_HLCDC_ISR                      0x38
> +
> +#define ATMEL_HLCDC_CLKPOL           BIT(0)
> +#define ATMEL_HLCDC_CLKSEL           BIT(2)
> +#define ATMEL_HLCDC_CLKPWMSEL                BIT(3)
> +#define ATMEL_HLCDC_CGDIS(i)         BIT(8 + (i))
> +#define ATMEL_HLCDC_CLKDIV_SHFT              16
> +#define ATMEL_HLCDC_CLKDIV_MASK              (0xff << 
> ATMEL_HLCDC_CLKDIV_SHFT)
> +#define ATMEL_HLCDC_CLKDIV(div)              ((div - 2) << 
> ATMEL_HLCDC_CLKDIV_SHFT)
> +
> +#define ATMEL_HLCDC_PIXEL_CLK                BIT(0)
> +#define ATMEL_HLCDC_SYNC             BIT(1)
> +#define ATMEL_HLCDC_DISP             BIT(2)
> +#define ATMEL_HLCDC_PWM                      BIT(3)
> +#define ATMEL_HLCDC_SIP                      BIT(4)
> +
> +/**
> + * Structure shared by the MFD device and its subdevices.
> + *
> + * @regmap: register map used to access HLCDC IP registers
> + * @periph_clk: the hlcdc peripheral clock
> + * @sys_clk: the hlcdc system clock
> + * @slow_clk: the system slow clk
> + */
> +struct atmel_hlcdc {
> +     struct regmap *regmap;
> +     struct clk *periph_clk;
> +     struct clk *sys_clk;
> +     struct clk *slow_clk;
> +};
> +
> +#endif /* __LINUX_MFD_HLCDC_H */
> 

Reply via email to