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(hregmap->regs + ATMEL_HLCDC_SR, status,
  54                                   !(status & ATMEL_HLCDC_SIP), 1, 100);
  55        }
  56
  57        writel(val, hregmap->regs + reg);
  58
  59        return 0;
  60}
  61
  62static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg,
  63                                       unsigned int *val)
  64{
  65        struct atmel_hlcdc_regmap *hregmap = context;
  66
  67        *val = readl(hregmap->regs + reg);
  68
  69        return 0;
  70}
  71
  72static const struct regmap_config atmel_hlcdc_regmap_config = {
  73        .reg_bits = 32,
  74        .val_bits = 32,
  75        .reg_stride = 4,
  76        .max_register = ATMEL_HLCDC_REG_MAX,
  77        .reg_write = regmap_atmel_hlcdc_reg_write,
  78        .reg_read = regmap_atmel_hlcdc_reg_read,
  79        .fast_io = true,
  80};
  81
  82static int atmel_hlcdc_probe(struct platform_device *pdev)
  83{
  84        struct atmel_hlcdc_regmap *hregmap;
  85        struct device *dev = &pdev->dev;
  86        struct atmel_hlcdc *hlcdc;
  87        struct resource *res;
  88
  89        hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL);
  90        if (!hregmap)
  91                return -ENOMEM;
  92
  93        hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
  94        if (!hlcdc)
  95                return -ENOMEM;
  96
  97        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  98        hregmap->regs = devm_ioremap_resource(dev, res);
  99        if (IS_ERR(hregmap->regs))
 100                return PTR_ERR(hregmap->regs);
 101
 102        hlcdc->irq = platform_get_irq(pdev, 0);
 103        if (hlcdc->irq < 0)
 104                return hlcdc->irq;
 105
 106        hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
 107        if (IS_ERR(hlcdc->periph_clk)) {
 108                dev_err(dev, "failed to get peripheral clock\n");
 109                return PTR_ERR(hlcdc->periph_clk);
 110        }
 111
 112        hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
 113        if (IS_ERR(hlcdc->sys_clk)) {
 114                dev_err(dev, "failed to get system clock\n");
 115                return PTR_ERR(hlcdc->sys_clk);
 116        }
 117
 118        hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
 119        if (IS_ERR(hlcdc->slow_clk)) {
 120                dev_err(dev, "failed to get slow clock\n");
 121                return PTR_ERR(hlcdc->slow_clk);
 122        }
 123
 124        hlcdc->regmap = devm_regmap_init(dev, NULL, hregmap,
 125                                         &atmel_hlcdc_regmap_config);
 126        if (IS_ERR(hlcdc->regmap))
 127                return PTR_ERR(hlcdc->regmap);
 128
 129        dev_set_drvdata(dev, hlcdc);
 130
 131        return mfd_add_devices(dev, -1, atmel_hlcdc_cells,
 132                               ARRAY_SIZE(atmel_hlcdc_cells),
 133                               NULL, 0, NULL);
 134}
 135
 136static int atmel_hlcdc_remove(struct platform_device *pdev)
 137{
 138        mfd_remove_devices(&pdev->dev);
 139
 140        return 0;
 141}
 142
 143static const struct of_device_id atmel_hlcdc_match[] = {
 144        { .compatible = "atmel,at91sam9n12-hlcdc" },
 145        { .compatible = "atmel,at91sam9x5-hlcdc" },
 146        { .compatible = "atmel,sama5d2-hlcdc" },
 147        { .compatible = "atmel,sama5d3-hlcdc" },
 148        { .compatible = "atmel,sama5d4-hlcdc" },
 149        { /* sentinel */ },
 150};
 151MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
 152
 153static struct platform_driver atmel_hlcdc_driver = {
 154        .probe = atmel_hlcdc_probe,
 155        .remove = atmel_hlcdc_remove,
 156        .driver = {
 157                .name = "atmel-hlcdc",
 158                .of_match_table = atmel_hlcdc_match,
 159        },
 160};
 161module_platform_driver(atmel_hlcdc_driver);
 162
 163MODULE_ALIAS("platform:atmel-hlcdc");
 164MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
 165MODULE_DESCRIPTION("Atmel HLCDC driver");
 166MODULE_LICENSE("GPL v2");
 167