xref: /linux/drivers/bus/imx-weim.c (revision d8dfa59f5a512a536b80a4a8f12fa993683f48df)
185bf6d4eSHuang Shijie /*
285bf6d4eSHuang Shijie  * EIM driver for Freescale's i.MX chips
385bf6d4eSHuang Shijie  *
485bf6d4eSHuang Shijie  * Copyright (C) 2013 Freescale Semiconductor, Inc.
585bf6d4eSHuang Shijie  *
685bf6d4eSHuang Shijie  * This file is licensed under the terms of the GNU General Public
785bf6d4eSHuang Shijie  * License version 2. This program is licensed "as is" without any
885bf6d4eSHuang Shijie  * warranty of any kind, whether express or implied.
985bf6d4eSHuang Shijie  */
1085bf6d4eSHuang Shijie #include <linux/module.h>
1185bf6d4eSHuang Shijie #include <linux/clk.h>
1285bf6d4eSHuang Shijie #include <linux/io.h>
1385bf6d4eSHuang Shijie #include <linux/of_device.h>
148d9ee21eSShawn Guo #include <linux/mfd/syscon.h>
158d9ee21eSShawn Guo #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
168d9ee21eSShawn Guo #include <linux/regmap.h>
1785bf6d4eSHuang Shijie 
183f98b6baSAlexander Shiyan struct imx_weim_devtype {
193f98b6baSAlexander Shiyan 	unsigned int	cs_count;
203f98b6baSAlexander Shiyan 	unsigned int	cs_regs_count;
213f98b6baSAlexander Shiyan 	unsigned int	cs_stride;
223f98b6baSAlexander Shiyan };
233f98b6baSAlexander Shiyan 
243f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx1_weim_devtype = {
253f98b6baSAlexander Shiyan 	.cs_count	= 6,
263f98b6baSAlexander Shiyan 	.cs_regs_count	= 2,
273f98b6baSAlexander Shiyan 	.cs_stride	= 0x08,
283f98b6baSAlexander Shiyan };
293f98b6baSAlexander Shiyan 
303f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx27_weim_devtype = {
313f98b6baSAlexander Shiyan 	.cs_count	= 6,
323f98b6baSAlexander Shiyan 	.cs_regs_count	= 3,
333f98b6baSAlexander Shiyan 	.cs_stride	= 0x10,
343f98b6baSAlexander Shiyan };
353f98b6baSAlexander Shiyan 
363f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx50_weim_devtype = {
373f98b6baSAlexander Shiyan 	.cs_count	= 4,
383f98b6baSAlexander Shiyan 	.cs_regs_count	= 6,
393f98b6baSAlexander Shiyan 	.cs_stride	= 0x18,
403f98b6baSAlexander Shiyan };
413f98b6baSAlexander Shiyan 
423f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx51_weim_devtype = {
433f98b6baSAlexander Shiyan 	.cs_count	= 6,
443f98b6baSAlexander Shiyan 	.cs_regs_count	= 6,
453f98b6baSAlexander Shiyan 	.cs_stride	= 0x18,
463f98b6baSAlexander Shiyan };
473f98b6baSAlexander Shiyan 
48*d8dfa59fSKees Cook #define MAX_CS_REGS_COUNT	6
49*d8dfa59fSKees Cook 
5085bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = {
513f98b6baSAlexander Shiyan 	/* i.MX1/21 */
523f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
533f98b6baSAlexander Shiyan 	/* i.MX25/27/31/35 */
543f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, },
553f98b6baSAlexander Shiyan 	/* i.MX50/53/6Q */
563f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, },
573f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, },
583f98b6baSAlexander Shiyan 	/* i.MX51 */
593f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, },
6085bf6d4eSHuang Shijie 	{ }
6185bf6d4eSHuang Shijie };
6285bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table);
6385bf6d4eSHuang Shijie 
648d9ee21eSShawn Guo static int __init imx_weim_gpr_setup(struct platform_device *pdev)
658d9ee21eSShawn Guo {
668d9ee21eSShawn Guo 	struct device_node *np = pdev->dev.of_node;
678d9ee21eSShawn Guo 	struct property *prop;
688d9ee21eSShawn Guo 	const __be32 *p;
698d9ee21eSShawn Guo 	struct regmap *gpr;
708d9ee21eSShawn Guo 	u32 gprvals[4] = {
718d9ee21eSShawn Guo 		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M)  */
728d9ee21eSShawn Guo 		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M)  */
738d9ee21eSShawn Guo 		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M)  */
748d9ee21eSShawn Guo 		01111,	/* CS0(32M)  CS1(32M) CS2(32M) CS3(32M) */
758d9ee21eSShawn Guo 	};
768d9ee21eSShawn Guo 	u32 gprval = 0;
778d9ee21eSShawn Guo 	u32 val;
788d9ee21eSShawn Guo 	int cs = 0;
798d9ee21eSShawn Guo 	int i = 0;
808d9ee21eSShawn Guo 
818d9ee21eSShawn Guo 	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
828d9ee21eSShawn Guo 	if (IS_ERR(gpr)) {
838d9ee21eSShawn Guo 		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
848d9ee21eSShawn Guo 		return 0;
858d9ee21eSShawn Guo 	}
868d9ee21eSShawn Guo 
878d9ee21eSShawn Guo 	of_property_for_each_u32(np, "ranges", prop, p, val) {
888d9ee21eSShawn Guo 		if (i % 4 == 0) {
898d9ee21eSShawn Guo 			cs = val;
908d9ee21eSShawn Guo 		} else if (i % 4 == 3 && val) {
918d9ee21eSShawn Guo 			val = (val / SZ_32M) | 1;
928d9ee21eSShawn Guo 			gprval |= val << cs * 3;
938d9ee21eSShawn Guo 		}
948d9ee21eSShawn Guo 		i++;
958d9ee21eSShawn Guo 	}
968d9ee21eSShawn Guo 
978d9ee21eSShawn Guo 	if (i == 0 || i % 4)
988d9ee21eSShawn Guo 		goto err;
998d9ee21eSShawn Guo 
1008d9ee21eSShawn Guo 	for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
1018d9ee21eSShawn Guo 		if (gprval == gprvals[i]) {
1028d9ee21eSShawn Guo 			/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
1038d9ee21eSShawn Guo 			regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
1048d9ee21eSShawn Guo 			return 0;
1058d9ee21eSShawn Guo 		}
1068d9ee21eSShawn Guo 	}
1078d9ee21eSShawn Guo 
1088d9ee21eSShawn Guo err:
1098d9ee21eSShawn Guo 	dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
1108d9ee21eSShawn Guo 	return -EINVAL;
1118d9ee21eSShawn Guo }
1128d9ee21eSShawn Guo 
11385bf6d4eSHuang Shijie /* Parse and set the timing for this device. */
1143f98b6baSAlexander Shiyan static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
1153f98b6baSAlexander Shiyan 				    const struct imx_weim_devtype *devtype)
11685bf6d4eSHuang Shijie {
117*d8dfa59fSKees Cook 	u32 cs_idx, value[MAX_CS_REGS_COUNT];
1183f98b6baSAlexander Shiyan 	int i, ret;
11985bf6d4eSHuang Shijie 
120*d8dfa59fSKees Cook 	if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT))
121*d8dfa59fSKees Cook 		return -EINVAL;
122*d8dfa59fSKees Cook 
12385bf6d4eSHuang Shijie 	/* get the CS index from this child node's "reg" property. */
12485bf6d4eSHuang Shijie 	ret = of_property_read_u32(np, "reg", &cs_idx);
12585bf6d4eSHuang Shijie 	if (ret)
12685bf6d4eSHuang Shijie 		return ret;
12785bf6d4eSHuang Shijie 
1283f98b6baSAlexander Shiyan 	if (cs_idx >= devtype->cs_count)
12985bf6d4eSHuang Shijie 		return -EINVAL;
13085bf6d4eSHuang Shijie 
13185bf6d4eSHuang Shijie 	ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
1323f98b6baSAlexander Shiyan 					 value, devtype->cs_regs_count);
13385bf6d4eSHuang Shijie 	if (ret)
13485bf6d4eSHuang Shijie 		return ret;
13585bf6d4eSHuang Shijie 
13685bf6d4eSHuang Shijie 	/* set the timing for WEIM */
1373f98b6baSAlexander Shiyan 	for (i = 0; i < devtype->cs_regs_count; i++)
1383f98b6baSAlexander Shiyan 		writel(value[i], base + cs_idx * devtype->cs_stride + i * 4);
1393f98b6baSAlexander Shiyan 
14085bf6d4eSHuang Shijie 	return 0;
14185bf6d4eSHuang Shijie }
14285bf6d4eSHuang Shijie 
14329e54970SAlexander Shiyan static int __init weim_parse_dt(struct platform_device *pdev,
14429e54970SAlexander Shiyan 				void __iomem *base)
14585bf6d4eSHuang Shijie {
1463f98b6baSAlexander Shiyan 	const struct of_device_id *of_id = of_match_device(weim_id_table,
1473f98b6baSAlexander Shiyan 							   &pdev->dev);
1483f98b6baSAlexander Shiyan 	const struct imx_weim_devtype *devtype = of_id->data;
14985bf6d4eSHuang Shijie 	struct device_node *child;
15052c47b63SAlison Chaiken 	int ret, have_child = 0;
15185bf6d4eSHuang Shijie 
1528d9ee21eSShawn Guo 	if (devtype == &imx50_weim_devtype) {
1538d9ee21eSShawn Guo 		ret = imx_weim_gpr_setup(pdev);
1548d9ee21eSShawn Guo 		if (ret)
1558d9ee21eSShawn Guo 			return ret;
1568d9ee21eSShawn Guo 	}
1578d9ee21eSShawn Guo 
15833b96d2cSFabio Estevam 	for_each_available_child_of_node(pdev->dev.of_node, child) {
15985bf6d4eSHuang Shijie 		if (!child->name)
16085bf6d4eSHuang Shijie 			continue;
16185bf6d4eSHuang Shijie 
1623f98b6baSAlexander Shiyan 		ret = weim_timing_setup(child, base, devtype);
16352c47b63SAlison Chaiken 		if (ret)
1649c0982d8SRob Herring 			dev_warn(&pdev->dev, "%pOF set timing failed.\n",
1659c0982d8SRob Herring 				child);
16652c47b63SAlison Chaiken 		else
16752c47b63SAlison Chaiken 			have_child = 1;
16885bf6d4eSHuang Shijie 	}
16985bf6d4eSHuang Shijie 
17052c47b63SAlison Chaiken 	if (have_child)
17139ec8d38SKefeng Wang 		ret = of_platform_default_populate(pdev->dev.of_node,
17226651c43SLiu Ying 						   NULL, &pdev->dev);
17385bf6d4eSHuang Shijie 	if (ret)
1749c0982d8SRob Herring 		dev_err(&pdev->dev, "%pOF fail to create devices.\n",
1759c0982d8SRob Herring 			pdev->dev.of_node);
17685bf6d4eSHuang Shijie 	return ret;
17785bf6d4eSHuang Shijie }
17885bf6d4eSHuang Shijie 
17929e54970SAlexander Shiyan static int __init weim_probe(struct platform_device *pdev)
18085bf6d4eSHuang Shijie {
18185bf6d4eSHuang Shijie 	struct resource *res;
18270ac98daSAlexander Shiyan 	struct clk *clk;
18370ac98daSAlexander Shiyan 	void __iomem *base;
184b2d1fb73SAlexander Shiyan 	int ret;
18585bf6d4eSHuang Shijie 
18685bf6d4eSHuang Shijie 	/* get the resource */
18785bf6d4eSHuang Shijie 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
18870ac98daSAlexander Shiyan 	base = devm_ioremap_resource(&pdev->dev, res);
189b2d1fb73SAlexander Shiyan 	if (IS_ERR(base))
190b2d1fb73SAlexander Shiyan 		return PTR_ERR(base);
19185bf6d4eSHuang Shijie 
19285bf6d4eSHuang Shijie 	/* get the clock */
19370ac98daSAlexander Shiyan 	clk = devm_clk_get(&pdev->dev, NULL);
19470ac98daSAlexander Shiyan 	if (IS_ERR(clk))
195b2d1fb73SAlexander Shiyan 		return PTR_ERR(clk);
19685bf6d4eSHuang Shijie 
19770ac98daSAlexander Shiyan 	ret = clk_prepare_enable(clk);
19885bf6d4eSHuang Shijie 	if (ret)
199b2d1fb73SAlexander Shiyan 		return ret;
20085bf6d4eSHuang Shijie 
20185bf6d4eSHuang Shijie 	/* parse the device node */
20270ac98daSAlexander Shiyan 	ret = weim_parse_dt(pdev, base);
203b2d1fb73SAlexander Shiyan 	if (ret)
20470ac98daSAlexander Shiyan 		clk_disable_unprepare(clk);
205b2d1fb73SAlexander Shiyan 	else
206b2d1fb73SAlexander Shiyan 		dev_info(&pdev->dev, "Driver registered.\n");
20785bf6d4eSHuang Shijie 
20885bf6d4eSHuang Shijie 	return ret;
20985bf6d4eSHuang Shijie }
21085bf6d4eSHuang Shijie 
21185bf6d4eSHuang Shijie static struct platform_driver weim_driver = {
21285bf6d4eSHuang Shijie 	.driver = {
21385bf6d4eSHuang Shijie 		.name		= "imx-weim",
21485bf6d4eSHuang Shijie 		.of_match_table	= weim_id_table,
21585bf6d4eSHuang Shijie 	},
21685bf6d4eSHuang Shijie };
21729e54970SAlexander Shiyan module_platform_driver_probe(weim_driver, weim_probe);
21885bf6d4eSHuang Shijie 
21985bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc.");
22085bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver");
22185bf6d4eSHuang Shijie MODULE_LICENSE("GPL");
222