| /* | 
 |  * i.MX IIM driver | 
 |  * | 
 |  * Copyright (c) 2017 Pengutronix, Michael Grzeschik <m.grzeschik@pengutronix.de> | 
 |  * | 
 |  * Based on the barebox iim driver, | 
 |  * Copyright (c) 2010 Baruch Siach <baruch@tkos.co.il>, | 
 |  *	Orex Computed Radiography | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License version 2 | 
 |  * as published by the Free Software Foundation. | 
 |  * | 
 |  * http://www.opensource.org/licenses/gpl-license.html | 
 |  * http://www.gnu.org/copyleft/gpl.html | 
 |  */ | 
 |  | 
 | #include <linux/device.h> | 
 | #include <linux/io.h> | 
 | #include <linux/module.h> | 
 | #include <linux/nvmem-provider.h> | 
 | #include <linux/of.h> | 
 | #include <linux/of_device.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/clk.h> | 
 |  | 
 | #define IIM_BANK_BASE(n)	(0x800 + 0x400 * (n)) | 
 |  | 
 | struct imx_iim_drvdata { | 
 | 	unsigned int nregs; | 
 | }; | 
 |  | 
 | struct iim_priv { | 
 | 	void __iomem *base; | 
 | 	struct clk *clk; | 
 | }; | 
 |  | 
 | static int imx_iim_read(void *context, unsigned int offset, | 
 | 			  void *buf, size_t bytes) | 
 | { | 
 | 	struct iim_priv *iim = context; | 
 | 	int i, ret; | 
 | 	u8 *buf8 = buf; | 
 |  | 
 | 	ret = clk_prepare_enable(iim->clk); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	for (i = offset; i < offset + bytes; i++) { | 
 | 		int bank = i >> 5; | 
 | 		int reg = i & 0x1f; | 
 |  | 
 | 		*buf8++ = readl(iim->base + IIM_BANK_BASE(bank) + reg * 4); | 
 | 	} | 
 |  | 
 | 	clk_disable_unprepare(iim->clk); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct imx_iim_drvdata imx27_drvdata = { | 
 | 	.nregs = 2 * 32, | 
 | }; | 
 |  | 
 | static struct imx_iim_drvdata imx25_imx31_imx35_drvdata = { | 
 | 	.nregs = 3 * 32, | 
 | }; | 
 |  | 
 | static struct imx_iim_drvdata imx51_drvdata = { | 
 | 	.nregs = 4 * 32, | 
 | }; | 
 |  | 
 | static struct imx_iim_drvdata imx53_drvdata = { | 
 | 	.nregs = 4 * 32 + 16, | 
 | }; | 
 |  | 
 | static const struct of_device_id imx_iim_dt_ids[] = { | 
 | 	{ | 
 | 		.compatible = "fsl,imx25-iim", | 
 | 		.data = &imx25_imx31_imx35_drvdata, | 
 | 	}, { | 
 | 		.compatible = "fsl,imx27-iim", | 
 | 		.data = &imx27_drvdata, | 
 | 	}, { | 
 | 		.compatible = "fsl,imx31-iim", | 
 | 		.data = &imx25_imx31_imx35_drvdata, | 
 | 	}, { | 
 | 		.compatible = "fsl,imx35-iim", | 
 | 		.data = &imx25_imx31_imx35_drvdata, | 
 | 	}, { | 
 | 		.compatible = "fsl,imx51-iim", | 
 | 		.data = &imx51_drvdata, | 
 | 	}, { | 
 | 		.compatible = "fsl,imx53-iim", | 
 | 		.data = &imx53_drvdata, | 
 | 	}, { | 
 | 		/* sentinel */ | 
 | 	}, | 
 | }; | 
 | MODULE_DEVICE_TABLE(of, imx_iim_dt_ids); | 
 |  | 
 | static int imx_iim_probe(struct platform_device *pdev) | 
 | { | 
 | 	const struct of_device_id *of_id; | 
 | 	struct device *dev = &pdev->dev; | 
 | 	struct resource *res; | 
 | 	struct iim_priv *iim; | 
 | 	struct nvmem_device *nvmem; | 
 | 	struct nvmem_config cfg = {}; | 
 | 	const struct imx_iim_drvdata *drvdata = NULL; | 
 |  | 
 | 	iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL); | 
 | 	if (!iim) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
 | 	iim->base = devm_ioremap_resource(dev, res); | 
 | 	if (IS_ERR(iim->base)) | 
 | 		return PTR_ERR(iim->base); | 
 |  | 
 | 	of_id = of_match_device(imx_iim_dt_ids, dev); | 
 | 	if (!of_id) | 
 | 		return -ENODEV; | 
 |  | 
 | 	drvdata = of_id->data; | 
 |  | 
 | 	iim->clk = devm_clk_get(dev, NULL); | 
 | 	if (IS_ERR(iim->clk)) | 
 | 		return PTR_ERR(iim->clk); | 
 |  | 
 | 	cfg.name = "imx-iim", | 
 | 	cfg.read_only = true, | 
 | 	cfg.word_size = 1, | 
 | 	cfg.stride = 1, | 
 | 	cfg.reg_read = imx_iim_read, | 
 | 	cfg.dev = dev; | 
 | 	cfg.size = drvdata->nregs; | 
 | 	cfg.priv = iim; | 
 |  | 
 | 	nvmem = devm_nvmem_register(dev, &cfg); | 
 |  | 
 | 	return PTR_ERR_OR_ZERO(nvmem); | 
 | } | 
 |  | 
 | static struct platform_driver imx_iim_driver = { | 
 | 	.probe	= imx_iim_probe, | 
 | 	.driver = { | 
 | 		.name	= "imx-iim", | 
 | 		.of_match_table = imx_iim_dt_ids, | 
 | 	}, | 
 | }; | 
 | module_platform_driver(imx_iim_driver); | 
 |  | 
 | MODULE_AUTHOR("Michael Grzeschik <m.grzeschik@pengutronix.de>"); | 
 | MODULE_DESCRIPTION("i.MX IIM driver"); | 
 | MODULE_LICENSE("GPL v2"); |