xref: /linux/drivers/bus/imx-weim.c (revision 8b8cb52af34da2faa293614b2554c8eac30faeaa)
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 
48d8dfa59fSKees Cook #define MAX_CS_REGS_COUNT	6
49*8b8cb52aSSven Van Asbroeck #define OF_REG_SIZE		3
50d8dfa59fSKees Cook 
5185bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = {
523f98b6baSAlexander Shiyan 	/* i.MX1/21 */
533f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
543f98b6baSAlexander Shiyan 	/* i.MX25/27/31/35 */
553f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, },
563f98b6baSAlexander Shiyan 	/* i.MX50/53/6Q */
573f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, },
583f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, },
593f98b6baSAlexander Shiyan 	/* i.MX51 */
603f98b6baSAlexander Shiyan 	{ .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, },
6185bf6d4eSHuang Shijie 	{ }
6285bf6d4eSHuang Shijie };
6385bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table);
6485bf6d4eSHuang Shijie 
658d9ee21eSShawn Guo static int __init imx_weim_gpr_setup(struct platform_device *pdev)
668d9ee21eSShawn Guo {
678d9ee21eSShawn Guo 	struct device_node *np = pdev->dev.of_node;
688d9ee21eSShawn Guo 	struct property *prop;
698d9ee21eSShawn Guo 	const __be32 *p;
708d9ee21eSShawn Guo 	struct regmap *gpr;
718d9ee21eSShawn Guo 	u32 gprvals[4] = {
728d9ee21eSShawn Guo 		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M)  */
738d9ee21eSShawn Guo 		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M)  */
748d9ee21eSShawn Guo 		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M)  */
758d9ee21eSShawn Guo 		01111,	/* CS0(32M)  CS1(32M) CS2(32M) CS3(32M) */
768d9ee21eSShawn Guo 	};
778d9ee21eSShawn Guo 	u32 gprval = 0;
788d9ee21eSShawn Guo 	u32 val;
798d9ee21eSShawn Guo 	int cs = 0;
808d9ee21eSShawn Guo 	int i = 0;
818d9ee21eSShawn Guo 
828d9ee21eSShawn Guo 	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
838d9ee21eSShawn Guo 	if (IS_ERR(gpr)) {
848d9ee21eSShawn Guo 		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
858d9ee21eSShawn Guo 		return 0;
868d9ee21eSShawn Guo 	}
878d9ee21eSShawn Guo 
888d9ee21eSShawn Guo 	of_property_for_each_u32(np, "ranges", prop, p, val) {
898d9ee21eSShawn Guo 		if (i % 4 == 0) {
908d9ee21eSShawn Guo 			cs = val;
918d9ee21eSShawn Guo 		} else if (i % 4 == 3 && val) {
928d9ee21eSShawn Guo 			val = (val / SZ_32M) | 1;
938d9ee21eSShawn Guo 			gprval |= val << cs * 3;
948d9ee21eSShawn Guo 		}
958d9ee21eSShawn Guo 		i++;
968d9ee21eSShawn Guo 	}
978d9ee21eSShawn Guo 
988d9ee21eSShawn Guo 	if (i == 0 || i % 4)
998d9ee21eSShawn Guo 		goto err;
1008d9ee21eSShawn Guo 
1018d9ee21eSShawn Guo 	for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
1028d9ee21eSShawn Guo 		if (gprval == gprvals[i]) {
1038d9ee21eSShawn Guo 			/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
1048d9ee21eSShawn Guo 			regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
1058d9ee21eSShawn Guo 			return 0;
1068d9ee21eSShawn Guo 		}
1078d9ee21eSShawn Guo 	}
1088d9ee21eSShawn Guo 
1098d9ee21eSShawn Guo err:
1108d9ee21eSShawn Guo 	dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
1118d9ee21eSShawn Guo 	return -EINVAL;
1128d9ee21eSShawn Guo }
1138d9ee21eSShawn Guo 
11485bf6d4eSHuang Shijie /* Parse and set the timing for this device. */
1153f98b6baSAlexander Shiyan static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
1163f98b6baSAlexander Shiyan 				    const struct imx_weim_devtype *devtype)
11785bf6d4eSHuang Shijie {
118d8dfa59fSKees Cook 	u32 cs_idx, value[MAX_CS_REGS_COUNT];
1193f98b6baSAlexander Shiyan 	int i, ret;
120*8b8cb52aSSven Van Asbroeck 	int reg_idx, num_regs;
12185bf6d4eSHuang Shijie 
122d8dfa59fSKees Cook 	if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT))
123d8dfa59fSKees Cook 		return -EINVAL;
124d8dfa59fSKees Cook 
12585bf6d4eSHuang Shijie 	ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
1263f98b6baSAlexander Shiyan 					 value, devtype->cs_regs_count);
12785bf6d4eSHuang Shijie 	if (ret)
12885bf6d4eSHuang Shijie 		return ret;
12985bf6d4eSHuang Shijie 
130*8b8cb52aSSven Van Asbroeck 	/*
131*8b8cb52aSSven Van Asbroeck 	 * the child node's "reg" property may contain multiple address ranges,
132*8b8cb52aSSven Van Asbroeck 	 * extract the chip select for each.
133*8b8cb52aSSven Van Asbroeck 	 */
134*8b8cb52aSSven Van Asbroeck 	num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE);
135*8b8cb52aSSven Van Asbroeck 	if (num_regs < 0)
136*8b8cb52aSSven Van Asbroeck 		return num_regs;
137*8b8cb52aSSven Van Asbroeck 	if (!num_regs)
138*8b8cb52aSSven Van Asbroeck 		return -EINVAL;
139*8b8cb52aSSven Van Asbroeck 	for (reg_idx = 0; reg_idx < num_regs; reg_idx++) {
140*8b8cb52aSSven Van Asbroeck 		/* get the CS index from this child node's "reg" property. */
141*8b8cb52aSSven Van Asbroeck 		ret = of_property_read_u32_index(np, "reg",
142*8b8cb52aSSven Van Asbroeck 					reg_idx * OF_REG_SIZE, &cs_idx);
143*8b8cb52aSSven Van Asbroeck 		if (ret)
144*8b8cb52aSSven Van Asbroeck 			break;
145*8b8cb52aSSven Van Asbroeck 
146*8b8cb52aSSven Van Asbroeck 		if (cs_idx >= devtype->cs_count)
147*8b8cb52aSSven Van Asbroeck 			return -EINVAL;
148*8b8cb52aSSven Van Asbroeck 
14985bf6d4eSHuang Shijie 		/* set the timing for WEIM */
1503f98b6baSAlexander Shiyan 		for (i = 0; i < devtype->cs_regs_count; i++)
151*8b8cb52aSSven Van Asbroeck 			writel(value[i],
152*8b8cb52aSSven Van Asbroeck 				base + cs_idx * devtype->cs_stride + i * 4);
153*8b8cb52aSSven Van Asbroeck 	}
1543f98b6baSAlexander Shiyan 
15585bf6d4eSHuang Shijie 	return 0;
15685bf6d4eSHuang Shijie }
15785bf6d4eSHuang Shijie 
15829e54970SAlexander Shiyan static int __init weim_parse_dt(struct platform_device *pdev,
15929e54970SAlexander Shiyan 				void __iomem *base)
16085bf6d4eSHuang Shijie {
1613f98b6baSAlexander Shiyan 	const struct of_device_id *of_id = of_match_device(weim_id_table,
1623f98b6baSAlexander Shiyan 							   &pdev->dev);
1633f98b6baSAlexander Shiyan 	const struct imx_weim_devtype *devtype = of_id->data;
16485bf6d4eSHuang Shijie 	struct device_node *child;
16552c47b63SAlison Chaiken 	int ret, have_child = 0;
16685bf6d4eSHuang Shijie 
1678d9ee21eSShawn Guo 	if (devtype == &imx50_weim_devtype) {
1688d9ee21eSShawn Guo 		ret = imx_weim_gpr_setup(pdev);
1698d9ee21eSShawn Guo 		if (ret)
1708d9ee21eSShawn Guo 			return ret;
1718d9ee21eSShawn Guo 	}
1728d9ee21eSShawn Guo 
17333b96d2cSFabio Estevam 	for_each_available_child_of_node(pdev->dev.of_node, child) {
1743f98b6baSAlexander Shiyan 		ret = weim_timing_setup(child, base, devtype);
17552c47b63SAlison Chaiken 		if (ret)
1769c0982d8SRob Herring 			dev_warn(&pdev->dev, "%pOF set timing failed.\n",
1779c0982d8SRob Herring 				child);
17852c47b63SAlison Chaiken 		else
17952c47b63SAlison Chaiken 			have_child = 1;
18085bf6d4eSHuang Shijie 	}
18185bf6d4eSHuang Shijie 
18252c47b63SAlison Chaiken 	if (have_child)
18339ec8d38SKefeng Wang 		ret = of_platform_default_populate(pdev->dev.of_node,
18426651c43SLiu Ying 						   NULL, &pdev->dev);
18585bf6d4eSHuang Shijie 	if (ret)
1869c0982d8SRob Herring 		dev_err(&pdev->dev, "%pOF fail to create devices.\n",
1879c0982d8SRob Herring 			pdev->dev.of_node);
18885bf6d4eSHuang Shijie 	return ret;
18985bf6d4eSHuang Shijie }
19085bf6d4eSHuang Shijie 
19129e54970SAlexander Shiyan static int __init weim_probe(struct platform_device *pdev)
19285bf6d4eSHuang Shijie {
19385bf6d4eSHuang Shijie 	struct resource *res;
19470ac98daSAlexander Shiyan 	struct clk *clk;
19570ac98daSAlexander Shiyan 	void __iomem *base;
196b2d1fb73SAlexander Shiyan 	int ret;
19785bf6d4eSHuang Shijie 
19885bf6d4eSHuang Shijie 	/* get the resource */
19985bf6d4eSHuang Shijie 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
20070ac98daSAlexander Shiyan 	base = devm_ioremap_resource(&pdev->dev, res);
201b2d1fb73SAlexander Shiyan 	if (IS_ERR(base))
202b2d1fb73SAlexander Shiyan 		return PTR_ERR(base);
20385bf6d4eSHuang Shijie 
20485bf6d4eSHuang Shijie 	/* get the clock */
20570ac98daSAlexander Shiyan 	clk = devm_clk_get(&pdev->dev, NULL);
20670ac98daSAlexander Shiyan 	if (IS_ERR(clk))
207b2d1fb73SAlexander Shiyan 		return PTR_ERR(clk);
20885bf6d4eSHuang Shijie 
20970ac98daSAlexander Shiyan 	ret = clk_prepare_enable(clk);
21085bf6d4eSHuang Shijie 	if (ret)
211b2d1fb73SAlexander Shiyan 		return ret;
21285bf6d4eSHuang Shijie 
21385bf6d4eSHuang Shijie 	/* parse the device node */
21470ac98daSAlexander Shiyan 	ret = weim_parse_dt(pdev, base);
215b2d1fb73SAlexander Shiyan 	if (ret)
21670ac98daSAlexander Shiyan 		clk_disable_unprepare(clk);
217b2d1fb73SAlexander Shiyan 	else
218b2d1fb73SAlexander Shiyan 		dev_info(&pdev->dev, "Driver registered.\n");
21985bf6d4eSHuang Shijie 
22085bf6d4eSHuang Shijie 	return ret;
22185bf6d4eSHuang Shijie }
22285bf6d4eSHuang Shijie 
22385bf6d4eSHuang Shijie static struct platform_driver weim_driver = {
22485bf6d4eSHuang Shijie 	.driver = {
22585bf6d4eSHuang Shijie 		.name		= "imx-weim",
22685bf6d4eSHuang Shijie 		.of_match_table	= weim_id_table,
22785bf6d4eSHuang Shijie 	},
22885bf6d4eSHuang Shijie };
22929e54970SAlexander Shiyan module_platform_driver_probe(weim_driver, weim_probe);
23085bf6d4eSHuang Shijie 
23185bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc.");
23285bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver");
23385bf6d4eSHuang Shijie MODULE_LICENSE("GPL");
234