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

The MFD device provides a regmap and several clocks (those connected
to this hardware block) to its subdevices.

This way concurrent accesses to the iomem range are handled by the regmap
framework, and each subdevice can safely access HLCDC registers.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Tested-by: Anthony Harivel <anthony.harivel@emtrion.de>
Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
This commit is contained in:
Boris Brezillon 2014-10-06 15:48:43 +02:00 коммит произвёл Lee Jones
Родитель 6e3f62f079
Коммит 2c86e9fb72
4 изменённых файлов: 214 добавлений и 0 удалений

Просмотреть файл

@ -59,6 +59,12 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
config MFD_ATMEL_HLCDC
tristate
select MFD_CORE
select REGMAP_MMIO
depends on OF
config MFD_BCM590XX
tristate "Broadcom BCM590xx PMUs"
select MFD_CORE

Просмотреть файл

@ -157,6 +157,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.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

122
drivers/mfd/atmel-hlcdc.c Normal file
Просмотреть файл

@ -0,0 +1,122 @@
/*
* Copyright (C) 2014 Free Electrons
* Copyright (C) 2014 Atmel
*
* Author: Boris BREZILLON <boris.brezillon@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/clk.h>
#include <linux/mfd/atmel-hlcdc.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4)
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-display-controller",
},
};
static const struct regmap_config atmel_hlcdc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = ATMEL_HLCDC_REG_MAX,
};
static int atmel_hlcdc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
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->irq = platform_get_irq(pdev, 0);
if (hlcdc->irq < 0)
return hlcdc->irq;
hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
if (IS_ERR(hlcdc->periph_clk)) {
dev_err(dev, "failed to get peripheral 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 system 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);
}
hlcdc->regmap = devm_regmap_init_mmio(dev, regs,
&atmel_hlcdc_regmap_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);
return 0;
}
static const struct of_device_id atmel_hlcdc_match[] = {
{ .compatible = "atmel,sama5d3-hlcdc" },
{ /* sentinel */ },
};
static struct platform_driver atmel_hlcdc_driver = {
.probe = atmel_hlcdc_probe,
.remove = atmel_hlcdc_remove,
.driver = {
.name = "atmel-hlcdc",
.of_match_table = atmel_hlcdc_match,
},
};
module_platform_driver(atmel_hlcdc_driver);
MODULE_ALIAS("platform:atmel-hlcdc");
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
MODULE_DESCRIPTION("Atmel HLCDC driver");
MODULE_LICENSE("GPL v2");

Просмотреть файл

@ -0,0 +1,85 @@
/*
* Copyright (C) 2014 Free Electrons
* Copyright (C) 2014 Atmel
*
* Author: Boris BREZILLON <boris.brezillon@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 GENMASK(9, 8)
#define ATMEL_HLCDC_PP BIT(10)
#define ATMEL_HLCDC_VSPSU BIT(12)
#define ATMEL_HLCDC_VSPHO BIT(13)
#define ATMEL_HLCDC_GUARDTIME_MASK GENMASK(20, 16)
#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 GENMASK(23, 16)
#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)
#define ATMEL_HLCDC_SOF BIT(0)
#define ATMEL_HLCDC_SYNCDIS BIT(1)
#define ATMEL_HLCDC_FIFOERR BIT(4)
#define ATMEL_HLCDC_LAYER_STATUS(x) BIT((x) + 8)
/**
* 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
* @irq: the hlcdc irq
*/
struct atmel_hlcdc {
struct regmap *regmap;
struct clk *periph_clk;
struct clk *sys_clk;
struct clk *slow_clk;
int irq;
};
#endif /* __LINUX_MFD_HLCDC_H */