]>
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 | ||
04635a30 EJ |
80 | static int hub_master_link_enable(struct fsi_master *master, int link, |
81 | bool enable) | |
7f9e8f76 CB |
82 | { |
83 | struct fsi_master_hub *hub = to_fsi_master_hub(master); | |
84 | int idx, bit; | |
85 | __be32 reg; | |
86 | int rc; | |
87 | ||
88 | idx = link / 32; | |
89 | bit = link % 32; | |
90 | ||
91 | reg = cpu_to_be32(0x80000000 >> bit); | |
92 | ||
04635a30 EJ |
93 | if (!enable) |
94 | return fsi_device_write(hub->upstream, FSI_MCENP0 + (4 * idx), | |
95 | ®, 4); | |
96 | ||
7f9e8f76 | 97 | rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), ®, 4); |
a1d5ce11 EJ |
98 | if (rc) |
99 | return rc; | |
7f9e8f76 CB |
100 | |
101 | mdelay(FSI_LINK_ENABLE_SETUP_TIME); | |
102 | ||
a1d5ce11 | 103 | return 0; |
7f9e8f76 CB |
104 | } |
105 | ||
106 | static void hub_master_release(struct device *dev) | |
107 | { | |
d5d8dfb0 | 108 | struct fsi_master_hub *hub = to_fsi_master_hub(to_fsi_master(dev)); |
7f9e8f76 CB |
109 | |
110 | kfree(hub); | |
111 | } | |
112 | ||
113 | /* mmode encoders */ | |
114 | static inline u32 fsi_mmode_crs0(u32 x) | |
115 | { | |
116 | return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT; | |
117 | } | |
118 | ||
119 | static inline u32 fsi_mmode_crs1(u32 x) | |
120 | { | |
121 | return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT; | |
122 | } | |
123 | ||
124 | static int hub_master_init(struct fsi_master_hub *hub) | |
125 | { | |
126 | struct fsi_device *dev = hub->upstream; | |
127 | __be32 reg; | |
128 | int rc; | |
129 | ||
130 | reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK | |
131 | | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE); | |
132 | rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg)); | |
133 | if (rc) | |
134 | return rc; | |
135 | ||
136 | /* Initialize the MFSI (hub master) engine */ | |
137 | reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK | |
138 | | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE); | |
139 | rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg)); | |
140 | if (rc) | |
141 | return rc; | |
142 | ||
143 | reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM); | |
144 | rc = fsi_device_write(dev, FSI_MECTRL, ®, sizeof(reg)); | |
145 | if (rc) | |
146 | return rc; | |
147 | ||
148 | reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC | |
149 | | fsi_mmode_crs0(1) | fsi_mmode_crs1(1) | |
150 | | FSI_MMODE_P8_TO_LSB); | |
151 | rc = fsi_device_write(dev, FSI_MMODE, ®, sizeof(reg)); | |
152 | if (rc) | |
153 | return rc; | |
154 | ||
155 | reg = cpu_to_be32(0xffff0000); | |
156 | rc = fsi_device_write(dev, FSI_MDLYR, ®, sizeof(reg)); | |
157 | if (rc) | |
158 | return rc; | |
159 | ||
fbdb5eac | 160 | reg = cpu_to_be32(~0); |
7f9e8f76 CB |
161 | rc = fsi_device_write(dev, FSI_MSENP0, ®, sizeof(reg)); |
162 | if (rc) | |
163 | return rc; | |
164 | ||
165 | /* Leave enabled long enough for master logic to set up */ | |
166 | mdelay(FSI_LINK_ENABLE_SETUP_TIME); | |
167 | ||
168 | rc = fsi_device_write(dev, FSI_MCENP0, ®, sizeof(reg)); | |
169 | if (rc) | |
170 | return rc; | |
171 | ||
172 | rc = fsi_device_read(dev, FSI_MAEB, ®, sizeof(reg)); | |
173 | if (rc) | |
174 | return rc; | |
175 | ||
176 | reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK); | |
177 | rc = fsi_device_write(dev, FSI_MRESP0, ®, sizeof(reg)); | |
178 | if (rc) | |
179 | return rc; | |
180 | ||
181 | rc = fsi_device_read(dev, FSI_MLEVP0, ®, sizeof(reg)); | |
182 | if (rc) | |
183 | return rc; | |
184 | ||
185 | /* Reset the master bridge */ | |
186 | reg = cpu_to_be32(FSI_MRESB_RST_GEN); | |
187 | rc = fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg)); | |
188 | if (rc) | |
189 | return rc; | |
190 | ||
191 | reg = cpu_to_be32(FSI_MRESB_RST_ERR); | |
192 | return fsi_device_write(dev, FSI_MRESB0, ®, sizeof(reg)); | |
193 | } | |
194 | ||
195 | static int hub_master_probe(struct device *dev) | |
196 | { | |
197 | struct fsi_device *fsi_dev = to_fsi_dev(dev); | |
198 | struct fsi_master_hub *hub; | |
199 | uint32_t reg, links; | |
200 | __be32 __reg; | |
201 | int rc; | |
202 | ||
203 | rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg)); | |
204 | if (rc) | |
205 | return rc; | |
206 | ||
207 | reg = be32_to_cpu(__reg); | |
208 | links = (reg >> 8) & 0xff; | |
638bd9ac | 209 | dev_dbg(dev, "hub version %08x (%d links)\n", reg, links); |
7f9e8f76 CB |
210 | |
211 | rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, | |
212 | FSI_HUB_LINK_SIZE * links); | |
213 | if (rc) { | |
214 | dev_err(dev, "can't claim slave address range for links"); | |
215 | return rc; | |
216 | } | |
217 | ||
218 | hub = kzalloc(sizeof(*hub), GFP_KERNEL); | |
219 | if (!hub) { | |
220 | rc = -ENOMEM; | |
221 | goto err_release; | |
222 | } | |
223 | ||
224 | hub->addr = FSI_HUB_LINK_OFFSET; | |
225 | hub->size = FSI_HUB_LINK_SIZE * links; | |
226 | hub->upstream = fsi_dev; | |
227 | ||
228 | hub->master.dev.parent = dev; | |
229 | hub->master.dev.release = hub_master_release; | |
f6a2f8eb | 230 | hub->master.dev.of_node = of_node_get(dev_of_node(dev)); |
7f9e8f76 CB |
231 | |
232 | hub->master.n_links = links; | |
233 | hub->master.read = hub_master_read; | |
234 | hub->master.write = hub_master_write; | |
235 | hub->master.send_break = hub_master_break; | |
236 | hub->master.link_enable = hub_master_link_enable; | |
237 | ||
238 | dev_set_drvdata(dev, hub); | |
239 | ||
240 | hub_master_init(hub); | |
241 | ||
242 | rc = fsi_master_register(&hub->master); | |
e0c24bdd JK |
243 | if (rc) |
244 | goto err_release; | |
245 | ||
246 | /* At this point, fsi_master_register performs the device_initialize(), | |
247 | * and holds the sole reference on master.dev. This means the device | |
248 | * will be freed (via ->release) during any subsequent call to | |
249 | * fsi_master_unregister. We add our own reference to it here, so we | |
250 | * can perform cleanup (in _remove()) without it being freed before | |
251 | * we're ready. | |
252 | */ | |
253 | get_device(&hub->master.dev); | |
254 | return 0; | |
7f9e8f76 | 255 | |
7f9e8f76 CB |
256 | err_release: |
257 | fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, | |
258 | FSI_HUB_LINK_SIZE * links); | |
259 | return rc; | |
260 | } | |
261 | ||
262 | static int hub_master_remove(struct device *dev) | |
263 | { | |
264 | struct fsi_master_hub *hub = dev_get_drvdata(dev); | |
265 | ||
266 | fsi_master_unregister(&hub->master); | |
267 | fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size); | |
f6a2f8eb JK |
268 | of_node_put(hub->master.dev.of_node); |
269 | ||
e0c24bdd JK |
270 | /* |
271 | * master.dev will likely be ->release()ed after this, which free()s | |
272 | * the hub | |
273 | */ | |
274 | put_device(&hub->master.dev); | |
275 | ||
7f9e8f76 CB |
276 | return 0; |
277 | } | |
278 | ||
31901bb7 | 279 | static const struct fsi_device_id hub_master_ids[] = { |
7f9e8f76 CB |
280 | { |
281 | .engine_type = FSI_ENGID_HUB_MASTER, | |
282 | .version = FSI_VERSION_ANY, | |
283 | }, | |
284 | { 0 } | |
285 | }; | |
286 | ||
287 | static struct fsi_driver hub_master_driver = { | |
288 | .id_table = hub_master_ids, | |
289 | .drv = { | |
290 | .name = "fsi-master-hub", | |
291 | .bus = &fsi_bus_type, | |
292 | .probe = hub_master_probe, | |
293 | .remove = hub_master_remove, | |
294 | } | |
295 | }; | |
296 | ||
297 | module_fsi_driver(hub_master_driver); | |
298 | MODULE_LICENSE("GPL"); |