xref: /openbmc/linux/drivers/mfd/atmel-flexcom.c (revision ccb92c4d)
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