linux/drivers/mfd/atmel-hlcdc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2014 Free Electrons
   4 * Copyright (C) 2014 Atmel
   5 *
   6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/iopoll.h>
  11#include <linux/mfd/atmel-hlcdc.h>
  12#include <linux/mfd/core.h>
  13#include <linux/module.h>
  14#include <linux/mod_devicetable.h>
  15#include <linux/platform_device.h>
  16#include <linux/regmap.h>
  17
  18#define ATMEL_HLCDC_REG_MAX             (0x4000 - 0x4)
  19
  20struct atmel_hlcdc_regmap {
  21        void __iomem *regs;
  22        struct device *dev;
  23};
  24
  25static const struct mfd_cell atmel_hlcdc_cells[] = {
  26        {
  27                .name = "atmel-hlcdc-pwm",
  28                .of_compatible = "atmel,hlcdc-pwm",
  29        },
  30        {
  31                .name = "atmel-hlcdc-dc",
  32                .of_compatible = "atmel,hlcdc-display-controller",
  33        },
  34};
  35
  36static int regmap_atmel_hlcdc_reg_write(void *context, unsigned int reg,
  37                                        unsigned int val)
  38{
  39        struct atmel_hlcdc_regmap *hregmap = context;
  40
  41        if (reg <= ATMEL_HLCDC_DIS) {
  42                u32 status;
  43                int ret;
  44
  45                ret = readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR,
  46                                                status,
  47                                                !(status & ATMEL_HLCDC_SIP),
  48                                                1, 100);
  49                if (ret) {
  50                        dev_err(hregmap->dev,
  51                                "Timeout! Clock domain synchronization is in progress!\n");
  52                        return ret;
  53                }
  54        }
  55
  56        writel(val, hregmap->regs + reg);
  57
  58        return 0;
  59}
  60
  61static int regmap_atmel_hlcdc_reg_read(void *context, unsigned int reg,
  62                                       unsigned int *val)
  63{
  64        struct atmel_hlcdc_regmap *hregmap = context;
  65
  66        *val = readl(hregmap->regs + reg);
  67
  68        return 0;
  69}
  70
  71static const struct regmap_config atmel_hlcdc_regmap_config = {
  72        .reg_bits = 32,
  73        .val_bits = 32,
  74        .reg_stride = 4,
  75        .max_register = ATMEL_HLCDC_REG_MAX,
  76        .reg_write = regmap_atmel_hlcdc_reg_write,
  77        .reg_read = regmap_atmel_hlcdc_reg_read,
  78        .fast_io = true,
  79};
  80
  81static int atmel_hlcdc_probe(struct platform_device *pdev)
  82{
  83        struct atmel_hlcdc_regmap *hregmap;
  84        struct device *dev = &pdev->dev;
  85        struct atmel_hlcdc *hlcdc;
  86        struct resource *res;
  87
  88        hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL);
  89        if (!hregmap)
  90                return -ENOMEM;
  91
  92        hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
  93        if (!hlcdc)
  94                return -ENOMEM;
  95
  96        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  97        hregmap->regs = devm_ioremap_resource(dev, res);
  98        if (IS_ERR(hregmap->regs))
  99                return PTR_ERR(hregmap->regs);
 100
 101        hregmap->dev = &pdev->dev;
 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        { .compatible = "microchip,sam9x60-hlcdc" },
 144        { /* sentinel */ },
 145};
 146MODULE_DEVICE_TABLE(of, atmel_hlcdc_match);
 147
 148static struct platform_driver atmel_hlcdc_driver = {
 149        .probe = atmel_hlcdc_probe,
 150        .driver = {
 151                .name = "atmel-hlcdc",
 152                .of_match_table = atmel_hlcdc_match,
 153        },
 154};
 155module_platform_driver(atmel_hlcdc_driver);
 156
 157MODULE_ALIAS("platform:atmel-hlcdc");
 158MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
 159MODULE_DESCRIPTION("Atmel HLCDC driver");
 160MODULE_LICENSE("GPL v2");
 161