linux/drivers/mfd/atmel-hlcdc.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2014 Free Electrons
   3 * Copyright (C) 2014 Atmel
   4 *
   5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/clk.h>
  21#include <linux/iopoll.h>
  22#include <linux/mfd/atmel-hlcdc.h>
  23#include <linux/mfd/core.h>
  24#include <linux/module.h>
  25#include <linux/platform_device.h>
  26#include <linux/regmap.h>
  27
  28#define ATMEL_HLCDC_REG_MAX             (0x4000 - 0x4)
  29
  30struct atmel_hlcdc_regmap {
  31        void __iomem *regs;
  32};
  33
  34static const struct mfd_cell atmel_hlcdc_cells[] = {
  35        {
  36                .name = "atmel-hlcdc-pwm",
  37                .of_compatible = "atmel,hlcdc-pwm",
  38        },
  39        {
  40                .name = "atmel-hlcdc-dc",
  41                .of_compatible = "atmel,hlcdc-display-controller",
  42        },
  43};
  44
  45static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg,
  46                                        unsigned int val)
  47{
  48        struct atmel_hlcdc_regmap *hregmap = context;
  49
  50        if (reg <= ATMEL_HLCDC_DIS) {
  51                u32 status;
  52
  53                readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
  54                                          status, !(status & ATMEL_HLCDC_SIP),
  55                                          1, 100);
  56        }
  57
  58        writel(val, hregmap->regs + reg);
  59
  60        return 0;
  61}
  62
  63static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg,
  64                                       unsigned int *val)
  65{
  66        struct atmel_hlcdc_regmap *hregmap = context;
  67
  68        *val = readl(hregmap->regs + reg);
  69
  70        return 0;
  71}
  72
  73static const struct regmap_config atmel_hlcdc_regmap_config = {
  74        .reg_bits = 32,
  75        .val_bits = 32,
  76        .reg_stride = 4,
  77        .max_register = ATMEL_HLCDC_REG_MAX,
  78        .reg_write = regmap_atmel_hlcdc_reg_write,
  79        .reg_read = regmap_atmel_hlcdc_reg_read,
  80        .fast_io = true,
  81};
  82
  83static int atmel_hlcdc_probe(struct platform_device *pdev)
  84{
  85        struct atmel_hlcdc_regmap *hregmap;
  86        struct device *dev = &pdev->dev;
  87        struct atmel_hlcdc *hlcdc;
  88        struct resource *res;
  89
  90        hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL);
  91        if (!hregmap)
  92                return -ENOMEM;
  93
  94        hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
  95        if (!hlcdc)
  96                return -ENOMEM;
  97
  98        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  99        hregmap->regs = devm_ioremap_resource(dev, res);
 100        if (IS_ERR(hregmap->regs))
 101                return PTR_ERR(hregmap->regs);
 102
 103        hlcdc->irq = platform_get_irq(pdev, 0);
 104        if (hlcdc->irq < 0)
 105                return hlcdc->irq;
 106
 107        hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
 108        if (IS_ERR(hlcdc->periph_clk)) {
 109                dev_err(dev, "failed to get peripheral clock\n");
 110                return PTR_ERR(hlcdc->periph_clk);
 111        }
 112
 113        hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
 114        if (IS_ERR(hlcdc->sys_clk)) {
 115                dev_err(dev, "failed to get system clock\n");
 116                return PTR_ERR(hlcdc->sys_clk);
 117        }
 118
 119        hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
 120        if (IS_ERR(hlcdc->slow_clk)) {
 121                dev_err(dev, "failed to get slow clock\n");
 122                return PTR_ERR(hlcdc->slow_clk);
 123        }
 124
 125        hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap,
 126                                         &atmel_hlcdc_regmap_config);
 127        if (IS_ERR(hlcdc->regmap))
 128                return PTR_ERR(hlcdc->regmap);
 129
 130        dev_set_drvdata(dev, hlcdc);
 131
 132        return devm_mfd_add_devices(dev, -1, atmel_hlcdc_cells,
 133                                    ARRAY_SIZE(atmel_hlcdc_cells),
 134                                    NULL, 0, NULL);
 135}
 136
 137static const struct of_device_id atmel_hlcdc_match[] = {
 138        { .compatible = "atmel,at91sam9n12-hlcdc" },
 139        { .compatible = "atmel,at91sam9x5-hlcdc" },
 140        { .compatible = "atmel,sama5d2-hlcdc" },
 141        { .compatible = "atmel,sama5d3-hlcdc" },
 142        { .compatible = "atmel,sama5d4-hlcdc" },
 143        { /* sentinel */ },
 144};
 145MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
 146
 147static struct platform_driver atmel_hlcdc_driver = {
 148        .probe = atmel_hlcdc_probe,
 149        .driver = {
 150                .name = "atmel-hlcdc",
 151                .of_match_table = atmel_hlcdc_match,
 152        },
 153};
 154module_platform_driver(atmel_hlcdc_driver);
 155
 156MODULE_ALIAS("platform:atmel-hlcdc");
 157MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
 158MODULE_DESCRIPTION("Atmel HLCDC driver");
 159MODULE_LICENSE("GPL v2");
 160