]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/extres/phy/phy.c
Further refine the ExpDataSN checks for SCSI Response PDUs.
[FreeBSD/FreeBSD.git] / sys / dev / extres / phy / phy.c
1 /*-
2  * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27  #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_platform.h"
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/kobj.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/queue.h>
37 #include <sys/systm.h>
38 #include <sys/sx.h>
39
40 #ifdef FDT
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 #endif
44
45 #include  <dev/extres/phy/phy.h>
46 #include  <dev/extres/phy/phy_internal.h>
47
48 #include "phydev_if.h"
49
50 MALLOC_DEFINE(M_PHY, "phy", "Phy framework");
51
52 /* Default phy methods. */
53 static int phynode_method_init(struct phynode *phynode);
54 static int phynode_method_enable(struct phynode *phynode, bool disable);
55 static int phynode_method_status(struct phynode *phynode, int *status);
56
57
58 /*
59  * Phy controller methods.
60  */
61 static phynode_method_t phynode_methods[] = {
62         PHYNODEMETHOD(phynode_init,             phynode_method_init),
63         PHYNODEMETHOD(phynode_enable,           phynode_method_enable),
64         PHYNODEMETHOD(phynode_status,           phynode_method_status),
65
66         PHYNODEMETHOD_END
67 };
68 DEFINE_CLASS_0(phynode, phynode_class, phynode_methods, 0);
69
70 static phynode_list_t phynode_list = TAILQ_HEAD_INITIALIZER(phynode_list);
71 struct sx phynode_topo_lock;
72 SX_SYSINIT(phy_topology, &phynode_topo_lock, "Phy topology lock");
73
74 /* ----------------------------------------------------------------------------
75  *
76  * Default phy methods for base class.
77  *
78  */
79
80 static int
81 phynode_method_init(struct phynode *phynode)
82 {
83
84         return (0);
85 }
86
87 static int
88 phynode_method_enable(struct phynode *phynode, bool enable)
89 {
90
91         if (!enable)
92                 return (ENXIO);
93
94         return (0);
95 }
96
97 static int
98 phynode_method_status(struct phynode *phynode, int *status)
99 {
100         *status = PHY_STATUS_ENABLED;
101         return (0);
102 }
103
104 /* ----------------------------------------------------------------------------
105  *
106  * Internal functions.
107  *
108  */
109 /*
110  * Create and initialize phy object, but do not register it.
111  */
112 struct phynode *
113 phynode_create(device_t pdev, phynode_class_t phynode_class,
114     struct phynode_init_def *def)
115 {
116         struct phynode *phynode;
117
118
119         /* Create object and initialize it. */
120         phynode = malloc(sizeof(struct phynode), M_PHY, M_WAITOK | M_ZERO);
121         kobj_init((kobj_t)phynode, (kobj_class_t)phynode_class);
122         sx_init(&phynode->lock, "Phy node lock");
123
124         /* Allocate softc if required. */
125         if (phynode_class->size > 0) {
126                 phynode->softc = malloc(phynode_class->size, M_PHY,
127                     M_WAITOK | M_ZERO);
128         }
129
130         /* Rest of init. */
131         TAILQ_INIT(&phynode->consumers_list);
132         phynode->id = def->id;
133         phynode->pdev = pdev;
134 #ifdef FDT
135         phynode->ofw_node = def->ofw_node;
136 #endif
137
138         return (phynode);
139 }
140
141 /* Register phy object. */
142 struct phynode *
143 phynode_register(struct phynode *phynode)
144 {
145         int rv;
146
147 #ifdef FDT
148         if (phynode->ofw_node <= 0)
149                 phynode->ofw_node = ofw_bus_get_node(phynode->pdev);
150         if (phynode->ofw_node <= 0)
151                 return (NULL);
152 #endif
153
154         rv = PHYNODE_INIT(phynode);
155         if (rv != 0) {
156                 printf("PHYNODE_INIT failed: %d\n", rv);
157                 return (NULL);
158         }
159
160         PHY_TOPO_XLOCK();
161         TAILQ_INSERT_TAIL(&phynode_list, phynode, phylist_link);
162         PHY_TOPO_UNLOCK();
163 #ifdef FDT
164         OF_device_register_xref(OF_xref_from_node(phynode->ofw_node),
165             phynode->pdev);
166 #endif
167         return (phynode);
168 }
169
170 static struct phynode *
171 phynode_find_by_id(device_t dev, intptr_t id)
172 {
173         struct phynode *entry;
174
175         PHY_TOPO_ASSERT();
176
177         TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
178                 if ((entry->pdev == dev) && (entry->id ==  id))
179                         return (entry);
180         }
181
182         return (NULL);
183 }
184
185 /* --------------------------------------------------------------------------
186  *
187  * Phy providers interface
188  *
189  */
190
191 void *
192 phynode_get_softc(struct phynode *phynode)
193 {
194
195         return (phynode->softc);
196 }
197
198 device_t
199 phynode_get_device(struct phynode *phynode)
200 {
201
202         return (phynode->pdev);
203 }
204
205 intptr_t phynode_get_id(struct phynode *phynode)
206 {
207
208         return (phynode->id);
209 }
210
211 #ifdef FDT
212 phandle_t
213 phynode_get_ofw_node(struct phynode *phynode)
214 {
215
216         return (phynode->ofw_node);
217 }
218 #endif
219
220 /* --------------------------------------------------------------------------
221  *
222  * Real consumers executive
223  *
224  */
225
226 /*
227  * Enable phy.
228  */
229 int
230 phynode_enable(struct phynode *phynode)
231 {
232         int rv;
233
234         PHY_TOPO_ASSERT();
235
236         PHYNODE_XLOCK(phynode);
237         if (phynode->enable_cnt == 0) {
238                 rv = PHYNODE_ENABLE(phynode, true);
239                 if (rv != 0) {
240                         PHYNODE_UNLOCK(phynode);
241                         return (rv);
242                 }
243         }
244         phynode->enable_cnt++;
245         PHYNODE_UNLOCK(phynode);
246         return (0);
247 }
248
249 /*
250  * Disable phy.
251  */
252 int
253 phynode_disable(struct phynode *phynode)
254 {
255         int rv;
256
257         PHY_TOPO_ASSERT();
258
259         PHYNODE_XLOCK(phynode);
260         if (phynode->enable_cnt == 1) {
261                 rv = PHYNODE_ENABLE(phynode, false);
262                 if (rv != 0) {
263                         PHYNODE_UNLOCK(phynode);
264                         return (rv);
265                 }
266         }
267         phynode->enable_cnt--;
268         PHYNODE_UNLOCK(phynode);
269         return (0);
270 }
271
272
273 /*
274  * Get phy status. (PHY_STATUS_*)
275  */
276 int
277 phynode_status(struct phynode *phynode, int *status)
278 {
279         int rv;
280
281         PHY_TOPO_ASSERT();
282
283         PHYNODE_XLOCK(phynode);
284         rv = PHYNODE_STATUS(phynode, status);
285         PHYNODE_UNLOCK(phynode);
286         return (rv);
287 }
288
289  /* --------------------------------------------------------------------------
290  *
291  * Phy consumers interface.
292  *
293  */
294
295 /* Helper function for phy_get*() */
296 static phy_t
297 phy_create(struct phynode *phynode, device_t cdev)
298 {
299         struct phy *phy;
300
301         PHY_TOPO_ASSERT();
302
303         phy =  malloc(sizeof(struct phy), M_PHY, M_WAITOK | M_ZERO);
304         phy->cdev = cdev;
305         phy->phynode = phynode;
306         phy->enable_cnt = 0;
307
308         PHYNODE_XLOCK(phynode);
309         phynode->ref_cnt++;
310         TAILQ_INSERT_TAIL(&phynode->consumers_list, phy, link);
311         PHYNODE_UNLOCK(phynode);
312
313         return (phy);
314 }
315
316 int
317 phy_enable(phy_t phy)
318 {
319         int rv;
320         struct phynode *phynode;
321
322         phynode = phy->phynode;
323         KASSERT(phynode->ref_cnt > 0,
324             ("Attempt to access unreferenced phy.\n"));
325
326         PHY_TOPO_SLOCK();
327         rv = phynode_enable(phynode);
328         if (rv == 0)
329                 phy->enable_cnt++;
330         PHY_TOPO_UNLOCK();
331         return (rv);
332 }
333
334 int
335 phy_disable(phy_t phy)
336 {
337         int rv;
338         struct phynode *phynode;
339
340         phynode = phy->phynode;
341         KASSERT(phynode->ref_cnt > 0,
342            ("Attempt to access unreferenced phy.\n"));
343         KASSERT(phy->enable_cnt > 0,
344            ("Attempt to disable already disabled phy.\n"));
345
346         PHY_TOPO_SLOCK();
347         rv = phynode_disable(phynode);
348         if (rv == 0)
349                 phy->enable_cnt--;
350         PHY_TOPO_UNLOCK();
351         return (rv);
352 }
353
354 int
355 phy_status(phy_t phy, int *status)
356 {
357         int rv;
358         struct phynode *phynode;
359
360         phynode = phy->phynode;
361         KASSERT(phynode->ref_cnt > 0,
362            ("Attempt to access unreferenced phy.\n"));
363
364         PHY_TOPO_SLOCK();
365         rv = phynode_status(phynode, status);
366         PHY_TOPO_UNLOCK();
367         return (rv);
368 }
369
370 int
371 phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id,
372     phy_t *phy)
373 {
374         struct phynode *phynode;
375
376         PHY_TOPO_SLOCK();
377
378         phynode = phynode_find_by_id(provider_dev, id);
379         if (phynode == NULL) {
380                 PHY_TOPO_UNLOCK();
381                 return (ENODEV);
382         }
383         *phy = phy_create(phynode, consumer_dev);
384         PHY_TOPO_UNLOCK();
385
386         return (0);
387 }
388
389 void
390 phy_release(phy_t phy)
391 {
392         struct phynode *phynode;
393
394         phynode = phy->phynode;
395         KASSERT(phynode->ref_cnt > 0,
396            ("Attempt to access unreferenced phy.\n"));
397
398         PHY_TOPO_SLOCK();
399         while (phy->enable_cnt > 0) {
400                 phynode_disable(phynode);
401                 phy->enable_cnt--;
402         }
403         PHYNODE_XLOCK(phynode);
404         TAILQ_REMOVE(&phynode->consumers_list, phy, link);
405         phynode->ref_cnt--;
406         PHYNODE_UNLOCK(phynode);
407         PHY_TOPO_UNLOCK();
408
409         free(phy, M_PHY);
410 }
411
412 #ifdef FDT
413 int phydev_default_ofw_map(device_t provider, phandle_t xref, int ncells,
414     pcell_t *cells, intptr_t *id)
415 {
416         struct phynode *entry;
417         phandle_t node;
418
419         /* Single device can register multiple subnodes. */
420         if (ncells == 0) {
421
422                 node = OF_node_from_xref(xref);
423                 PHY_TOPO_XLOCK();
424                 TAILQ_FOREACH(entry, &phynode_list, phylist_link) {
425                         if ((entry->pdev == provider) &&
426                             (entry->ofw_node == node)) {
427                                 *id = entry->id;
428                                 PHY_TOPO_UNLOCK();
429                                 return (0);
430                         }
431                 }
432                 PHY_TOPO_UNLOCK();
433                 return (ERANGE);
434         }
435
436         /* First cell is ID. */
437         if (ncells == 1) {
438                 *id = cells[0];
439                 return (0);
440         }
441
442         /* No default way how to get ID, custom mapper is required. */
443         return  (ERANGE);
444 }
445
446 int
447 phy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy)
448 {
449         phandle_t xnode;
450         pcell_t *cells;
451         device_t phydev;
452         int ncells, rv;
453         intptr_t id;
454
455         if (cnode <= 0)
456                 cnode = ofw_bus_get_node(consumer_dev);
457         if (cnode <= 0) {
458                 device_printf(consumer_dev,
459                     "%s called on not ofw based device\n", __func__);
460                 return (ENXIO);
461         }
462         rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx,
463             &xnode, &ncells, &cells);
464         if (rv != 0)
465                 return (rv);
466
467         /* Tranlate provider to device. */
468         phydev = OF_device_from_xref(xnode);
469         if (phydev == NULL) {
470                 OF_prop_free(cells);
471                 return (ENODEV);
472         }
473         /* Map phy to number. */
474         rv = PHYDEV_MAP(phydev, xnode, ncells, cells, &id);
475         OF_prop_free(cells);
476         if (rv != 0)
477                 return (rv);
478
479         return (phy_get_by_id(consumer_dev, phydev, id, phy));
480 }
481
482 int
483 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name,
484     phy_t *phy)
485 {
486         int rv, idx;
487
488         if (cnode <= 0)
489                 cnode = ofw_bus_get_node(consumer_dev);
490         if (cnode <= 0) {
491                 device_printf(consumer_dev,
492                     "%s called on not ofw based device\n",  __func__);
493                 return (ENXIO);
494         }
495         rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx);
496         if (rv != 0)
497                 return (rv);
498         return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy));
499 }
500
501 int
502 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name,
503     phy_t *phy)
504 {
505         pcell_t *cells;
506         device_t phydev;
507         int ncells, rv;
508         intptr_t id;
509
510         if (cnode <= 0)
511                 cnode = ofw_bus_get_node(consumer_dev);
512         if (cnode <= 0) {
513                 device_printf(consumer_dev,
514                     "%s called on not ofw based device\n", __func__);
515                 return (ENXIO);
516         }
517         ncells = OF_getencprop_alloc_multi(cnode, name, sizeof(pcell_t),
518             (void **)&cells);
519         if (ncells < 1)
520                 return (ENOENT);
521
522         /* Tranlate provider to device. */
523         phydev = OF_device_from_xref(cells[0]);
524         if (phydev == NULL) {
525                 OF_prop_free(cells);
526                 return (ENODEV);
527         }
528         /* Map phy to number. */
529         rv = PHYDEV_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id);
530         OF_prop_free(cells);
531         if (rv != 0)
532                 return (rv);
533
534         return (phy_get_by_id(consumer_dev, phydev, id, phy));
535 }
536 #endif