phy.c revision 308324
1296907Smmel/*-
2296907Smmel * Copyright 2016 Michal Meloun <mmel@FreeBSD.org>
3296907Smmel * All rights reserved.
4296907Smmel *
5296907Smmel * Redistribution and use in source and binary forms, with or without
6296907Smmel * modification, are permitted provided that the following conditions
7296907Smmel * are met:
8296907Smmel * 1. Redistributions of source code must retain the above copyright
9296907Smmel *    notice, this list of conditions and the following disclaimer.
10296907Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296907Smmel *    notice, this list of conditions and the following disclaimer in the
12296907Smmel *    documentation and/or other materials provided with the distribution.
13296907Smmel *
14296907Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296907Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296907Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296907Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296907Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296907Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296907Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296907Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296907Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296907Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296907Smmel * SUCH DAMAGE.
25296907Smmel *
26296907Smmel * $FreeBSD: stable/11/sys/dev/extres/phy/phy.c 308324 2016-11-05 04:17:32Z mmel $
27296907Smmel */
28296907Smmel#include "opt_platform.h"
29296907Smmel#include <sys/cdefs.h>
30296907Smmel#include <sys/param.h>
31296907Smmel#include <sys/kernel.h>
32296907Smmel#include <sys/malloc.h>
33296907Smmel#include <sys/systm.h>
34296907Smmel
35296907Smmel#ifdef FDT
36296907Smmel#include <dev/ofw/ofw_bus.h>
37296907Smmel#include <dev/ofw/ofw_bus_subr.h>
38296907Smmel#endif
39296907Smmel
40296907Smmel#include  <dev/extres/phy/phy.h>
41296907Smmel
42296907Smmel#include "phy_if.h"
43296907Smmel
44296907Smmelstruct phy {
45296907Smmel	device_t	consumer_dev;	/* consumer device*/
46296907Smmel	device_t	provider_dev;	/* provider device*/
47296907Smmel	uintptr_t	phy_id;		/* phy id */
48296907Smmel};
49296907Smmel
50296907SmmelMALLOC_DEFINE(M_PHY, "phy", "Phy framework");
51296907Smmel
52296907Smmelint
53296907Smmelphy_init(device_t consumer, phy_t phy)
54296907Smmel{
55296907Smmel
56296907Smmel	return (PHY_INIT(phy->provider_dev, phy->phy_id, true));
57296907Smmel}
58296907Smmel
59296907Smmelint
60296907Smmelphy_deinit(device_t consumer, phy_t phy)
61296907Smmel{
62296907Smmel
63296907Smmel	return (PHY_INIT(phy->provider_dev, phy->phy_id, false));
64296907Smmel}
65296907Smmel
66296907Smmel
67296907Smmelint
68296907Smmelphy_enable(device_t consumer, phy_t phy)
69296907Smmel{
70296907Smmel
71296907Smmel	return (PHY_ENABLE(phy->provider_dev, phy->phy_id, true));
72296907Smmel}
73296907Smmel
74296907Smmelint
75296907Smmelphy_disable(device_t consumer, phy_t phy)
76296907Smmel{
77296907Smmel
78296907Smmel	return (PHY_ENABLE(phy->provider_dev, phy->phy_id, false));
79296907Smmel}
80296907Smmel
81296907Smmelint
82296907Smmelphy_status(device_t consumer, phy_t phy, int *value)
83296907Smmel{
84296907Smmel
85296907Smmel	return (PHY_STATUS(phy->provider_dev, phy->phy_id, value));
86296907Smmel}
87296907Smmel
88296907Smmelint
89296907Smmelphy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id,
90296907Smmel    phy_t *phy_out)
91296907Smmel{
92296907Smmel	phy_t phy;
93296907Smmel
94296907Smmel	/* Create handle */
95296907Smmel	phy = malloc(sizeof(struct phy), M_PHY,
96296907Smmel	    M_WAITOK | M_ZERO);
97296907Smmel	phy->consumer_dev = consumer_dev;
98296907Smmel	phy->provider_dev = provider_dev;
99296907Smmel	phy->phy_id = id;
100296907Smmel	*phy_out = phy;
101296907Smmel	return (0);
102296907Smmel}
103296907Smmel
104296907Smmelvoid
105296907Smmelphy_release(phy_t phy)
106296907Smmel{
107296907Smmel	free(phy, M_PHY);
108296907Smmel}
109296907Smmel
110296907Smmel
111296907Smmel#ifdef FDT
112296907Smmelint phy_default_map(device_t provider, phandle_t xref, int ncells,
113296907Smmel    pcell_t *cells, intptr_t *id)
114296907Smmel{
115296907Smmel
116296907Smmel	if (ncells == 0)
117296907Smmel		*id = 1;
118296907Smmel	else if (ncells == 1)
119296907Smmel		*id = cells[0];
120296907Smmel	else
121296907Smmel		return  (ERANGE);
122296907Smmel
123296907Smmel	return (0);
124296907Smmel}
125296907Smmel
126296907Smmelint
127308324Smmelphy_get_by_ofw_idx(device_t consumer_dev, phandle_t cnode, int idx, phy_t *phy)
128296907Smmel{
129308324Smmel	phandle_t xnode;
130296907Smmel	pcell_t *cells;
131296907Smmel	device_t phydev;
132296907Smmel	int ncells, rv;
133296907Smmel	intptr_t id;
134296907Smmel
135308324Smmel	if (cnode <= 0)
136308324Smmel		cnode = ofw_bus_get_node(consumer_dev);
137296907Smmel	if (cnode <= 0) {
138296907Smmel		device_printf(consumer_dev,
139296907Smmel		    "%s called on not ofw based device\n", __func__);
140296907Smmel		return (ENXIO);
141296907Smmel	}
142296907Smmel	rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx,
143296907Smmel	    &xnode, &ncells, &cells);
144296907Smmel	if (rv != 0)
145296907Smmel		return (rv);
146296907Smmel
147296907Smmel	/* Tranlate provider to device. */
148296907Smmel	phydev = OF_device_from_xref(xnode);
149296907Smmel	if (phydev == NULL) {
150299714Sgonzo		OF_prop_free(cells);
151296907Smmel		return (ENODEV);
152296907Smmel	}
153296907Smmel	/* Map phy to number. */
154296907Smmel	rv = PHY_MAP(phydev, xnode, ncells, cells, &id);
155299714Sgonzo	OF_prop_free(cells);
156296907Smmel	if (rv != 0)
157296907Smmel		return (rv);
158296907Smmel
159296907Smmel	return (phy_get_by_id(consumer_dev, phydev, id, phy));
160296907Smmel}
161296907Smmel
162296907Smmelint
163308324Smmelphy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name,
164308324Smmel    phy_t *phy)
165296907Smmel{
166296907Smmel	int rv, idx;
167296907Smmel
168308324Smmel	if (cnode <= 0)
169308324Smmel		cnode = ofw_bus_get_node(consumer_dev);
170296907Smmel	if (cnode <= 0) {
171296907Smmel		device_printf(consumer_dev,
172296907Smmel		    "%s called on not ofw based device\n",  __func__);
173296907Smmel		return (ENXIO);
174296907Smmel	}
175296907Smmel	rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx);
176296907Smmel	if (rv != 0)
177296907Smmel		return (rv);
178308324Smmel	return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy));
179296907Smmel}
180296907Smmel
181296907Smmelint
182308324Smmelphy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name,
183308324Smmel    phy_t *phy)
184296907Smmel{
185296907Smmel	pcell_t *cells;
186296907Smmel	device_t phydev;
187296907Smmel	int ncells, rv;
188296907Smmel	intptr_t id;
189296907Smmel
190308324Smmel	if (cnode <= 0)
191308324Smmel		cnode = ofw_bus_get_node(consumer_dev);
192296907Smmel	if (cnode <= 0) {
193296907Smmel		device_printf(consumer_dev,
194296907Smmel		    "%s called on not ofw based device\n", __func__);
195296907Smmel		return (ENXIO);
196296907Smmel	}
197296907Smmel	ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t),
198296907Smmel	    (void **)&cells);
199296907Smmel	if (ncells < 1)
200296907Smmel		return (ENXIO);
201296907Smmel
202296907Smmel	/* Tranlate provider to device. */
203296907Smmel	phydev = OF_device_from_xref(cells[0]);
204296907Smmel	if (phydev == NULL) {
205299714Sgonzo		OF_prop_free(cells);
206296907Smmel		return (ENODEV);
207296907Smmel	}
208296907Smmel	/* Map phy to number. */
209296907Smmel	rv = PHY_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id);
210299714Sgonzo	OF_prop_free(cells);
211296907Smmel	if (rv != 0)
212296907Smmel		return (rv);
213296907Smmel
214296907Smmel	return (phy_get_by_id(consumer_dev, phydev, id, phy));
215296907Smmel}
216296907Smmel
217296907Smmelvoid
218296907Smmelphy_register_provider(device_t provider_dev)
219296907Smmel{
220296907Smmel	phandle_t xref, node;
221296907Smmel
222296907Smmel	node = ofw_bus_get_node(provider_dev);
223296907Smmel	if (node <= 0)
224296907Smmel		panic("%s called on not ofw based device.\n", __func__);
225296907Smmel
226296907Smmel	xref = OF_xref_from_node(node);
227296907Smmel	OF_device_register_xref(xref, provider_dev);
228296907Smmel}
229296907Smmel
230296907Smmelvoid
231296907Smmelphy_unregister_provider(device_t provider_dev)
232296907Smmel{
233296907Smmel	phandle_t xref;
234296907Smmel
235296907Smmel	xref = OF_xref_from_device(provider_dev);
236296907Smmel	OF_device_register_xref(xref, NULL);
237296907Smmel}
238296907Smmel#endif
239