]>
Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
7f9e8f76 CB |
2 | /* |
3 | * FSI hub master driver | |
4 | * | |
5 | * Copyright (C) IBM Corporation 2016 | |
7f9e8f76 CB |
6 | */ |
7 | ||
8 | #include <linux/delay.h> | |
9 | #include <linux/fsi.h> | |
10 | #include <linux/module.h> | |
f6a2f8eb | 11 | #include <linux/of.h> |
7f9e8f76 CB |
12 | #include <linux/slab.h> |
13 | ||
14 | #include "fsi-master.h" | |
15 | ||
7f9e8f76 | 16 | #define FSI_ENGID_HUB_MASTER 0x1c |
7f9e8f76 CB |
17 | |
18 | #define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */ | |
19 | ||
20 | /* | |
21 | * FSI hub master support | |
22 | * | |
23 | * A hub master increases the number of potential target devices that the | |
24 | * primary FSI master can access. For each link a primary master supports, | |
25 | * each of those links can in turn be chained to a hub master with multiple | |
26 | * links of its own. | |
27 | * | |
28 | * The hub is controlled by a set of control registers exposed as a regular fsi | |
29 | * device (the hub->upstream device), and provides access to the downstream FSI | |
30 | * bus as through an address range on the slave itself (->addr and ->size). | |
31 | * | |
32 | * [This differs from "cascaded" masters, which expose the entire downstream | |
33 | * bus entirely through the fsi device address range, and so have a smaller | |
34 | * accessible address space.] | |
35 | */ | |
36 | struct fsi_master_hub { | |
37 | struct fsi_master master; | |
38 | struct fsi_device *upstream; | |
39 | uint32_t addr, size; /* slave-relative addr of */ | |
40 | /* master address space */ | |
41 | }; | |
42 | ||
43 | #define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master) | |
44 | ||
45 | static int hub_master_read(struct fsi_master *master, int link, | |
46 | uint8_t id, uint32_t addr, void *val, size_t size) | |
47 | { | |
48 | struct fsi_master_hub *hub = to_fsi_master_hub(master); | |
49 | ||
50 | if (id != 0) | |
51 | return -EINVAL; | |
52 | ||
53 | addr += hub->addr + (link * FSI_HUB_LINK_SIZE); | |
54 | return fsi_slave_read(hub->upstream->slave, addr, val, size); | |
55 | } | |
56 | ||
57 | static int hub_master_write(struct fsi_master *master, int link, | |
58 | uint8_t id, uint32_t addr, const void *val, size_t size) | |
59 | { | |
60 | struct fsi_master_hub *hub = to_fsi_master_hub(master); | |
61 | ||
62 | if (id != 0) | |
63 | return -EINVAL; | |
64 | ||
65 | addr += hub->addr + (link * FSI_HUB_LINK_SIZE); | |
66 | return fsi_slave_write(hub->upstream->slave, addr, val, size); | |
67 | } | |
68 | ||
69 | static int hub_master_break(struct fsi_master *master, int link) | |
70 | { | |
fbdb5eac JS |
71 | uint32_t addr; |
72 | __be32 cmd; | |
7f9e8f76 CB |
73 | |
74 | addr = 0x4; | |
75 | cmd = cpu_to_be32(0xc0de0000); | |
76 | ||
77 | return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd)); | |
78 | } | |
79 | ||
80 | static int hub_master_link_enable(struct fsi_master *master, int link) | |
81 | { | |
82 | struct fsi_master_hub *hub = to_fsi_master_hub(master); | |
83 | int idx, bit; | |
84 | __be32 reg; | |
85 | int rc; | |
86 | ||
87 | idx = link / 32; | |
88 | bit = link % 32; | |
89 | ||
90 | reg = cpu_to_be32(0x80000000 >> bit); | |
91 | ||
92 | rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), ®, 4); | |
93 | ||
94 | mdelay(FSI_LINK_ENABLE_SETUP_TIME); | |
95 | ||
96 | fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), ®, 4); | |
97 | ||
98 | return rc; | |
99 | } | |
100 | ||
101 | static void hub_master_release(struct device *dev) | |
102 | { | |
103 | struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev)); | |
104 | ||
105 | kfree(hub); | |
106 | } | |
107 | ||
108 | /* mmode encoders */ | |
109 | static inline u32 fsi_mmode_crs0(u32 x) | |
110 | { | |
111 | return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT; | |
112 | } | |
113 | ||
114 | static inline u32 fsi_mmode_crs1(u32 x) | |
115 | { | |
116 | return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT; | |
117 | } | |
118 | ||
119 | static int hub_master_init(struct fsi_master_hub *hub) | |
120 | { | |
121 | struct fsi_device *dev = hub->upstream; | |
122 | __be32 reg; | |
123 | int rc; | |
124 | ||
125 | reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK | |
126 | | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE); | |
127 | rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg)); | |
128 | if (rc) | |
129 | return rc; | |
130 | ||
131 | /* Initialize the MFSI (hub master) engine */ | |
132 | reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK | |
133 | | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE); | |
134 | rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg)); | |
135 | if (rc) | |
136 | return rc; | |
137 | ||
138 | reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM); | |
139 | rc = fsi_device_write(dev, FSI_MECTRL, ®, sizeof(reg)); | |
140 | if (rc) | |
141 | return rc; | |
142 | ||
143 | reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC | |
144 | | fsi_mmode_crs0(1) | fsi_mmode_crs1(1) | |
145 | | FSI_MMODE_P8_TO_LSB); | |
146 | rc = fsi_device_write(dev, FSI_MMODE, ®, sizeof(reg)); | |
147 | if (rc) | |
148 | return rc; | |
149 | ||
150 | reg = cpu_to_be32(0xffff0000); | |
151 | rc = fsi_device_write(dev, FSI_MDLYR, ®, sizeof(reg)); | |
152 | if (rc) | |
153 | return rc; | |
154 | ||
fbdb5eac | 155 | reg = cpu_to_be32(~0); |
7f9e8f76 CB |
156 | rc = fsi_device_write(dev, FSI_MSENP0, ®, sizeof(reg)); |
157 | if (rc) | |
158 | return rc; | |
159 | ||
160 | /* Leave enabled long enough for master logic to set up */ | |
161 | mdelay(FSI_LINK_ENABLE_SETUP_TIME); | |
162 | ||
163 | rc = fsi_device_write(dev, FSI_MCENP0, ®, sizeof(reg)); | |
164 | if (rc) | |
165 | return rc; | |
166 | ||
167 | rc = fsi_device_read(dev, FSI_MAEB, ®, sizeof(reg)); | |
168 | if (rc) | |
169 | return rc; | |
170 | ||
171 | reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK); | |
172 | rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg)); | |
173 | if (rc) | |
174 | return rc; | |
175 | ||
176 | rc = fsi_device_read(dev, FSI_MLEVP0, ®, sizeof(reg)); | |
177 | if (rc) | |
178 | return rc; | |
179 | ||
180 | /* Reset the master bridge */ | |
181 | reg = cpu_to_be32(FSI_MRESB_RST_GEN); | |
182 | rc = fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg)); | |
183 | if (rc) | |
184 | return rc; | |
185 | ||
186 | reg = cpu_to_be32(FSI_MRESB_RST_ERR); | |
187 | return fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg)); | |
188 | } | |
189 | ||
190 | static int hub_master_probe(struct device *dev) | |
191 | { | |
192 | struct fsi_device *fsi_dev = to_fsi_dev(dev); | |
193 | struct fsi_master_hub *hub; | |
194 | uint32_t reg, links; | |
195 | __be32 __reg; | |
196 | int rc; | |
197 | ||
198 | rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg)); | |
199 | if (rc) | |
200 | return rc; | |
201 | ||
202 | reg = be32_to_cpu(__reg); | |
203 | links = (reg >> 8) & 0xff; | |
638bd9ac | 204 | dev_dbg(dev, "hub version %08x (%d links)\n", reg, links); |
7f9e8f76 CB |
205 | |
206 | rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, | |
207 | FSI_HUB_LINK_SIZE * links); | |
208 | if (rc) { | |
209 | dev_err(dev, "can't claim slave address range for links"); | |
210 | return rc; | |
211 | } | |
212 | ||
213 | hub = kzalloc(sizeof(*hub), GFP_KERNEL); | |
214 | if (!hub) { | |
215 | rc = -ENOMEM; | |
216 | goto err_release; | |
217 | } | |
218 | ||
219 | hub->addr = FSI_HUB_LINK_OFFSET; | |
220 | hub->size = FSI_HUB_LINK_SIZE * links; | |
221 | hub->upstream = fsi_dev; | |
222 | ||
223 | hub->master.dev.parent = dev; | |
224 | hub->master.dev.release = hub_master_release; | |
f6a2f8eb | 225 | hub->master.dev.of_node = of_node_get(dev_of_node(dev)); |
7f9e8f76 CB |
226 | |
227 | hub->master.n_links = links; | |
228 | hub->master.read = hub_master_read; | |
229 | hub->master.write = hub_master_write; | |
230 | hub->master.send_break = hub_master_break; | |
231 | hub->master.link_enable = hub_master_link_enable; | |
232 | ||
233 | dev_set_drvdata(dev, hub); | |
234 | ||
235 | hub_master_init(hub); | |
236 | ||
237 | rc = fsi_master_register(&hub->master); | |
e0c24bdd JK |
238 | if (rc) |
239 | goto err_release; | |
240 | ||
241 | /* At this point, fsi_master_register performs the device_initialize(), | |
242 | * and holds the sole reference on master.dev. This means the device | |
243 | * will be freed (via ->release) during any subsequent call to | |
244 | * fsi_master_unregister. We add our own reference to it here, so we | |
245 | * can perform cleanup (in _remove()) without it being freed before | |
246 | * we're ready. | |
247 | */ | |
248 | get_device(&hub->master.dev); | |
249 | return 0; | |
7f9e8f76 | 250 | |
7f9e8f76 CB |
251 | err_release: |
252 | fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, | |
253 | FSI_HUB_LINK_SIZE * links); | |
254 | return rc; | |
255 | } | |
256 | ||
257 | static int hub_master_remove(struct device *dev) | |
258 | { | |
259 | struct fsi_master_hub *hub = dev_get_drvdata(dev); | |
260 | ||
261 | fsi_master_unregister(&hub->master); | |
262 | fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size); | |
f6a2f8eb JK |
263 | of_node_put(hub->master.dev.of_node); |
264 | ||
e0c24bdd JK |
265 | /* |
266 | * master.dev will likely be ->release()ed after this, which free()s | |
267 | * the hub | |
268 | */ | |
269 | put_device(&hub->master.dev); | |
270 | ||
7f9e8f76 CB |
271 | return 0; |
272 | } | |
273 | ||
274 | static struct fsi_device_id hub_master_ids[] = { | |
275 | { | |
276 | .engine_type = FSI_ENGID_HUB_MASTER, | |
277 | .version = FSI_VERSION_ANY, | |
278 | }, | |
279 | { 0 } | |
280 | }; | |
281 | ||
282 | static struct fsi_driver hub_master_driver = { | |
283 | .id_table = hub_master_ids, | |
284 | .drv = { | |
285 | .name = "fsi-master-hub", | |
286 | .bus = &fsi_bus_type, | |
287 | .probe = hub_master_probe, | |
288 | .remove = hub_master_remove, | |
289 | } | |
290 | }; | |
291 | ||
292 | module_fsi_driver(hub_master_driver); | |
293 | MODULE_LICENSE("GPL"); |