linux/drivers/mfd/atmel-flexcom.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Driver for Atmel Flexcom
   4 *
   5 * Copyright (C) 2015 Atmel Corporation
   6 *
   7 * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
   8 */
   9
  10#include <linux/module.h>
  11#include <linux/types.h>
  12#include <linux/kernel.h>
  13#include <linux/platform_device.h>
  14#include <linux/of.h>
  15#include <linux/of_platform.h>
  16#include <linux/err.h>
  17#include <linux/io.h>
  18#include <linux/clk.h>
  19#include <dt-bindings/mfd/atmel-flexcom.h>
  20
  21/* I/O register offsets */
  22#define FLEX_MR         0x0     /* Mode Register */
  23#define FLEX_VERSION    0xfc    /* Version Register */
  24
  25/* Mode Register bit fields */
  26#define FLEX_MR_OPMODE_OFFSET   (0)  /* Operating Mode */
  27#define FLEX_MR_OPMODE_MASK     (0x3 << FLEX_MR_OPMODE_OFFSET)
  28#define FLEX_MR_OPMODE(opmode)  (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
  29                                 FLEX_MR_OPMODE_MASK)
  30
  31struct atmel_flexcom {
  32        void __iomem *base;
  33        u32 opmode;
  34        struct clk *clk;
  35};
  36
  37static int atmel_flexcom_probe(struct platform_device *pdev)
  38{
  39        struct device_node *np = pdev->dev.of_node;
  40        struct resource *res;
  41        struct atmel_flexcom *ddata;
  42        int err;
  43
  44        ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
  45        if (!ddata)
  46                return -ENOMEM;
  47
  48        platform_set_drvdata(pdev, ddata);
  49
  50        err = of_property_read_u32(np, "atmel,flexcom-mode", &ddata->opmode);
  51        if (err)
  52                return err;
  53
  54        if (ddata->opmode < ATMEL_FLEXCOM_MODE_USART ||
  55            ddata->opmode > ATMEL_FLEXCOM_MODE_TWI)
  56                return -EINVAL;
  57
  58        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  59        ddata->base = devm_ioremap_resource(&pdev->dev, res);
  60        if (IS_ERR(ddata->base))
  61                return PTR_ERR(ddata->base);
  62
  63        ddata->clk = devm_clk_get(&pdev->dev, NULL);
  64        if (IS_ERR(ddata->clk))
  65                return PTR_ERR(ddata->clk);
  66
  67        err = clk_prepare_enable(ddata->clk);
  68        if (err)
  69                return err;
  70
  71        /*
  72         * Set the Operating Mode in the Mode Register: only the selected device
  73         * is clocked. Hence, registers of the other serial devices remain
  74         * inaccessible and are read as zero. Also the external I/O lines of the
  75         * Flexcom are muxed to reach the selected device.
  76         */
  77        writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
  78
  79        clk_disable_unprepare(ddata->clk);
  80
  81        return devm_of_platform_populate(&pdev->dev);
  82}
  83
  84static const struct of_device_id atmel_flexcom_of_match[] = {
  85        { .compatible = "atmel,sama5d2-flexcom" },
  86        { /* sentinel */ }
  87};
  88MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
  89
  90#ifdef CONFIG_PM_SLEEP
  91static int atmel_flexcom_resume(struct device *dev)
  92{
  93        struct atmel_flexcom *ddata = dev_get_drvdata(dev);
  94        int err;
  95        u32 val;
  96
  97        err = clk_prepare_enable(ddata->clk);
  98        if (err)
  99                return err;
 100
 101        val = FLEX_MR_OPMODE(ddata->opmode),
 102        writel(val, ddata->base + FLEX_MR);
 103
 104        clk_disable_unprepare(ddata->clk);
 105
 106        return 0;
 107}
 108#endif
 109
 110static SIMPLE_DEV_PM_OPS(atmel_flexcom_pm_ops, NULL,
 111                         atmel_flexcom_resume);
 112
 113static struct platform_driver atmel_flexcom_driver = {
 114        .probe  = atmel_flexcom_probe,
 115        .driver = {
 116                .name           = "atmel_flexcom",
 117                .pm             = &atmel_flexcom_pm_ops,
 118                .of_match_table = atmel_flexcom_of_match,
 119        },
 120};
 121
 122module_platform_driver(atmel_flexcom_driver);
 123
 124MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
 125MODULE_DESCRIPTION("Atmel Flexcom MFD driver");
 126MODULE_LICENSE("GPL v2");
 127