1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25c41f11cSCyrille Pitchen /*
35c41f11cSCyrille Pitchen * Driver for Atmel Flexcom
45c41f11cSCyrille Pitchen *
55c41f11cSCyrille Pitchen * Copyright (C) 2015 Atmel Corporation
65c41f11cSCyrille Pitchen *
75c41f11cSCyrille Pitchen * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
85c41f11cSCyrille Pitchen */
95c41f11cSCyrille Pitchen
105c41f11cSCyrille Pitchen #include <linux/module.h>
115c41f11cSCyrille Pitchen #include <linux/types.h>
125c41f11cSCyrille Pitchen #include <linux/kernel.h>
135c41f11cSCyrille Pitchen #include <linux/platform_device.h>
145c41f11cSCyrille Pitchen #include <linux/of.h>
155c41f11cSCyrille Pitchen #include <linux/of_platform.h>
165c41f11cSCyrille Pitchen #include <linux/err.h>
175c41f11cSCyrille Pitchen #include <linux/io.h>
185c41f11cSCyrille Pitchen #include <linux/clk.h>
195c41f11cSCyrille Pitchen #include <dt-bindings/mfd/atmel-flexcom.h>
205c41f11cSCyrille Pitchen
215c41f11cSCyrille Pitchen /* I/O register offsets */
225c41f11cSCyrille Pitchen #define FLEX_MR 0x0 /* Mode Register */
235c41f11cSCyrille Pitchen #define FLEX_VERSION 0xfc /* Version Register */
245c41f11cSCyrille Pitchen
255c41f11cSCyrille Pitchen /* Mode Register bit fields */
265c41f11cSCyrille Pitchen #define FLEX_MR_OPMODE_OFFSET (0) /* Operating Mode */
275c41f11cSCyrille Pitchen #define FLEX_MR_OPMODE_MASK (0x3 << FLEX_MR_OPMODE_OFFSET)
285c41f11cSCyrille Pitchen #define FLEX_MR_OPMODE(opmode) (((opmode) << FLEX_MR_OPMODE_OFFSET) & \
295c41f11cSCyrille Pitchen FLEX_MR_OPMODE_MASK)
305c41f11cSCyrille Pitchen
317fdec110SRomain Izard struct atmel_flexcom {
327fdec110SRomain Izard void __iomem *base;
337fdec110SRomain Izard u32 opmode;
347fdec110SRomain Izard struct clk *clk;
357fdec110SRomain Izard };
365c41f11cSCyrille Pitchen
atmel_flexcom_probe(struct platform_device * pdev)375c41f11cSCyrille Pitchen static int atmel_flexcom_probe(struct platform_device *pdev)
385c41f11cSCyrille Pitchen {
395c41f11cSCyrille Pitchen struct device_node *np = pdev->dev.of_node;
407fdec110SRomain Izard struct atmel_flexcom *ddata;
415c41f11cSCyrille Pitchen int err;
425c41f11cSCyrille Pitchen
437fdec110SRomain Izard ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
447fdec110SRomain Izard if (!ddata)
457fdec110SRomain Izard return -ENOMEM;
467fdec110SRomain Izard
477fdec110SRomain Izard platform_set_drvdata(pdev, ddata);
487fdec110SRomain Izard
497fdec110SRomain Izard err = of_property_read_u32(np, "atmel,flexcom-mode", &ddata->opmode);
505c41f11cSCyrille Pitchen if (err)
515c41f11cSCyrille Pitchen return err;
525c41f11cSCyrille Pitchen
537fdec110SRomain Izard if (ddata->opmode < ATMEL_FLEXCOM_MODE_USART ||
547fdec110SRomain Izard ddata->opmode > ATMEL_FLEXCOM_MODE_TWI)
555c41f11cSCyrille Pitchen return -EINVAL;
565c41f11cSCyrille Pitchen
57*ccb92c4dSYe Xingchen ddata->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
587fdec110SRomain Izard if (IS_ERR(ddata->base))
597fdec110SRomain Izard return PTR_ERR(ddata->base);
605c41f11cSCyrille Pitchen
617fdec110SRomain Izard ddata->clk = devm_clk_get(&pdev->dev, NULL);
627fdec110SRomain Izard if (IS_ERR(ddata->clk))
637fdec110SRomain Izard return PTR_ERR(ddata->clk);
645c41f11cSCyrille Pitchen
657fdec110SRomain Izard err = clk_prepare_enable(ddata->clk);
665c41f11cSCyrille Pitchen if (err)
675c41f11cSCyrille Pitchen return err;
685c41f11cSCyrille Pitchen
695c41f11cSCyrille Pitchen /*
705c41f11cSCyrille Pitchen * Set the Operating Mode in the Mode Register: only the selected device
715c41f11cSCyrille Pitchen * is clocked. Hence, registers of the other serial devices remain
725c41f11cSCyrille Pitchen * inaccessible and are read as zero. Also the external I/O lines of the
735c41f11cSCyrille Pitchen * Flexcom are muxed to reach the selected device.
745c41f11cSCyrille Pitchen */
757fdec110SRomain Izard writel(FLEX_MR_OPMODE(ddata->opmode), ddata->base + FLEX_MR);
765c41f11cSCyrille Pitchen
777fdec110SRomain Izard clk_disable_unprepare(ddata->clk);
785c41f11cSCyrille Pitchen
79ad56b2a4SBenjamin Gaignard return devm_of_platform_populate(&pdev->dev);
805c41f11cSCyrille Pitchen }
815c41f11cSCyrille Pitchen
825c41f11cSCyrille Pitchen static const struct of_device_id atmel_flexcom_of_match[] = {
835c41f11cSCyrille Pitchen { .compatible = "atmel,sama5d2-flexcom" },
845c41f11cSCyrille Pitchen { /* sentinel */ }
855c41f11cSCyrille Pitchen };
865c41f11cSCyrille Pitchen MODULE_DEVICE_TABLE(of, atmel_flexcom_of_match);
875c41f11cSCyrille Pitchen
atmel_flexcom_resume_noirq(struct device * dev)885d051cf9SClaudiu Beznea static int __maybe_unused atmel_flexcom_resume_noirq(struct device *dev)
897fdec110SRomain Izard {
907fdec110SRomain Izard struct atmel_flexcom *ddata = dev_get_drvdata(dev);
917fdec110SRomain Izard int err;
927fdec110SRomain Izard u32 val;
937fdec110SRomain Izard
947fdec110SRomain Izard err = clk_prepare_enable(ddata->clk);
957fdec110SRomain Izard if (err)
967fdec110SRomain Izard return err;
977fdec110SRomain Izard
987fdec110SRomain Izard val = FLEX_MR_OPMODE(ddata->opmode),
997fdec110SRomain Izard writel(val, ddata->base + FLEX_MR);
1007fdec110SRomain Izard
1017fdec110SRomain Izard clk_disable_unprepare(ddata->clk);
1027fdec110SRomain Izard
1037fdec110SRomain Izard return 0;
1047fdec110SRomain Izard }
1057fdec110SRomain Izard
106748718bcSClaudiu Beznea static const struct dev_pm_ops __maybe_unused atmel_flexcom_pm_ops = {
1075d051cf9SClaudiu Beznea .resume_noirq = atmel_flexcom_resume_noirq,
1085d051cf9SClaudiu Beznea };
1097fdec110SRomain Izard
1105c41f11cSCyrille Pitchen static struct platform_driver atmel_flexcom_driver = {
1115c41f11cSCyrille Pitchen .probe = atmel_flexcom_probe,
1125c41f11cSCyrille Pitchen .driver = {
1135c41f11cSCyrille Pitchen .name = "atmel_flexcom",
1148c0fad75SClaudiu Beznea .pm = pm_ptr(&atmel_flexcom_pm_ops),
1155c41f11cSCyrille Pitchen .of_match_table = atmel_flexcom_of_match,
1165c41f11cSCyrille Pitchen },
1175c41f11cSCyrille Pitchen };
1185c41f11cSCyrille Pitchen
1195c41f11cSCyrille Pitchen module_platform_driver(atmel_flexcom_driver);
1205c41f11cSCyrille Pitchen
1215c41f11cSCyrille Pitchen MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
1225c41f11cSCyrille Pitchen MODULE_DESCRIPTION("Atmel Flexcom MFD driver");
1235c41f11cSCyrille Pitchen MODULE_LICENSE("GPL v2");
124