0001
0002
0003
0004
0005
0006
0007
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
0022 #define FLEX_MR 0x0
0023 #define FLEX_VERSION 0xfc
0024
0025
0026 #define FLEX_MR_OPMODE_OFFSET (0)
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
0073
0074
0075
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 { }
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");