linux/drivers/mfd/syscon.c
<<
>>
Prefs
   1/*
   2 * System Control Driver
   3 *
   4 * Copyright (C) 2012 Freescale Semiconductor, Inc.
   5 * Copyright (C) 2012 Linaro Ltd.
   6 *
   7 * Author: Dong Aisheng <dong.aisheng@linaro.org>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 */
  14
  15#include <linux/err.h>
  16#include <linux/io.h>
  17#include <linux/module.h>
  18#include <linux/of.h>
  19#include <linux/of_address.h>
  20#include <linux/of_platform.h>
  21#include <linux/platform_device.h>
  22#include <linux/regmap.h>
  23
  24static struct platform_driver syscon_driver;
  25
  26struct syscon {
  27        struct device *dev;
  28        void __iomem *base;
  29        struct regmap *regmap;
  30};
  31
  32static int syscon_match(struct device *dev, void *data)
  33{
  34        struct syscon *syscon = dev_get_drvdata(dev);
  35        struct device_node *dn = data;
  36
  37        return (syscon->dev->of_node == dn) ? 1 : 0;
  38}
  39
  40struct regmap *syscon_node_to_regmap(struct device_node *np)
  41{
  42        struct syscon *syscon;
  43        struct device *dev;
  44
  45        dev = driver_find_device(&syscon_driver.driver, NULL, np,
  46                                 syscon_match);
  47        if (!dev)
  48                return ERR_PTR(-EPROBE_DEFER);
  49
  50        syscon = dev_get_drvdata(dev);
  51
  52        return syscon->regmap;
  53}
  54EXPORT_SYMBOL_GPL(syscon_node_to_regmap);
  55
  56struct regmap *syscon_regmap_lookup_by_compatible(const char *s)
  57{
  58        struct device_node *syscon_np;
  59        struct regmap *regmap;
  60
  61        syscon_np = of_find_compatible_node(NULL, NULL, s);
  62        if (!syscon_np)
  63                return ERR_PTR(-ENODEV);
  64
  65        regmap = syscon_node_to_regmap(syscon_np);
  66        of_node_put(syscon_np);
  67
  68        return regmap;
  69}
  70EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible);
  71
  72struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np,
  73                                        const char *property)
  74{
  75        struct device_node *syscon_np;
  76        struct regmap *regmap;
  77
  78        syscon_np = of_parse_phandle(np, property, 0);
  79        if (!syscon_np)
  80                return ERR_PTR(-ENODEV);
  81
  82        regmap = syscon_node_to_regmap(syscon_np);
  83        of_node_put(syscon_np);
  84
  85        return regmap;
  86}
  87EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
  88
  89static const struct of_device_id of_syscon_match[] = {
  90        { .compatible = "syscon", },
  91        { },
  92};
  93
  94static struct regmap_config syscon_regmap_config = {
  95        .reg_bits = 32,
  96        .val_bits = 32,
  97        .reg_stride = 4,
  98};
  99
 100static int __devinit syscon_probe(struct platform_device *pdev)
 101{
 102        struct device *dev = &pdev->dev;
 103        struct device_node *np = dev->of_node;
 104        struct syscon *syscon;
 105        struct resource res;
 106        int ret;
 107
 108        if (!np)
 109                return -ENOENT;
 110
 111        syscon = devm_kzalloc(dev, sizeof(struct syscon),
 112                            GFP_KERNEL);
 113        if (!syscon)
 114                return -ENOMEM;
 115
 116        syscon->base = of_iomap(np, 0);
 117        if (!syscon->base)
 118                return -EADDRNOTAVAIL;
 119
 120        ret = of_address_to_resource(np, 0, &res);
 121        if (ret)
 122                return ret;
 123
 124        syscon_regmap_config.max_register = res.end - res.start - 3;
 125        syscon->regmap = devm_regmap_init_mmio(dev, syscon->base,
 126                                        &syscon_regmap_config);
 127        if (IS_ERR(syscon->regmap)) {
 128                dev_err(dev, "regmap init failed\n");
 129                return PTR_ERR(syscon->regmap);
 130        }
 131
 132        syscon->dev = dev;
 133        platform_set_drvdata(pdev, syscon);
 134
 135        dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n",
 136                res.start, res.end);
 137
 138        return 0;
 139}
 140
 141static int __devexit syscon_remove(struct platform_device *pdev)
 142{
 143        struct syscon *syscon;
 144
 145        syscon = platform_get_drvdata(pdev);
 146        iounmap(syscon->base);
 147        platform_set_drvdata(pdev, NULL);
 148
 149        return 0;
 150}
 151
 152static struct platform_driver syscon_driver = {
 153        .driver = {
 154                .name = "syscon",
 155                .owner = THIS_MODULE,
 156                .of_match_table = of_syscon_match,
 157        },
 158        .probe          = syscon_probe,
 159        .remove         = __devexit_p(syscon_remove),
 160};
 161
 162static int __init syscon_init(void)
 163{
 164        return platform_driver_register(&syscon_driver);
 165}
 166postcore_initcall(syscon_init);
 167
 168static void __exit syscon_exit(void)
 169{
 170        platform_driver_unregister(&syscon_driver);
 171}
 172module_exit(syscon_exit);
 173
 174MODULE_AUTHOR("Dong Aisheng <dong.aisheng@linaro.org>");
 175MODULE_DESCRIPTION("System Control driver");
 176MODULE_LICENSE("GPL v2");
 177
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.