linux/drivers/mfd/atmel-flexcom.c
<<
>>
Prefs
   1/*
   2 * Driver for Atmel Flexcom
   3 *
   4 * Copyright (C) 2015 Atmel Corporation
   5 *
   6 * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but WITHOUT
  13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15 * more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along with
  18 * this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/types.h>
  23#include <linux/kernel.h>
  24#include <linux/platform_device.h>
  25#include <linux/of.h>
  26#include <linux/of_platform.h>
  27#include <linux/err.h>
  28#include <linux/io.h>
  29#include <linux/clk.h>
  30#include <dt-bindings/mfd/atmel-flexcom.h>
  31
  32/* I/O register offsets */
  33#define FLEX_MR         0x0     /* Mode Register */
  34#define FLEX_VERSION    0xfc    /* Version Register */
  35
  36/* Mode Register bit fields */
  37#define FLEX_MR_OPMODE_OFFSET   (0)  /* Operating Mode */
  38#define FLEX_MR_OPMODE_MASK     (0x3 << FLEX_MR_OPMODE_OFFSET)
  39#define FLEX_MR_OPMODE(opmode)  (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
  40                                 FLEX_MR_OPMODE_MASK)
  41
  42
  43static int atmel_flexcom_probe(struct platform_device *pdev)
  44{
  45        struct device_node *np = pdev->dev.of_node;
  46        struct clk *clk;
  47        struct resource *res;
  48        void __iomem *base;
  49        u32 opmode;
  50        int err;
  51
  52        err = of_property_read_u32(np, "atmel,flexcom-mode", &opmode);
  53        if (err)
  54                return err;
  55
  56        if (opmode < ATMEL_FLEXCOM_MODE_USART ||
  57            opmode > ATMEL_FLEXCOM_MODE_TWI)
  58                return -EINVAL;
  59
  60        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  61        base = devm_ioremap_resource(&pdev->dev, res);
  62        if (IS_ERR(base))
  63                return PTR_ERR(base);
  64
  65        clk = devm_clk_get(&pdev->dev, NULL);
  66        if (IS_ERR(clk))
  67                return PTR_ERR(clk);
  68
  69        err = clk_prepare_enable(clk);
  70        if (err)
  71                return err;
  72
  73        /*
  74         * Set the Operating Mode in the Mode Register: only the selected device
  75         * is clocked. Hence, registers of the other serial devices remain
  76         * inaccessible and are read as zero. Also the external I/O lines of the
  77         * Flexcom are muxed to reach the selected device.
  78         */
  79        writel(FLEX_MR_OPMODE(opmode), base + FLEX_MR);
  80
  81        clk_disable_unprepare(clk);
  82
  83        return of_platform_populate(np, NULL, NULL, &pdev->dev);
  84}
  85
  86static const struct of_device_id atmel_flexcom_of_match[] = {
  87        { .compatible = "atmel,sama5d2-flexcom" },
  88        { /* sentinel */ }
  89};
  90MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
  91
  92static struct platform_driver atmel_flexcom_driver = {
  93        .probe  = atmel_flexcom_probe,
  94        .driver = {
  95                .name           = "atmel_flexcom",
  96                .of_match_table = atmel_flexcom_of_match,
  97        },
  98};
  99
 100module_platform_driver(atmel_flexcom_driver);
 101
 102MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
 103MODULE_DESCRIPTION("Atmel Flexcom MFD driver");
 104MODULE_LICENSE("GPL v2");
 105