Commit | Line | Data |
---|---|---|
26a84b3e KVA |
1 | /* |
2 | * omap-ocp2scp.c - transform ocp interface protocol to scp protocol | |
3 | * | |
4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * Author: Kishon Vijay Abraham I <kishon@ti.com> | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/err.h> | |
22 | #include <linux/pm_runtime.h> | |
23 | #include <linux/of.h> | |
24 | #include <linux/of_platform.h> | |
0133370f KVA |
25 | #include <linux/platform_data/omap_ocp2scp.h> |
26 | ||
27 | /** | |
28 | * _count_resources - count for the number of resources | |
29 | * @res: struct resource * | |
30 | * | |
31 | * Count and return the number of resources populated for the device that is | |
32 | * connected to ocp2scp. | |
33 | */ | |
34 | static unsigned _count_resources(struct resource *res) | |
35 | { | |
36 | int cnt = 0; | |
37 | ||
38 | while (res->start != res->end) { | |
39 | cnt++; | |
40 | res++; | |
41 | } | |
42 | ||
43 | return cnt; | |
44 | } | |
26a84b3e KVA |
45 | |
46 | static int ocp2scp_remove_devices(struct device *dev, void *c) | |
47 | { | |
48 | struct platform_device *pdev = to_platform_device(dev); | |
49 | ||
50 | platform_device_unregister(pdev); | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
0fe763c5 | 55 | static int omap_ocp2scp_probe(struct platform_device *pdev) |
26a84b3e | 56 | { |
0133370f KVA |
57 | int ret; |
58 | unsigned res_cnt, i; | |
59 | struct device_node *np = pdev->dev.of_node; | |
60 | struct platform_device *pdev_child; | |
61 | struct omap_ocp2scp_platform_data *pdata = pdev->dev.platform_data; | |
62 | struct omap_ocp2scp_dev *dev; | |
26a84b3e KVA |
63 | |
64 | if (np) { | |
65 | ret = of_platform_populate(np, NULL, NULL, &pdev->dev); | |
66 | if (ret) { | |
0133370f KVA |
67 | dev_err(&pdev->dev, |
68 | "failed to add resources for ocp2scp child\n"); | |
26a84b3e KVA |
69 | goto err0; |
70 | } | |
0133370f KVA |
71 | } else if (pdata) { |
72 | for (i = 0, dev = *pdata->devices; i < pdata->dev_cnt; i++, | |
73 | dev++) { | |
74 | res_cnt = _count_resources(dev->res); | |
75 | ||
76 | pdev_child = platform_device_alloc(dev->drv_name, | |
77 | PLATFORM_DEVID_AUTO); | |
78 | if (!pdev_child) { | |
79 | dev_err(&pdev->dev, | |
80 | "failed to allocate mem for ocp2scp child\n"); | |
81 | goto err0; | |
82 | } | |
83 | ||
84 | ret = platform_device_add_resources(pdev_child, | |
85 | dev->res, res_cnt); | |
86 | if (ret) { | |
87 | dev_err(&pdev->dev, | |
88 | "failed to add resources for ocp2scp child\n"); | |
89 | goto err1; | |
90 | } | |
91 | ||
92 | pdev_child->dev.parent = &pdev->dev; | |
93 | ||
94 | ret = platform_device_add(pdev_child); | |
95 | if (ret) { | |
96 | dev_err(&pdev->dev, | |
97 | "failed to register ocp2scp child device\n"); | |
98 | goto err1; | |
99 | } | |
100 | } | |
101 | } else { | |
102 | dev_err(&pdev->dev, "OCP2SCP initialized without plat data\n"); | |
103 | return -EINVAL; | |
26a84b3e | 104 | } |
0133370f | 105 | |
26a84b3e KVA |
106 | pm_runtime_enable(&pdev->dev); |
107 | ||
108 | return 0; | |
109 | ||
0133370f KVA |
110 | err1: |
111 | platform_device_put(pdev_child); | |
112 | ||
26a84b3e KVA |
113 | err0: |
114 | device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices); | |
115 | ||
116 | return ret; | |
117 | } | |
118 | ||
0fe763c5 | 119 | static int omap_ocp2scp_remove(struct platform_device *pdev) |
26a84b3e KVA |
120 | { |
121 | pm_runtime_disable(&pdev->dev); | |
122 | device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices); | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | #ifdef CONFIG_OF | |
128 | static const struct of_device_id omap_ocp2scp_id_table[] = { | |
129 | { .compatible = "ti,omap-ocp2scp" }, | |
130 | {} | |
131 | }; | |
46ca6811 | 132 | MODULE_DEVICE_TABLE(of, omap_ocp2scp_id_table); |
26a84b3e KVA |
133 | #endif |
134 | ||
135 | static struct platform_driver omap_ocp2scp_driver = { | |
136 | .probe = omap_ocp2scp_probe, | |
0fe763c5 | 137 | .remove = omap_ocp2scp_remove, |
26a84b3e KVA |
138 | .driver = { |
139 | .name = "omap-ocp2scp", | |
140 | .owner = THIS_MODULE, | |
141 | .of_match_table = of_match_ptr(omap_ocp2scp_id_table), | |
142 | }, | |
143 | }; | |
144 | ||
145 | module_platform_driver(omap_ocp2scp_driver); | |
146 | ||
147 | MODULE_ALIAS("platform: omap-ocp2scp"); | |
148 | MODULE_AUTHOR("Texas Instruments Inc."); | |
149 | MODULE_DESCRIPTION("OMAP OCP2SCP driver"); | |
150 | MODULE_LICENSE("GPL v2"); |