Skip to content

Commit

Permalink
mfd: Add syscon driver based on regmap
Browse files Browse the repository at this point in the history
Add regmap based syscon driver.
This is usually used for access misc bits in registers which does not belong
to a specific module, for example, IMX IOMUXC GPR and ANATOP.
With this driver, client can use generic regmap API to access registers
which are registered into syscon.

Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
Signed-off-by: Dong Aisheng <dong.aisheng@linaro.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
  • Loading branch information
Dong Aisheng authored and Samuel Ortiz committed Sep 17, 2012
1 parent a435ae1 commit 87d6873
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 0 deletions.
20 changes: 20 additions & 0 deletions Documentation/devicetree/bindings/mfd/syscon.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
* System Controller Registers R/W driver

System controller node represents a register region containing a set
of miscellaneous registers. The registers are not cohesive enough to
represent as any specific type of device. The typical use-case is for
some other node's driver, or platform-specific code, to acquire a
reference to the syscon node (e.g. by phandle, node path, or search
using a specific compatible value), interrogate the node (or associated
OS driver) to determine the location of the registers, and access the
registers directly.

Required properties:
- compatible: Should contain "syscon".
- reg: the register region can be accessed from syscon

Examples:
gpr: iomuxc-gpr@020e0000 {
compatible = "fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x020e0000 0x38>;
};
8 changes: 8 additions & 0 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,14 @@ config MFD_ANATOP
MFD controller. This controller embeds regulator and
thermal devices for Freescale i.MX platforms.

config MFD_SYSCON
bool "System Controller Register R/W Based on Regmap"
depends on OF
select REGMAP_MMIO
help
Select this option to enable accessing system control registers
via regmap.

config MFD_PALMAS
bool "Support for the TI Palmas series chips"
select MFD_CORE
Expand Down
1 change: 1 addition & 0 deletions drivers/mfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,5 @@ obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_ANATOP) += anatop-mfd.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
176 changes: 176 additions & 0 deletions drivers/mfd/syscon.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* System Control Driver
*
* Copyright (C) 2012 Freescale Semiconductor, Inc.
* Copyright (C) 2012 Linaro Ltd.
*
* Author: Dong Aisheng <dong.aisheng@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/

#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>

static struct platform_driver syscon_driver;

struct syscon {
struct device *dev;
void __iomem *base;
struct regmap *regmap;
};

static int syscon_match(struct device *dev, void *data)
{
struct syscon *syscon = dev_get_drvdata(dev);
struct device_node *dn = data;

return (syscon->dev->of_node == dn) ? 1 : 0;
}

struct regmap *syscon_node_to_regmap(struct device_node *np)
{
struct syscon *syscon;
struct device *dev;

dev = driver_find_device(&syscon_driver.driver, NULL, np,
syscon_match);
if (!dev)
return ERR_PTR(-EPROBE_DEFER);

syscon = dev_get_drvdata(dev);

return syscon->regmap;
}
EXPORT_SYMBOL_GPL(syscon_node_to_regmap);

struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
{
struct device_node *syscon_np;
struct regmap *regmap;

syscon_np = of_find_compatible_node(NULL, NULL, s);
if (!syscon_np)
return ERR_PTR(-ENODEV);

regmap = syscon_node_to_regmap(syscon_np);
of_node_put(syscon_np);

return regmap;
}
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);

struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
const char *property)
{
struct device_node *syscon_np;
struct regmap *regmap;

syscon_np = of_parse_phandle(np, property, 0);
if (!syscon_np)
return ERR_PTR(-ENODEV);

regmap = syscon_node_to_regmap(syscon_np);
of_node_put(syscon_np);

return regmap;
}
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);

static const struct of_device_id of_syscon_match[] = {
{ .compatible = "syscon", },
{ },
};

static struct regmap_config syscon_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};

static int __devinit syscon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct syscon *syscon;
struct resource res;
int ret;

if (!np)
return -ENOENT;

syscon = devm_kzalloc(dev, sizeof(struct syscon),
GFP_KERNEL);
if (!syscon)
return -ENOMEM;

syscon->base = of_iomap(np, 0);
if (!syscon->base)
return -EADDRNOTAVAIL;

ret = of_address_to_resource(np, 0, &res);
if (ret)
return ret;

syscon_regmap_config.max_register = res.end - res.start - 3;
syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
&syscon_regmap_config);
if (IS_ERR(syscon->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(syscon->regmap);
}

syscon->dev = dev;
platform_set_drvdata(pdev, syscon);

dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n",
res.start, res.end);

return 0;
}

static int __devexit syscon_remove(struct platform_device *pdev)
{
struct syscon *syscon;

syscon = platform_get_drvdata(pdev);
iounmap(syscon->base);
platform_set_drvdata(pdev, NULL);

return 0;
}

static struct platform_driver syscon_driver = {
.driver = {
.name = "syscon",
.owner = THIS_MODULE,
.of_match_table = of_syscon_match,
},
.probe = syscon_probe,
.remove = __devexit_p(syscon_remove),
};

static int __init syscon_init(void)
{
return platform_driver_register(&syscon_driver);
}
postcore_initcall(syscon_init);

static void __exit syscon_exit(void)
{
platform_driver_unregister(&syscon_driver);
}
module_exit(syscon_exit);

MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>");
MODULE_DESCRIPTION("System Control driver");
MODULE_LICENSE("GPL v2");
23 changes: 23 additions & 0 deletions include/linux/mfd/syscon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* System Control Driver
*
* Copyright (C) 2012 Freescale Semiconductor, Inc.
* Copyright (C) 2012 Linaro Ltd.
*
* Author: Dong Aisheng <dong.aisheng@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/

#ifndef __LINUX_MFD_SYSCON_H__
#define __LINUX_MFD_SYSCON_H__

extern struct regmap *syscon_node_to_regmap(struct device_node *np);
extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s);
extern struct regmap *syscon_regmap_lookup_by_phandle(
struct device_node *np,
const char *property);
#endif /* __LINUX_MFD_SYSCON_H__ */

0 comments on commit 87d6873

Please sign in to comment.