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.brezil...@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@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.brezil...@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);
+       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.brezil...@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.brezil...@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 */
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to