xref: /linux/drivers/bus/imx-weim.c (revision 29e54970c2681ad621d13df739d0cddf052acf9d)
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>
1485bf6d4eSHuang Shijie 
1585bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = {
1685bf6d4eSHuang Shijie 	{ .compatible = "fsl,imx6q-weim", },
1785bf6d4eSHuang Shijie 	{}
1885bf6d4eSHuang Shijie };
1985bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table);
2085bf6d4eSHuang Shijie 
2185bf6d4eSHuang Shijie #define CS_TIMING_LEN 6
2285bf6d4eSHuang Shijie #define CS_REG_RANGE  0x18
2385bf6d4eSHuang Shijie 
2485bf6d4eSHuang Shijie /* Parse and set the timing for this device. */
25*29e54970SAlexander Shiyan static int __init weim_timing_setup(struct device_node *np, void __iomem *base)
2685bf6d4eSHuang Shijie {
2785bf6d4eSHuang Shijie 	u32 value[CS_TIMING_LEN];
2885bf6d4eSHuang Shijie 	u32 cs_idx;
2985bf6d4eSHuang Shijie 	int ret;
3085bf6d4eSHuang Shijie 	int i;
3185bf6d4eSHuang Shijie 
3285bf6d4eSHuang Shijie 	/* get the CS index from this child node's "reg" property. */
3385bf6d4eSHuang Shijie 	ret = of_property_read_u32(np, "reg", &cs_idx);
3485bf6d4eSHuang Shijie 	if (ret)
3585bf6d4eSHuang Shijie 		return ret;
3685bf6d4eSHuang Shijie 
3785bf6d4eSHuang Shijie 	/* The weim has four chip selects. */
3885bf6d4eSHuang Shijie 	if (cs_idx > 3)
3985bf6d4eSHuang Shijie 		return -EINVAL;
4085bf6d4eSHuang Shijie 
4185bf6d4eSHuang Shijie 	ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
4285bf6d4eSHuang Shijie 					value, CS_TIMING_LEN);
4385bf6d4eSHuang Shijie 	if (ret)
4485bf6d4eSHuang Shijie 		return ret;
4585bf6d4eSHuang Shijie 
4685bf6d4eSHuang Shijie 	/* set the timing for WEIM */
4785bf6d4eSHuang Shijie 	for (i = 0; i < CS_TIMING_LEN; i++)
4870ac98daSAlexander Shiyan 		writel(value[i], base + cs_idx * CS_REG_RANGE + i * 4);
4985bf6d4eSHuang Shijie 	return 0;
5085bf6d4eSHuang Shijie }
5185bf6d4eSHuang Shijie 
52*29e54970SAlexander Shiyan static int __init weim_parse_dt(struct platform_device *pdev,
53*29e54970SAlexander Shiyan 				void __iomem *base)
5485bf6d4eSHuang Shijie {
5585bf6d4eSHuang Shijie 	struct device_node *child;
5685bf6d4eSHuang Shijie 	int ret;
5785bf6d4eSHuang Shijie 
5885bf6d4eSHuang Shijie 	for_each_child_of_node(pdev->dev.of_node, child) {
5985bf6d4eSHuang Shijie 		if (!child->name)
6085bf6d4eSHuang Shijie 			continue;
6185bf6d4eSHuang Shijie 
6270ac98daSAlexander Shiyan 		ret = weim_timing_setup(child, base);
6385bf6d4eSHuang Shijie 		if (ret) {
6485bf6d4eSHuang Shijie 			dev_err(&pdev->dev, "%s set timing failed.\n",
6585bf6d4eSHuang Shijie 				child->full_name);
6685bf6d4eSHuang Shijie 			return ret;
6785bf6d4eSHuang Shijie 		}
6885bf6d4eSHuang Shijie 	}
6985bf6d4eSHuang Shijie 
7085bf6d4eSHuang Shijie 	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
7185bf6d4eSHuang Shijie 	if (ret)
7285bf6d4eSHuang Shijie 		dev_err(&pdev->dev, "%s fail to create devices.\n",
7385bf6d4eSHuang Shijie 			pdev->dev.of_node->full_name);
7485bf6d4eSHuang Shijie 	return ret;
7585bf6d4eSHuang Shijie }
7685bf6d4eSHuang Shijie 
77*29e54970SAlexander Shiyan static int __init weim_probe(struct platform_device *pdev)
7885bf6d4eSHuang Shijie {
7985bf6d4eSHuang Shijie 	struct resource *res;
8070ac98daSAlexander Shiyan 	struct clk *clk;
8170ac98daSAlexander Shiyan 	void __iomem *base;
82b2d1fb73SAlexander Shiyan 	int ret;
8385bf6d4eSHuang Shijie 
8485bf6d4eSHuang Shijie 	/* get the resource */
8585bf6d4eSHuang Shijie 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8670ac98daSAlexander Shiyan 	base = devm_ioremap_resource(&pdev->dev, res);
87b2d1fb73SAlexander Shiyan 	if (IS_ERR(base))
88b2d1fb73SAlexander Shiyan 		return PTR_ERR(base);
8985bf6d4eSHuang Shijie 
9085bf6d4eSHuang Shijie 	/* get the clock */
9170ac98daSAlexander Shiyan 	clk = devm_clk_get(&pdev->dev, NULL);
9270ac98daSAlexander Shiyan 	if (IS_ERR(clk))
93b2d1fb73SAlexander Shiyan 		return PTR_ERR(clk);
9485bf6d4eSHuang Shijie 
9570ac98daSAlexander Shiyan 	ret = clk_prepare_enable(clk);
9685bf6d4eSHuang Shijie 	if (ret)
97b2d1fb73SAlexander Shiyan 		return ret;
9885bf6d4eSHuang Shijie 
9985bf6d4eSHuang Shijie 	/* parse the device node */
10070ac98daSAlexander Shiyan 	ret = weim_parse_dt(pdev, base);
101b2d1fb73SAlexander Shiyan 	if (ret)
10270ac98daSAlexander Shiyan 		clk_disable_unprepare(clk);
103b2d1fb73SAlexander Shiyan 	else
104b2d1fb73SAlexander Shiyan 		dev_info(&pdev->dev, "Driver registered.\n");
10585bf6d4eSHuang Shijie 
10685bf6d4eSHuang Shijie 	return ret;
10785bf6d4eSHuang Shijie }
10885bf6d4eSHuang Shijie 
10985bf6d4eSHuang Shijie static struct platform_driver weim_driver = {
11085bf6d4eSHuang Shijie 	.driver = {
11185bf6d4eSHuang Shijie 		.name = "imx-weim",
11285bf6d4eSHuang Shijie 		.of_match_table = weim_id_table,
11385bf6d4eSHuang Shijie 	},
11485bf6d4eSHuang Shijie };
115*29e54970SAlexander Shiyan module_platform_driver_probe(weim_driver, weim_probe);
11685bf6d4eSHuang Shijie 
11785bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc.");
11885bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver");
11985bf6d4eSHuang Shijie MODULE_LICENSE("GPL");
120