Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for Atmel Flexcom
0004  *
0005  * Copyright (C) 2015 Atmel Corporation
0006  *
0007  * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/types.h>
0012 #include <linux/kernel.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/of.h>
0015 #include <linux/of_platform.h>
0016 #include <linux/err.h>
0017 #include <linux/io.h>
0018 #include <linux/clk.h>
0019 #include <dt-bindings/mfd/atmel-flexcom.h>
0020 
0021 /* I/O register offsets */
0022 #define FLEX_MR     0x0 /* Mode Register */
0023 #define FLEX_VERSION    0xfc    /* Version Register */
0024 
0025 /* Mode Register bit fields */
0026 #define FLEX_MR_OPMODE_OFFSET   (0)  /* Operating Mode */
0027 #define FLEX_MR_OPMODE_MASK (0x3 << FLEX_MR_OPMODE_OFFSET)
0028 #define FLEX_MR_OPMODE(opmode)  (((opmode) << FLEX_MR_OPMODE_OFFSET) &  \
0029                  FLEX_MR_OPMODE_MASK)
0030 
0031 struct atmel_flexcom {
0032     void __iomem *base;
0033     u32 opmode;
0034     struct clk *clk;
0035 };
0036 
0037 static int atmel_flexcom_probe(struct platform_device *pdev)
0038 {
0039     struct device_node *np = pdev->dev.of_node;
0040     struct resource *res;
0041     struct atmel_flexcom *ddata;
0042     int err;
0043 
0044     ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
0045     if (!ddata)
0046         return -ENOMEM;
0047 
0048     platform_set_drvdata(pdev, ddata);
0049 
0050     err = of_property_read_u32(np, "atmel,flexcom-mode", &ddata->opmode);
0051     if (err)
0052         return err;
0053 
0054     if (ddata->opmode < ATMEL_FLEXCOM_MODE_USART ||
0055         ddata->opmode > ATMEL_FLEXCOM_MODE_TWI)
0056         return -EINVAL;
0057 
0058     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0059     ddata->base = devm_ioremap_resource(&pdev->dev, res);
0060     if (IS_ERR(ddata->base))
0061         return PTR_ERR(ddata->base);
0062 
0063     ddata->clk = devm_clk_get(&pdev->dev, NULL);
0064     if (IS_ERR(ddata->clk))
0065         return PTR_ERR(ddata->clk);
0066 
0067     err = clk_prepare_enable(ddata->clk);
0068     if (err)
0069         return err;
0070 
0071     /*
0072      * Set the Operating Mode in the Mode Register: only the selected device
0073      * is clocked. Hence, registers of the other serial devices remain
0074      * inaccessible and are read as zero. Also the external I/O lines of the
0075      * Flexcom are muxed to reach the selected device.
0076      */
0077     writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
0078 
0079     clk_disable_unprepare(ddata->clk);
0080 
0081     return devm_of_platform_populate(&pdev->dev);
0082 }
0083 
0084 static const struct of_device_id atmel_flexcom_of_match[] = {
0085     { .compatible = "atmel,sama5d2-flexcom" },
0086     { /* sentinel */ }
0087 };
0088 MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
0089 
0090 static int __maybe_unused atmel_flexcom_resume_noirq(struct device *dev)
0091 {
0092     struct atmel_flexcom *ddata = dev_get_drvdata(dev);
0093     int err;
0094     u32 val;
0095 
0096     err = clk_prepare_enable(ddata->clk);
0097     if (err)
0098         return err;
0099 
0100     val = FLEX_MR_OPMODE(ddata->opmode),
0101     writel(val, ddata->base + FLEX_MR);
0102 
0103     clk_disable_unprepare(ddata->clk);
0104 
0105     return 0;
0106 }
0107 
0108 static const struct dev_pm_ops __maybe_unused atmel_flexcom_pm_ops = {
0109     .resume_noirq = atmel_flexcom_resume_noirq,
0110 };
0111 
0112 static struct platform_driver atmel_flexcom_driver = {
0113     .probe  = atmel_flexcom_probe,
0114     .driver = {
0115         .name       = "atmel_flexcom",
0116         .pm     = pm_ptr(&atmel_flexcom_pm_ops),
0117         .of_match_table = atmel_flexcom_of_match,
0118     },
0119 };
0120 
0121 module_platform_driver(atmel_flexcom_driver);
0122 
0123 MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
0124 MODULE_DESCRIPTION("Atmel Flexcom MFD driver");
0125 MODULE_LICENSE("GPL v2");