xref: /openbsd-src/sys/dev/ofw/ofw_misc.c (revision 949f008f01ce4510c6cf4a111524f59a568b1d46)
1*949f008fSpatrick /*	$OpenBSD: ofw_misc.c,v 1.43 2023/05/17 23:25:45 patrick Exp $	*/
286ab2308Skettenis /*
3aa1f5e88Skettenis  * Copyright (c) 2017-2021 Mark Kettenis
486ab2308Skettenis  *
586ab2308Skettenis  * Permission to use, copy, modify, and distribute this software for any
686ab2308Skettenis  * purpose with or without fee is hereby granted, provided that the above
786ab2308Skettenis  * copyright notice and this permission notice appear in all copies.
886ab2308Skettenis  *
986ab2308Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1086ab2308Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1186ab2308Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1286ab2308Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1386ab2308Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1486ab2308Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1586ab2308Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1686ab2308Skettenis  */
1786ab2308Skettenis 
1886ab2308Skettenis #include <sys/types.h>
19e9d39bafSkettenis #include <sys/device.h>
2086ab2308Skettenis #include <sys/malloc.h>
21e9d39bafSkettenis #include <sys/systm.h>
22e9d39bafSkettenis 
23e9d39bafSkettenis #include <net/if.h>
24e9d39bafSkettenis #include <net/if_media.h>
2586ab2308Skettenis 
2686ab2308Skettenis #include <machine/bus.h>
2786ab2308Skettenis 
28e9d39bafSkettenis #include <dev/mii/mii.h>
29e9d39bafSkettenis #include <dev/mii/miivar.h>
3086ab2308Skettenis #include <dev/ofw/openfirm.h>
314c45b354Skettenis #include <dev/ofw/ofw_gpio.h>
32c12728f2Skettenis #include <dev/ofw/ofw_misc.h>
334c45b354Skettenis #include <dev/ofw/ofw_regulator.h>
3486ab2308Skettenis 
351c2f50caSkettenis /*
361c2f50caSkettenis  * Register maps.
371c2f50caSkettenis  */
381c2f50caSkettenis 
3986ab2308Skettenis struct regmap {
40b8cbdd99Skettenis 	int			rm_node;
4186ab2308Skettenis 	uint32_t		rm_phandle;
4286ab2308Skettenis 	bus_space_tag_t		rm_tag;
4386ab2308Skettenis 	bus_space_handle_t	rm_handle;
4486ab2308Skettenis 	bus_size_t		rm_size;
4586ab2308Skettenis 
4686ab2308Skettenis 	LIST_ENTRY(regmap)	rm_list;
4786ab2308Skettenis };
4886ab2308Skettenis 
4986ab2308Skettenis LIST_HEAD(, regmap) regmaps = LIST_HEAD_INITIALIZER(regmap);
5086ab2308Skettenis 
5186ab2308Skettenis void
regmap_register(int node,bus_space_tag_t tag,bus_space_handle_t handle,bus_size_t size)5286ab2308Skettenis regmap_register(int node, bus_space_tag_t tag, bus_space_handle_t handle,
5386ab2308Skettenis     bus_size_t size)
5486ab2308Skettenis {
5586ab2308Skettenis 	struct regmap *rm;
5686ab2308Skettenis 
5786ab2308Skettenis 	rm = malloc(sizeof(struct regmap), M_DEVBUF, M_WAITOK);
58b8cbdd99Skettenis 	rm->rm_node = node;
5917b07209Skettenis 	rm->rm_phandle = OF_getpropint(node, "phandle", 0);
6086ab2308Skettenis 	rm->rm_tag = tag;
6186ab2308Skettenis 	rm->rm_handle = handle;
6286ab2308Skettenis 	rm->rm_size = size;
6386ab2308Skettenis 	LIST_INSERT_HEAD(&regmaps, rm, rm_list);
6486ab2308Skettenis }
6586ab2308Skettenis 
6686ab2308Skettenis struct regmap *
regmap_bycompatible(char * compatible)67a31afb08Spatrick regmap_bycompatible(char *compatible)
68a31afb08Spatrick {
69a31afb08Spatrick 	struct regmap *rm;
70a31afb08Spatrick 
71a31afb08Spatrick 	LIST_FOREACH(rm, &regmaps, rm_list) {
72a31afb08Spatrick 		if (OF_is_compatible(rm->rm_node, compatible))
73a31afb08Spatrick 			return rm;
74a31afb08Spatrick 	}
75a31afb08Spatrick 
76a31afb08Spatrick 	return NULL;
77a31afb08Spatrick }
78a31afb08Spatrick 
79a31afb08Spatrick struct regmap *
regmap_bynode(int node)80b8cbdd99Skettenis regmap_bynode(int node)
81b8cbdd99Skettenis {
82b8cbdd99Skettenis 	struct regmap *rm;
83b8cbdd99Skettenis 
84b8cbdd99Skettenis 	LIST_FOREACH(rm, &regmaps, rm_list) {
85b8cbdd99Skettenis 		if (rm->rm_node == node)
86b8cbdd99Skettenis 			return rm;
87b8cbdd99Skettenis 	}
88b8cbdd99Skettenis 
89b8cbdd99Skettenis 	return NULL;
90b8cbdd99Skettenis }
91b8cbdd99Skettenis 
92b8cbdd99Skettenis struct regmap *
regmap_byphandle(uint32_t phandle)9386ab2308Skettenis regmap_byphandle(uint32_t phandle)
9486ab2308Skettenis {
9586ab2308Skettenis 	struct regmap *rm;
9686ab2308Skettenis 
97a7702439Spatrick 	if (phandle == 0)
98a7702439Spatrick 		return NULL;
99a7702439Spatrick 
10086ab2308Skettenis 	LIST_FOREACH(rm, &regmaps, rm_list) {
10186ab2308Skettenis 		if (rm->rm_phandle == phandle)
10286ab2308Skettenis 			return rm;
10386ab2308Skettenis 	}
10486ab2308Skettenis 
10586ab2308Skettenis 	return NULL;
10686ab2308Skettenis }
10786ab2308Skettenis 
10886ab2308Skettenis void
regmap_write_4(struct regmap * rm,bus_size_t offset,uint32_t value)10986ab2308Skettenis regmap_write_4(struct regmap *rm, bus_size_t offset, uint32_t value)
11086ab2308Skettenis {
11186ab2308Skettenis 	KASSERT(offset <= rm->rm_size - sizeof(uint32_t));
11286ab2308Skettenis 	bus_space_write_4(rm->rm_tag, rm->rm_handle, offset, value);
11386ab2308Skettenis }
11486ab2308Skettenis 
11586ab2308Skettenis uint32_t
regmap_read_4(struct regmap * rm,bus_size_t offset)11686ab2308Skettenis regmap_read_4(struct regmap *rm, bus_size_t offset)
11786ab2308Skettenis {
11886ab2308Skettenis 	KASSERT(offset <= rm->rm_size - sizeof(uint32_t));
11986ab2308Skettenis 	return bus_space_read_4(rm->rm_tag, rm->rm_handle, offset);
12086ab2308Skettenis }
1212ec99202Skettenis 
1228f2101caSdlg /*
1238f2101caSdlg  * Network interface support.
1248f2101caSdlg  */
1258f2101caSdlg 
1268f2101caSdlg LIST_HEAD(, if_device) if_devices =
1278f2101caSdlg 	LIST_HEAD_INITIALIZER(if_devices);
1288f2101caSdlg 
1298f2101caSdlg void
if_register(struct if_device * ifd)1308f2101caSdlg if_register(struct if_device *ifd)
1318f2101caSdlg {
1328f2101caSdlg 	ifd->if_phandle = OF_getpropint(ifd->if_node, "phandle", 0);
1338f2101caSdlg 
1348f2101caSdlg 	LIST_INSERT_HEAD(&if_devices, ifd, if_list);
1358f2101caSdlg }
1368f2101caSdlg 
1378f2101caSdlg struct ifnet *
if_bynode(int node)1388f2101caSdlg if_bynode(int node)
1398f2101caSdlg {
1408f2101caSdlg 	struct if_device *ifd;
1418f2101caSdlg 
1428f2101caSdlg 	LIST_FOREACH(ifd, &if_devices, if_list) {
1438f2101caSdlg 		if (ifd->if_node == node)
1448f2101caSdlg 			return (ifd->if_ifp);
1458f2101caSdlg 	}
1468f2101caSdlg 
1478f2101caSdlg 	return (NULL);
1488f2101caSdlg }
1498f2101caSdlg 
1508f2101caSdlg struct ifnet *
if_byphandle(uint32_t phandle)1518f2101caSdlg if_byphandle(uint32_t phandle)
1528f2101caSdlg {
1538f2101caSdlg 	struct if_device *ifd;
1548f2101caSdlg 
155f56e72c6Sdlg 	if (phandle == 0)
156f56e72c6Sdlg 		return (NULL);
157f56e72c6Sdlg 
1588f2101caSdlg 	LIST_FOREACH(ifd, &if_devices, if_list) {
1598f2101caSdlg 		if (ifd->if_phandle == phandle)
1608f2101caSdlg 			return (ifd->if_ifp);
1618f2101caSdlg 	}
1628f2101caSdlg 
1638f2101caSdlg 	return (NULL);
1648f2101caSdlg }
1652ec99202Skettenis 
1662ec99202Skettenis /*
1672ec99202Skettenis  * PHY support.
1682ec99202Skettenis  */
1692ec99202Skettenis 
1702ec99202Skettenis LIST_HEAD(, phy_device) phy_devices =
1712ec99202Skettenis 	LIST_HEAD_INITIALIZER(phy_devices);
1722ec99202Skettenis 
1732ec99202Skettenis void
phy_register(struct phy_device * pd)1742ec99202Skettenis phy_register(struct phy_device *pd)
1752ec99202Skettenis {
1762ec99202Skettenis 	pd->pd_cells = OF_getpropint(pd->pd_node, "#phy-cells", 0);
1772ec99202Skettenis 	pd->pd_phandle = OF_getpropint(pd->pd_node, "phandle", 0);
1782ec99202Skettenis 	if (pd->pd_phandle == 0)
1792ec99202Skettenis 		return;
1802ec99202Skettenis 
1812ec99202Skettenis 	LIST_INSERT_HEAD(&phy_devices, pd, pd_list);
1822ec99202Skettenis }
1832ec99202Skettenis 
1842ec99202Skettenis int
phy_usb_nop_enable(int node)1854c45b354Skettenis phy_usb_nop_enable(int node)
1864c45b354Skettenis {
1874c45b354Skettenis 	uint32_t vcc_supply;
1884c45b354Skettenis 	uint32_t *gpio;
1894c45b354Skettenis 	int len;
1904c45b354Skettenis 
1914c45b354Skettenis 	vcc_supply = OF_getpropint(node, "vcc-supply", 0);
1924c45b354Skettenis 	if (vcc_supply)
1934c45b354Skettenis 		regulator_enable(vcc_supply);
1944c45b354Skettenis 
1954c45b354Skettenis 	len = OF_getproplen(node, "reset-gpios");
1964c45b354Skettenis 	if (len <= 0)
1974c45b354Skettenis 		return 0;
1984c45b354Skettenis 
1994c45b354Skettenis 	/* There should only be a single GPIO pin. */
2004c45b354Skettenis 	gpio = malloc(len, M_TEMP, M_WAITOK);
2014c45b354Skettenis 	OF_getpropintarray(node, "reset-gpios", gpio, len);
2024c45b354Skettenis 
2034c45b354Skettenis 	gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
2044c45b354Skettenis 	gpio_controller_set_pin(gpio, 1);
2054c45b354Skettenis 	delay(10000);
2064c45b354Skettenis 	gpio_controller_set_pin(gpio, 0);
2074c45b354Skettenis 
2084c45b354Skettenis 	free(gpio, M_TEMP, len);
2094c45b354Skettenis 
2104c45b354Skettenis 	return 0;
2114c45b354Skettenis }
2124c45b354Skettenis 
2134c45b354Skettenis int
phy_enable_cells(uint32_t * cells)2142ec99202Skettenis phy_enable_cells(uint32_t *cells)
2152ec99202Skettenis {
2162ec99202Skettenis 	struct phy_device *pd;
2172ec99202Skettenis 	uint32_t phandle = cells[0];
2184c45b354Skettenis 	int node;
2192ec99202Skettenis 
2202ec99202Skettenis 	LIST_FOREACH(pd, &phy_devices, pd_list) {
2212ec99202Skettenis 		if (pd->pd_phandle == phandle)
2222ec99202Skettenis 			break;
2232ec99202Skettenis 	}
2242ec99202Skettenis 
2252ec99202Skettenis 	if (pd && pd->pd_enable)
2262ec99202Skettenis 		return pd->pd_enable(pd->pd_cookie, &cells[1]);
2272ec99202Skettenis 
2284c45b354Skettenis 	node = OF_getnodebyphandle(phandle);
2294c45b354Skettenis 	if (node == 0)
2304c45b354Skettenis 		return ENXIO;
2314c45b354Skettenis 
2324c45b354Skettenis 	if (OF_is_compatible(node, "usb-nop-xceiv"))
2334c45b354Skettenis 		return phy_usb_nop_enable(node);
2344c45b354Skettenis 
2354c45b354Skettenis 	return ENXIO;
2362ec99202Skettenis }
2372ec99202Skettenis 
2382ec99202Skettenis uint32_t *
phy_next_phy(uint32_t * cells)2392ec99202Skettenis phy_next_phy(uint32_t *cells)
2402ec99202Skettenis {
2412ec99202Skettenis 	uint32_t phandle = cells[0];
2422ec99202Skettenis 	int node, ncells;
2432ec99202Skettenis 
2442ec99202Skettenis 	node = OF_getnodebyphandle(phandle);
2452ec99202Skettenis 	if (node == 0)
2462ec99202Skettenis 		return NULL;
2472ec99202Skettenis 
2482ec99202Skettenis 	ncells = OF_getpropint(node, "#phy-cells", 0);
2492ec99202Skettenis 	return cells + ncells + 1;
2502ec99202Skettenis }
2512ec99202Skettenis 
2522ec99202Skettenis int
phy_enable_prop_idx(int node,char * prop,int idx)2533ce5e03eSdlg phy_enable_prop_idx(int node, char *prop, int idx)
2542ec99202Skettenis {
2552ec99202Skettenis 	uint32_t *phys;
2562ec99202Skettenis 	uint32_t *phy;
2572ec99202Skettenis 	int rv = -1;
2582ec99202Skettenis 	int len;
2592ec99202Skettenis 
260074353b3Spatrick 	len = OF_getproplen(node, prop);
2612ec99202Skettenis 	if (len <= 0)
2622ec99202Skettenis 		return -1;
2632ec99202Skettenis 
2642ec99202Skettenis 	phys = malloc(len, M_TEMP, M_WAITOK);
2653ce5e03eSdlg 	OF_getpropintarray(node, prop, phys, len);
2662ec99202Skettenis 
2672ec99202Skettenis 	phy = phys;
2682ec99202Skettenis 	while (phy && phy < phys + (len / sizeof(uint32_t))) {
2692ec99202Skettenis 		if (idx <= 0)
2702ec99202Skettenis 			rv = phy_enable_cells(phy);
2712ec99202Skettenis 		if (idx == 0)
2722ec99202Skettenis 			break;
2732ec99202Skettenis 		phy = phy_next_phy(phy);
2742ec99202Skettenis 		idx--;
2752ec99202Skettenis 	}
2762ec99202Skettenis 
2772ec99202Skettenis 	free(phys, M_TEMP, len);
2782ec99202Skettenis 	return rv;
2792ec99202Skettenis }
2802ec99202Skettenis 
2812ec99202Skettenis int
phy_enable_idx(int node,int idx)2823ce5e03eSdlg phy_enable_idx(int node, int idx)
2833ce5e03eSdlg {
2843ce5e03eSdlg 	return (phy_enable_prop_idx(node, "phys", idx));
2853ce5e03eSdlg }
2863ce5e03eSdlg 
2873ce5e03eSdlg int
phy_enable(int node,const char * name)2882ec99202Skettenis phy_enable(int node, const char *name)
2892ec99202Skettenis {
2902ec99202Skettenis 	int idx;
2912ec99202Skettenis 
2922ec99202Skettenis 	idx = OF_getindex(node, name, "phy-names");
2932ec99202Skettenis 	if (idx == -1)
2942ec99202Skettenis 		return -1;
2952ec99202Skettenis 
2962ec99202Skettenis 	return phy_enable_idx(node, idx);
2972ec99202Skettenis }
2984216024aSpatrick 
2994216024aSpatrick /*
3004216024aSpatrick  * I2C support.
3014216024aSpatrick  */
3024216024aSpatrick 
3034216024aSpatrick LIST_HEAD(, i2c_bus) i2c_busses =
3044216024aSpatrick 	LIST_HEAD_INITIALIZER(i2c_bus);
3054216024aSpatrick 
3064216024aSpatrick void
i2c_register(struct i2c_bus * ib)3074216024aSpatrick i2c_register(struct i2c_bus *ib)
3084216024aSpatrick {
3094216024aSpatrick 	ib->ib_phandle = OF_getpropint(ib->ib_node, "phandle", 0);
3104216024aSpatrick 	if (ib->ib_phandle == 0)
3114216024aSpatrick 		return;
3124216024aSpatrick 
3134216024aSpatrick 	LIST_INSERT_HEAD(&i2c_busses, ib, ib_list);
3144216024aSpatrick }
3154216024aSpatrick 
3164216024aSpatrick struct i2c_controller *
i2c_bynode(int node)3174216024aSpatrick i2c_bynode(int node)
3184216024aSpatrick {
3194216024aSpatrick 	struct i2c_bus *ib;
3204216024aSpatrick 
3214216024aSpatrick 	LIST_FOREACH(ib, &i2c_busses, ib_list) {
3224216024aSpatrick 		if (ib->ib_node == node)
3234216024aSpatrick 			return ib->ib_ic;
3244216024aSpatrick 	}
3254216024aSpatrick 
3264216024aSpatrick 	return NULL;
3274216024aSpatrick }
3284216024aSpatrick 
3294216024aSpatrick struct i2c_controller *
i2c_byphandle(uint32_t phandle)3304216024aSpatrick i2c_byphandle(uint32_t phandle)
3314216024aSpatrick {
3324216024aSpatrick 	struct i2c_bus *ib;
3334216024aSpatrick 
334a7702439Spatrick 	if (phandle == 0)
335a7702439Spatrick 		return NULL;
336a7702439Spatrick 
3374216024aSpatrick 	LIST_FOREACH(ib, &i2c_busses, ib_list) {
3384216024aSpatrick 		if (ib->ib_phandle == phandle)
3394216024aSpatrick 			return ib->ib_ic;
3404216024aSpatrick 	}
3414216024aSpatrick 
3424216024aSpatrick 	return NULL;
3434216024aSpatrick }
3445296b96aSpatrick 
3455296b96aSpatrick /*
3465296b96aSpatrick  * SFP support.
3475296b96aSpatrick  */
3485296b96aSpatrick 
3495296b96aSpatrick LIST_HEAD(, sfp_device) sfp_devices =
3505296b96aSpatrick 	LIST_HEAD_INITIALIZER(sfp_devices);
3515296b96aSpatrick 
3525296b96aSpatrick void
sfp_register(struct sfp_device * sd)3535296b96aSpatrick sfp_register(struct sfp_device *sd)
3545296b96aSpatrick {
3555296b96aSpatrick 	sd->sd_phandle = OF_getpropint(sd->sd_node, "phandle", 0);
3565296b96aSpatrick 	if (sd->sd_phandle == 0)
3575296b96aSpatrick 		return;
3585296b96aSpatrick 
3595296b96aSpatrick 	LIST_INSERT_HEAD(&sfp_devices, sd, sd_list);
3605296b96aSpatrick }
3615296b96aSpatrick 
3625296b96aSpatrick int
sfp_do_enable(uint32_t phandle,int enable)3639e963265Skettenis sfp_do_enable(uint32_t phandle, int enable)
3649e963265Skettenis {
3659e963265Skettenis 	struct sfp_device *sd;
3669e963265Skettenis 
3679e963265Skettenis 	if (phandle == 0)
3689e963265Skettenis 		return ENXIO;
3699e963265Skettenis 
3709e963265Skettenis 	LIST_FOREACH(sd, &sfp_devices, sd_list) {
3719e963265Skettenis 		if (sd->sd_phandle == phandle)
3729e963265Skettenis 			return sd->sd_enable(sd->sd_cookie, enable);
3739e963265Skettenis 	}
3749e963265Skettenis 
3759e963265Skettenis 	return ENXIO;
3769e963265Skettenis }
3779e963265Skettenis 
3789e963265Skettenis int
sfp_enable(uint32_t phandle)3799e963265Skettenis sfp_enable(uint32_t phandle)
3809e963265Skettenis {
3819e963265Skettenis 	return sfp_do_enable(phandle, 1);
3829e963265Skettenis }
3839e963265Skettenis 
3849e963265Skettenis int
sfp_disable(uint32_t phandle)3859e963265Skettenis sfp_disable(uint32_t phandle)
3869e963265Skettenis {
3879e963265Skettenis 	return sfp_do_enable(phandle, 0);
3889e963265Skettenis }
3899e963265Skettenis 
3909e963265Skettenis int
sfp_get_sffpage(uint32_t phandle,struct if_sffpage * sff)3915296b96aSpatrick sfp_get_sffpage(uint32_t phandle, struct if_sffpage *sff)
3925296b96aSpatrick {
3935296b96aSpatrick 	struct sfp_device *sd;
3945296b96aSpatrick 
395a7702439Spatrick 	if (phandle == 0)
396a7702439Spatrick 		return ENXIO;
397a7702439Spatrick 
3985296b96aSpatrick 	LIST_FOREACH(sd, &sfp_devices, sd_list) {
3995296b96aSpatrick 		if (sd->sd_phandle == phandle)
4005296b96aSpatrick 			return sd->sd_get_sffpage(sd->sd_cookie, sff);
4015296b96aSpatrick 	}
4025296b96aSpatrick 
4035296b96aSpatrick 	return ENXIO;
4045296b96aSpatrick }
4051eb4c955Skettenis 
406fff4b955Skettenis #define SFF8472_TCC_XCC			3 /* 10G Ethernet Compliance Codes */
407fff4b955Skettenis #define SFF8472_TCC_XCC_10G_SR		(1 << 4)
408fff4b955Skettenis #define SFF8472_TCC_XCC_10G_LR		(1 << 5)
409fff4b955Skettenis #define SFF8472_TCC_XCC_10G_LRM		(1 << 6)
410fff4b955Skettenis #define SFF8472_TCC_XCC_10G_ER		(1 << 7)
411e9d39bafSkettenis #define SFF8472_TCC_ECC			6 /* Ethernet Compliance Codes */
412e9d39bafSkettenis #define SFF8472_TCC_ECC_1000_SX		(1 << 0)
413e9d39bafSkettenis #define SFF8472_TCC_ECC_1000_LX		(1 << 1)
414e9d39bafSkettenis #define SFF8472_TCC_ECC_1000_CX		(1 << 2)
41523de15b6Skettenis #define SFF8472_TCC_ECC_1000_T		(1 << 3)
416fff4b955Skettenis #define SFF8472_TCC_SCT			8 /* SFP+ Cable Technology */
417fff4b955Skettenis #define SFF8472_TCC_SCT_PASSIVE		(1 << 2)
418fff4b955Skettenis #define SFF8472_TCC_SCT_ACTIVE		(1 << 3)
419e9d39bafSkettenis 
420e9d39bafSkettenis int
sfp_add_media(uint32_t phandle,struct mii_data * mii)421e9d39bafSkettenis sfp_add_media(uint32_t phandle, struct mii_data *mii)
422e9d39bafSkettenis {
423e9d39bafSkettenis 	struct if_sffpage sff;
424e9d39bafSkettenis 	int error;
425e9d39bafSkettenis 
426e9d39bafSkettenis 	memset(&sff, 0, sizeof(sff));
427e9d39bafSkettenis 	sff.sff_addr = IFSFF_ADDR_EEPROM;
428e9d39bafSkettenis 	sff.sff_page = 0;
429e9d39bafSkettenis 
430e9d39bafSkettenis 	error = sfp_get_sffpage(phandle, &sff);
431e9d39bafSkettenis 	if (error)
432e9d39bafSkettenis 		return error;
433e9d39bafSkettenis 
434fff4b955Skettenis 	/* SFP */
43523de15b6Skettenis 	if (sff.sff_data[SFF8472_TCC_ECC] & SFF8472_TCC_ECC_1000_SX) {
436e9d39bafSkettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_1000_SX, 0, NULL);
43723de15b6Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_1000_SX | IFM_FDX;
43823de15b6Skettenis 	}
43923de15b6Skettenis 	if (sff.sff_data[SFF8472_TCC_ECC] & SFF8472_TCC_ECC_1000_LX) {
440e9d39bafSkettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_1000_LX, 0, NULL);
44123de15b6Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_1000_LX | IFM_FDX;
44223de15b6Skettenis 	}
44323de15b6Skettenis 	if (sff.sff_data[SFF8472_TCC_ECC] & SFF8472_TCC_ECC_1000_CX) {
444e9d39bafSkettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_1000_CX, 0, NULL);
44523de15b6Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_1000_CX | IFM_FDX;
44623de15b6Skettenis 	}
44723de15b6Skettenis 	if (sff.sff_data[SFF8472_TCC_ECC] & SFF8472_TCC_ECC_1000_T) {
44823de15b6Skettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_1000_T, 0, NULL);
44923de15b6Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_1000_T | IFM_FDX;
45023de15b6Skettenis 	}
451e9d39bafSkettenis 
452fff4b955Skettenis 	/* SFP+ */
453fff4b955Skettenis 	if (sff.sff_data[SFF8472_TCC_XCC] & SFF8472_TCC_XCC_10G_SR) {
454fff4b955Skettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_10G_SR, 0, NULL);
455fff4b955Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_10G_SR | IFM_FDX;
456fff4b955Skettenis 	}
457fff4b955Skettenis 	if (sff.sff_data[SFF8472_TCC_XCC] & SFF8472_TCC_XCC_10G_LR) {
458fff4b955Skettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_10G_LR, 0, NULL);
459fff4b955Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_10G_LR | IFM_FDX;
460fff4b955Skettenis 	}
461fff4b955Skettenis 	if (sff.sff_data[SFF8472_TCC_XCC] & SFF8472_TCC_XCC_10G_LRM) {
462fff4b955Skettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_10G_LRM, 0, NULL);
463fff4b955Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_10G_LRM | IFM_FDX;
464fff4b955Skettenis 	}
465fff4b955Skettenis 	if (sff.sff_data[SFF8472_TCC_XCC] & SFF8472_TCC_XCC_10G_ER) {
466fff4b955Skettenis 		ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_10G_ER, 0, NULL);
467fff4b955Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_10G_ER | IFM_FDX;
468fff4b955Skettenis 	}
469fff4b955Skettenis 
470fff4b955Skettenis 	/* SFP+ DAC */
471fff4b955Skettenis 	if (sff.sff_data[SFF8472_TCC_SCT] & SFF8472_TCC_SCT_PASSIVE ||
472fff4b955Skettenis 	    sff.sff_data[SFF8472_TCC_SCT] & SFF8472_TCC_SCT_ACTIVE) {
473fff4b955Skettenis 		ifmedia_add(&mii->mii_media,
474fff4b955Skettenis 		    IFM_ETHER | IFM_10G_SFP_CU, 0, NULL);
475fff4b955Skettenis 		mii->mii_media_active = IFM_ETHER | IFM_10G_SFP_CU | IFM_FDX;
476fff4b955Skettenis 	}
477fff4b955Skettenis 
478e9d39bafSkettenis 	return 0;
479e9d39bafSkettenis }
480e9d39bafSkettenis 
4811eb4c955Skettenis /*
4821eb4c955Skettenis  * PWM support.
4831eb4c955Skettenis  */
4841eb4c955Skettenis 
4851eb4c955Skettenis LIST_HEAD(, pwm_device) pwm_devices =
4861eb4c955Skettenis 	LIST_HEAD_INITIALIZER(pwm_devices);
4871eb4c955Skettenis 
4881eb4c955Skettenis void
pwm_register(struct pwm_device * pd)4891eb4c955Skettenis pwm_register(struct pwm_device *pd)
4901eb4c955Skettenis {
4911eb4c955Skettenis 	pd->pd_cells = OF_getpropint(pd->pd_node, "#pwm-cells", 0);
4921eb4c955Skettenis 	pd->pd_phandle = OF_getpropint(pd->pd_node, "phandle", 0);
4931eb4c955Skettenis 	if (pd->pd_phandle == 0)
4941eb4c955Skettenis 		return;
4951eb4c955Skettenis 
4961eb4c955Skettenis 	LIST_INSERT_HEAD(&pwm_devices, pd, pd_list);
4971eb4c955Skettenis 
4981eb4c955Skettenis }
4991eb4c955Skettenis 
5001eb4c955Skettenis int
pwm_init_state(uint32_t * cells,struct pwm_state * ps)5011eb4c955Skettenis pwm_init_state(uint32_t *cells, struct pwm_state *ps)
5021eb4c955Skettenis {
5031eb4c955Skettenis 	struct pwm_device *pd;
5041eb4c955Skettenis 
5051eb4c955Skettenis 	LIST_FOREACH(pd, &pwm_devices, pd_list) {
5061eb4c955Skettenis 		if (pd->pd_phandle == cells[0]) {
5071eb4c955Skettenis 			memset(ps, 0, sizeof(struct pwm_state));
5081eb4c955Skettenis 			pd->pd_get_state(pd->pd_cookie, &cells[1], ps);
5091eb4c955Skettenis 			ps->ps_pulse_width = 0;
5109800a309Spatrick 			if (pd->pd_cells >= 2)
5111eb4c955Skettenis 				ps->ps_period = cells[2];
5129800a309Spatrick 			if (pd->pd_cells >= 3)
5131eb4c955Skettenis 				ps->ps_flags = cells[3];
5141eb4c955Skettenis 			return 0;
5151eb4c955Skettenis 		}
5161eb4c955Skettenis 	}
5171eb4c955Skettenis 
5181eb4c955Skettenis 	return ENXIO;
5191eb4c955Skettenis }
5201eb4c955Skettenis 
5211eb4c955Skettenis int
pwm_get_state(uint32_t * cells,struct pwm_state * ps)5221eb4c955Skettenis pwm_get_state(uint32_t *cells, struct pwm_state *ps)
5231eb4c955Skettenis {
5241eb4c955Skettenis 	struct pwm_device *pd;
5251eb4c955Skettenis 
5261eb4c955Skettenis 	LIST_FOREACH(pd, &pwm_devices, pd_list) {
5271eb4c955Skettenis 		if (pd->pd_phandle == cells[0])
5281eb4c955Skettenis 			return pd->pd_get_state(pd->pd_cookie, &cells[1], ps);
5291eb4c955Skettenis 	}
5301eb4c955Skettenis 
5311eb4c955Skettenis 	return ENXIO;
5321eb4c955Skettenis }
5331eb4c955Skettenis 
5341eb4c955Skettenis int
pwm_set_state(uint32_t * cells,struct pwm_state * ps)5351eb4c955Skettenis pwm_set_state(uint32_t *cells, struct pwm_state *ps)
5361eb4c955Skettenis {
5371eb4c955Skettenis 	struct pwm_device *pd;
5381eb4c955Skettenis 
5391eb4c955Skettenis 	LIST_FOREACH(pd, &pwm_devices, pd_list) {
5401eb4c955Skettenis 		if (pd->pd_phandle == cells[0])
5411eb4c955Skettenis 			return pd->pd_set_state(pd->pd_cookie, &cells[1], ps);
5421eb4c955Skettenis 	}
5431eb4c955Skettenis 
5441eb4c955Skettenis 	return ENXIO;
5451eb4c955Skettenis }
546047fbc8bSkettenis 
547047fbc8bSkettenis /*
548047fbc8bSkettenis  * Non-volatile memory support.
549047fbc8bSkettenis  */
550047fbc8bSkettenis 
551047fbc8bSkettenis LIST_HEAD(, nvmem_device) nvmem_devices =
552047fbc8bSkettenis 	LIST_HEAD_INITIALIZER(nvmem_devices);
553047fbc8bSkettenis 
5541bd1536fSkettenis struct nvmem_cell {
5551bd1536fSkettenis 	uint32_t	nc_phandle;
5561bd1536fSkettenis 	struct nvmem_device *nc_nd;
5571bd1536fSkettenis 	bus_addr_t	nc_addr;
5581bd1536fSkettenis 	bus_size_t	nc_size;
559eb0ca4d1Skettenis 	uint32_t	nc_offset;
560eb0ca4d1Skettenis 	uint32_t	nc_bitlen;
5611bd1536fSkettenis 
5621bd1536fSkettenis 	LIST_ENTRY(nvmem_cell) nc_list;
5631bd1536fSkettenis };
5641bd1536fSkettenis 
5651bd1536fSkettenis LIST_HEAD(, nvmem_cell) nvmem_cells =
5661bd1536fSkettenis 	LIST_HEAD_INITIALIZER(nvmem_cells);
5671bd1536fSkettenis 
5681bd1536fSkettenis void
nvmem_register_child(int node,struct nvmem_device * nd)5691bd1536fSkettenis nvmem_register_child(int node, struct nvmem_device *nd)
5701bd1536fSkettenis {
5711bd1536fSkettenis 	struct nvmem_cell *nc;
5721bd1536fSkettenis 	uint32_t phandle;
573eb0ca4d1Skettenis 	uint32_t reg[2], bits[2] = {};
5741bd1536fSkettenis 
5751bd1536fSkettenis 	phandle = OF_getpropint(node, "phandle", 0);
5761bd1536fSkettenis 	if (phandle == 0)
5771bd1536fSkettenis 		return;
5781bd1536fSkettenis 
5791bd1536fSkettenis 	if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg))
5801bd1536fSkettenis 		return;
5811bd1536fSkettenis 
582eb0ca4d1Skettenis 	OF_getpropintarray(node, "bits", bits, sizeof(bits));
583eb0ca4d1Skettenis 
5841bd1536fSkettenis 	nc = malloc(sizeof(struct nvmem_cell), M_DEVBUF, M_WAITOK);
5851bd1536fSkettenis 	nc->nc_phandle = phandle;
5861bd1536fSkettenis 	nc->nc_nd = nd;
5871bd1536fSkettenis 	nc->nc_addr = reg[0];
5881bd1536fSkettenis 	nc->nc_size = reg[1];
589eb0ca4d1Skettenis 	nc->nc_offset = bits[0];
590eb0ca4d1Skettenis 	nc->nc_bitlen = bits[1];
5911bd1536fSkettenis 	LIST_INSERT_HEAD(&nvmem_cells, nc, nc_list);
5921bd1536fSkettenis }
5931bd1536fSkettenis 
594047fbc8bSkettenis void
nvmem_register(struct nvmem_device * nd)595047fbc8bSkettenis nvmem_register(struct nvmem_device *nd)
596047fbc8bSkettenis {
5971bd1536fSkettenis 	int node;
598047fbc8bSkettenis 
5991bd1536fSkettenis 	nd->nd_phandle = OF_getpropint(nd->nd_node, "phandle", 0);
6001bd1536fSkettenis 	if (nd->nd_phandle)
601047fbc8bSkettenis 		LIST_INSERT_HEAD(&nvmem_devices, nd, nd_list);
6021bd1536fSkettenis 
6031bd1536fSkettenis 	for (node = OF_child(nd->nd_node); node; node = OF_peer(node))
6041bd1536fSkettenis 		nvmem_register_child(node, nd);
605047fbc8bSkettenis }
606047fbc8bSkettenis 
607047fbc8bSkettenis int
nvmem_read(uint32_t phandle,bus_addr_t addr,void * data,bus_size_t size)608047fbc8bSkettenis nvmem_read(uint32_t phandle, bus_addr_t addr, void *data, bus_size_t size)
609047fbc8bSkettenis {
610047fbc8bSkettenis 	struct nvmem_device *nd;
611047fbc8bSkettenis 
612a7702439Spatrick 	if (phandle == 0)
613a7702439Spatrick 		return ENXIO;
614a7702439Spatrick 
615047fbc8bSkettenis 	LIST_FOREACH(nd, &nvmem_devices, nd_list) {
616047fbc8bSkettenis 		if (nd->nd_phandle == phandle)
617047fbc8bSkettenis 			return nd->nd_read(nd->nd_cookie, addr, data, size);
618047fbc8bSkettenis 	}
619047fbc8bSkettenis 
620047fbc8bSkettenis 	return ENXIO;
621047fbc8bSkettenis }
6221bd1536fSkettenis 
6231bd1536fSkettenis int
nvmem_read_cell(int node,const char * name,void * data,bus_size_t size)6241bd1536fSkettenis nvmem_read_cell(int node, const char *name, void *data, bus_size_t size)
6251bd1536fSkettenis {
6261bd1536fSkettenis 	struct nvmem_device *nd;
6271bd1536fSkettenis 	struct nvmem_cell *nc;
62898df0a09Skettenis 	uint8_t *p = data;
62998df0a09Skettenis 	bus_addr_t addr;
6301bd1536fSkettenis 	uint32_t phandle, *phandles;
631eb0ca4d1Skettenis 	uint32_t offset, bitlen;
632eb0ca4d1Skettenis 	int id, len, first;
6331bd1536fSkettenis 
6341bd1536fSkettenis 	id = OF_getindex(node, name, "nvmem-cell-names");
6351bd1536fSkettenis 	if (id < 0)
6361bd1536fSkettenis 		return ENXIO;
6371bd1536fSkettenis 
6381bd1536fSkettenis 	len = OF_getproplen(node, "nvmem-cells");
6391bd1536fSkettenis 	if (len <= 0)
6401bd1536fSkettenis 		return ENXIO;
6411bd1536fSkettenis 
6421bd1536fSkettenis 	phandles = malloc(len, M_TEMP, M_WAITOK);
6431bd1536fSkettenis 	OF_getpropintarray(node, "nvmem-cells", phandles, len);
6441bd1536fSkettenis 	phandle = phandles[id];
6451bd1536fSkettenis 	free(phandles, M_TEMP, len);
6461bd1536fSkettenis 
6471bd1536fSkettenis 	LIST_FOREACH(nc, &nvmem_cells, nc_list) {
6481bd1536fSkettenis 		if (nc->nc_phandle == phandle)
6491bd1536fSkettenis 			break;
6501bd1536fSkettenis 	}
6511bd1536fSkettenis 	if (nc == NULL)
6521bd1536fSkettenis 		return ENXIO;
6531bd1536fSkettenis 
6541bd1536fSkettenis 	nd = nc->nc_nd;
6552b93968fSkettenis 	if (nd->nd_read == NULL)
6562b93968fSkettenis 		return EACCES;
657eb0ca4d1Skettenis 
658eb0ca4d1Skettenis 	first = 1;
65998df0a09Skettenis 	addr = nc->nc_addr + (nc->nc_offset / 8);
66098df0a09Skettenis 	offset = nc->nc_offset % 8;
661eb0ca4d1Skettenis 	bitlen = nc->nc_bitlen;
662eb0ca4d1Skettenis 	while (bitlen > 0 && size > 0) {
663eb0ca4d1Skettenis 		uint8_t mask, tmp;
664eb0ca4d1Skettenis 		int error;
665eb0ca4d1Skettenis 
66698df0a09Skettenis 		error = nd->nd_read(nd->nd_cookie, addr++, &tmp, 1);
667eb0ca4d1Skettenis 		if (error)
668eb0ca4d1Skettenis 			return error;
669eb0ca4d1Skettenis 
670eb0ca4d1Skettenis 		if (bitlen >= 8)
671eb0ca4d1Skettenis 			mask = 0xff;
672eb0ca4d1Skettenis 		else
673eb0ca4d1Skettenis 			mask = (1 << bitlen) - 1;
674eb0ca4d1Skettenis 
675eb0ca4d1Skettenis 		if (!first) {
676eb0ca4d1Skettenis 			*p++ |= (tmp << (8 - offset)) & (mask << (8 - offset));
677eb0ca4d1Skettenis 			bitlen -= MIN(offset, bitlen);
67898df0a09Skettenis 			mask >>= offset;
679eb0ca4d1Skettenis 			size--;
680eb0ca4d1Skettenis 		}
681eb0ca4d1Skettenis 
682eb0ca4d1Skettenis 		if (bitlen > 0 && size > 0) {
68398df0a09Skettenis 			*p = (tmp >> offset) & mask;
684eb0ca4d1Skettenis 			bitlen -= MIN(8 - offset, bitlen);
685eb0ca4d1Skettenis 		}
686eb0ca4d1Skettenis 
687eb0ca4d1Skettenis 		first = 0;
688eb0ca4d1Skettenis 	}
689eb0ca4d1Skettenis 	if (nc->nc_bitlen > 0)
690eb0ca4d1Skettenis 		return 0;
691eb0ca4d1Skettenis 
692eb0ca4d1Skettenis 	if (size > nc->nc_size)
693eb0ca4d1Skettenis 		return EINVAL;
694eb0ca4d1Skettenis 
6951bd1536fSkettenis 	return nd->nd_read(nd->nd_cookie, nc->nc_addr, data, size);
6961bd1536fSkettenis }
697f11bbe72Spatrick 
6982b93968fSkettenis int
nvmem_write_cell(int node,const char * name,const void * data,bus_size_t size)6992b93968fSkettenis nvmem_write_cell(int node, const char *name, const void *data, bus_size_t size)
7002b93968fSkettenis {
7012b93968fSkettenis 	struct nvmem_device *nd;
7022b93968fSkettenis 	struct nvmem_cell *nc;
70398df0a09Skettenis 	const uint8_t *p = data;
70498df0a09Skettenis 	bus_addr_t addr;
7052b93968fSkettenis 	uint32_t phandle, *phandles;
706eb0ca4d1Skettenis 	uint32_t offset, bitlen;
707eb0ca4d1Skettenis 	int id, len, first;
7082b93968fSkettenis 
7092b93968fSkettenis 	id = OF_getindex(node, name, "nvmem-cell-names");
7102b93968fSkettenis 	if (id < 0)
7112b93968fSkettenis 		return ENXIO;
7122b93968fSkettenis 
7132b93968fSkettenis 	len = OF_getproplen(node, "nvmem-cells");
7142b93968fSkettenis 	if (len <= 0)
7152b93968fSkettenis 		return ENXIO;
7162b93968fSkettenis 
7172b93968fSkettenis 	phandles = malloc(len, M_TEMP, M_WAITOK);
7182b93968fSkettenis 	OF_getpropintarray(node, "nvmem-cells", phandles, len);
7192b93968fSkettenis 	phandle = phandles[id];
7202b93968fSkettenis 	free(phandles, M_TEMP, len);
7212b93968fSkettenis 
7222b93968fSkettenis 	LIST_FOREACH(nc, &nvmem_cells, nc_list) {
7232b93968fSkettenis 		if (nc->nc_phandle == phandle)
7242b93968fSkettenis 			break;
7252b93968fSkettenis 	}
7262b93968fSkettenis 	if (nc == NULL)
7272b93968fSkettenis 		return ENXIO;
7282b93968fSkettenis 
7292b93968fSkettenis 	nd = nc->nc_nd;
7302b93968fSkettenis 	if (nd->nd_write == NULL)
7312b93968fSkettenis 		return EACCES;
732eb0ca4d1Skettenis 
733eb0ca4d1Skettenis 	first = 1;
73498df0a09Skettenis 	addr = nc->nc_addr + (nc->nc_offset / 8);
73598df0a09Skettenis 	offset = nc->nc_offset % 8;
736eb0ca4d1Skettenis 	bitlen = nc->nc_bitlen;
737eb0ca4d1Skettenis 	while (bitlen > 0 && size > 0) {
738eb0ca4d1Skettenis 		uint8_t mask, tmp;
739eb0ca4d1Skettenis 		int error;
740eb0ca4d1Skettenis 
74198df0a09Skettenis 		error = nd->nd_read(nd->nd_cookie, addr, &tmp, 1);
742eb0ca4d1Skettenis 		if (error)
743eb0ca4d1Skettenis 			return error;
744eb0ca4d1Skettenis 
745eb0ca4d1Skettenis 		if (bitlen >= 8)
746eb0ca4d1Skettenis 			mask = 0xff;
747eb0ca4d1Skettenis 		else
748eb0ca4d1Skettenis 			mask = (1 << bitlen) - 1;
749eb0ca4d1Skettenis 
750eb0ca4d1Skettenis 		tmp &= ~(mask << offset);
751eb0ca4d1Skettenis 		tmp |= (*p++ << offset) & (mask << offset);
752eb0ca4d1Skettenis 		bitlen -= MIN(8 - offset, bitlen);
753eb0ca4d1Skettenis 		size--;
754eb0ca4d1Skettenis 
755eb0ca4d1Skettenis 		if (!first && bitlen > 0 && size > 0) {
756eb0ca4d1Skettenis 			tmp &= ~(mask >> (8 - offset));
757eb0ca4d1Skettenis 			tmp |= (*p >> (8 - offset)) & (mask >> (8 - offset));
758eb0ca4d1Skettenis 			bitlen -= MIN(offset, bitlen);
759eb0ca4d1Skettenis 		}
760eb0ca4d1Skettenis 
76198df0a09Skettenis 		error = nd->nd_write(nd->nd_cookie, addr++, &tmp, 1);
762eb0ca4d1Skettenis 		if (error)
763eb0ca4d1Skettenis 			return error;
764eb0ca4d1Skettenis 
765eb0ca4d1Skettenis 		first = 0;
766eb0ca4d1Skettenis 	}
767eb0ca4d1Skettenis 	if (nc->nc_bitlen > 0)
768eb0ca4d1Skettenis 		return 0;
769eb0ca4d1Skettenis 
770eb0ca4d1Skettenis 	if (size > nc->nc_size)
771eb0ca4d1Skettenis 		return EINVAL;
772eb0ca4d1Skettenis 
7732b93968fSkettenis 	return nd->nd_write(nd->nd_cookie, nc->nc_addr, data, size);
7742b93968fSkettenis }
7752b93968fSkettenis 
776c2490375Skettenis /* Port/endpoint interface support */
777f11bbe72Spatrick 
778c2490375Skettenis LIST_HEAD(, endpoint) endpoints =
779c2490375Skettenis 	LIST_HEAD_INITIALIZER(endpoints);
780f11bbe72Spatrick 
781f11bbe72Spatrick void
endpoint_register(int node,struct device_port * dp,enum endpoint_type type)782c2490375Skettenis endpoint_register(int node, struct device_port *dp, enum endpoint_type type)
783f11bbe72Spatrick {
784c2490375Skettenis 	struct endpoint *ep;
785c2490375Skettenis 
786c2490375Skettenis 	ep = malloc(sizeof(*ep), M_DEVBUF, M_WAITOK);
787c2490375Skettenis 	ep->ep_node = node;
788c2490375Skettenis 	ep->ep_phandle = OF_getpropint(node, "phandle", 0);
789c2490375Skettenis 	ep->ep_reg = OF_getpropint(node, "reg", -1);
790c2490375Skettenis 	ep->ep_port = dp;
791c2490375Skettenis 	ep->ep_type = type;
792c2490375Skettenis 
793c2490375Skettenis 	LIST_INSERT_HEAD(&endpoints, ep, ep_list);
794c2490375Skettenis 	LIST_INSERT_HEAD(&dp->dp_endpoints, ep, ep_plist);
795f11bbe72Spatrick }
796f11bbe72Spatrick 
797c2490375Skettenis void
device_port_register(int node,struct device_ports * ports,enum endpoint_type type)798c2490375Skettenis device_port_register(int node, struct device_ports *ports,
799c2490375Skettenis     enum endpoint_type type)
800f11bbe72Spatrick {
801c2490375Skettenis 	struct device_port *dp;
802f11bbe72Spatrick 
803c2490375Skettenis 	dp = malloc(sizeof(*dp), M_DEVBUF, M_WAITOK);
804c2490375Skettenis 	dp->dp_node = node;
805c2490375Skettenis 	dp->dp_phandle = OF_getpropint(node, "phandle", 0);
806c2490375Skettenis 	dp->dp_reg = OF_getpropint(node, "reg", -1);
807c2490375Skettenis 	dp->dp_ports = ports;
808c2490375Skettenis 	LIST_INIT(&dp->dp_endpoints);
809c2490375Skettenis 	for (node = OF_child(node); node; node = OF_peer(node))
810c2490375Skettenis 		endpoint_register(node, dp, type);
811c2490375Skettenis 
812c2490375Skettenis 	LIST_INSERT_HEAD(&ports->dp_ports, dp, dp_list);
813c2490375Skettenis }
814c2490375Skettenis 
815c2490375Skettenis void
device_ports_register(struct device_ports * ports,enum endpoint_type type)816c2490375Skettenis device_ports_register(struct device_ports *ports,
817c2490375Skettenis     enum endpoint_type type)
818c2490375Skettenis {
819c2490375Skettenis 	int node;
820c2490375Skettenis 
821c2490375Skettenis 	LIST_INIT(&ports->dp_ports);
822c2490375Skettenis 
823c2490375Skettenis 	node = OF_getnodebyname(ports->dp_node, "ports");
824c2490375Skettenis 	if (node == 0) {
825c2490375Skettenis 		node = OF_getnodebyname(ports->dp_node, "port");
826f11bbe72Spatrick 		if (node == 0)
827c2490375Skettenis 			return;
828f11bbe72Spatrick 
829c2490375Skettenis 		device_port_register(node, ports, type);
830c2490375Skettenis 		return;
831f11bbe72Spatrick 	}
832f11bbe72Spatrick 
833c2490375Skettenis 	for (node = OF_child(node); node; node = OF_peer(node))
834c2490375Skettenis 		device_port_register(node, ports, type);
835c2490375Skettenis }
836c2490375Skettenis 
83757d4ae74Skettenis struct device_ports *
device_ports_byphandle(uint32_t phandle)83857d4ae74Skettenis device_ports_byphandle(uint32_t phandle)
83957d4ae74Skettenis {
84057d4ae74Skettenis 	struct endpoint *ep;
84157d4ae74Skettenis 
84257d4ae74Skettenis 	if (phandle == 0)
84357d4ae74Skettenis 		return NULL;
84457d4ae74Skettenis 
84557d4ae74Skettenis 	LIST_FOREACH(ep, &endpoints, ep_list) {
84657d4ae74Skettenis 		if (ep->ep_port->dp_phandle == phandle)
84757d4ae74Skettenis 			return ep->ep_port->dp_ports;
84857d4ae74Skettenis 	}
84957d4ae74Skettenis 
85057d4ae74Skettenis 	return NULL;
85157d4ae74Skettenis }
85257d4ae74Skettenis 
853c2490375Skettenis struct endpoint *
endpoint_byphandle(uint32_t phandle)854c2490375Skettenis endpoint_byphandle(uint32_t phandle)
855c2490375Skettenis {
856c2490375Skettenis 	struct endpoint *ep;
857c2490375Skettenis 
858a7702439Spatrick 	if (phandle == 0)
859a7702439Spatrick 		return NULL;
860a7702439Spatrick 
861c2490375Skettenis 	LIST_FOREACH(ep, &endpoints, ep_list) {
862c2490375Skettenis 		if (ep->ep_phandle == phandle)
863c2490375Skettenis 			return ep;
864c2490375Skettenis 	}
865c2490375Skettenis 
866c2490375Skettenis 	return NULL;
867c2490375Skettenis }
868c2490375Skettenis 
869c2490375Skettenis struct endpoint *
endpoint_byreg(struct device_ports * ports,uint32_t dp_reg,uint32_t ep_reg)870c2490375Skettenis endpoint_byreg(struct device_ports *ports, uint32_t dp_reg, uint32_t ep_reg)
871c2490375Skettenis {
872c2490375Skettenis 	struct device_port *dp;
873c2490375Skettenis 	struct endpoint *ep;
874c2490375Skettenis 
875c2490375Skettenis 	LIST_FOREACH(dp, &ports->dp_ports, dp_list) {
876c2490375Skettenis 		if (dp->dp_reg != dp_reg)
877c2490375Skettenis 			continue;
878c2490375Skettenis 		LIST_FOREACH(ep, &dp->dp_endpoints, ep_list) {
879c2490375Skettenis 			if (ep->ep_reg != ep_reg)
880c2490375Skettenis 				continue;
881c2490375Skettenis 			return ep;
882c2490375Skettenis 		}
883c2490375Skettenis 	}
884c2490375Skettenis 
885c2490375Skettenis 	return NULL;
886c2490375Skettenis }
887c2490375Skettenis 
888c2490375Skettenis struct endpoint *
endpoint_remote(struct endpoint * ep)889c2490375Skettenis endpoint_remote(struct endpoint *ep)
890c2490375Skettenis {
891c2490375Skettenis 	struct endpoint *rep;
892c2490375Skettenis 	int phandle;
893c2490375Skettenis 
894c2490375Skettenis 	phandle = OF_getpropint(ep->ep_node, "remote-endpoint", 0);
895c2490375Skettenis 	if (phandle == 0)
896c2490375Skettenis 		return NULL;
897c2490375Skettenis 
898c2490375Skettenis 	LIST_FOREACH(rep, &endpoints, ep_list) {
899c2490375Skettenis 		if (rep->ep_phandle == phandle)
900c2490375Skettenis 			return rep;
901c2490375Skettenis 	}
902c2490375Skettenis 
903c2490375Skettenis 	return NULL;
904f11bbe72Spatrick }
905f11bbe72Spatrick 
906f11bbe72Spatrick int
endpoint_activate(struct endpoint * ep,void * arg)907c2490375Skettenis endpoint_activate(struct endpoint *ep, void *arg)
908f11bbe72Spatrick {
909c2490375Skettenis 	struct device_ports *ports = ep->ep_port->dp_ports;
910c2490375Skettenis 	return ports->dp_ep_activate(ports->dp_cookie, ep, arg);
911c2490375Skettenis }
912f11bbe72Spatrick 
913c2490375Skettenis void *
endpoint_get_cookie(struct endpoint * ep)914c2490375Skettenis endpoint_get_cookie(struct endpoint *ep)
915c2490375Skettenis {
916c2490375Skettenis 	struct device_ports *ports = ep->ep_port->dp_ports;
917c2490375Skettenis 	return ports->dp_ep_get_cookie(ports->dp_cookie, ep);
918c2490375Skettenis }
919c2490375Skettenis 
920eebea054Skettenis int
device_port_activate(uint32_t phandle,void * arg)921c2490375Skettenis device_port_activate(uint32_t phandle, void *arg)
922c2490375Skettenis {
923d53ed3a6Skettenis 	struct device_port *dp = NULL;
924c2490375Skettenis 	struct endpoint *ep, *rep;
925eebea054Skettenis 	int count;
926c2490375Skettenis 	int error;
927c2490375Skettenis 
928a7702439Spatrick 	if (phandle == 0)
929a7702439Spatrick 		return ENXIO;
930a7702439Spatrick 
931c2490375Skettenis 	LIST_FOREACH(ep, &endpoints, ep_list) {
932c2490375Skettenis 		if (ep->ep_port->dp_phandle == phandle) {
933c2490375Skettenis 			dp = ep->ep_port;
934f11bbe72Spatrick 			break;
935f11bbe72Spatrick 		}
936c2490375Skettenis 	}
937c2490375Skettenis 	if (dp == NULL)
938eebea054Skettenis 		return ENXIO;
939f11bbe72Spatrick 
940eebea054Skettenis 	count = 0;
941c2490375Skettenis 	LIST_FOREACH(ep, &dp->dp_endpoints, ep_plist) {
942c2490375Skettenis 		rep = endpoint_remote(ep);
943c2490375Skettenis 		if (rep == NULL)
944c2490375Skettenis 			continue;
945c2490375Skettenis 
946c2490375Skettenis 		error = endpoint_activate(ep, arg);
947c2490375Skettenis 		if (error)
948c2490375Skettenis 			continue;
949c2490375Skettenis 		error = endpoint_activate(rep, arg);
950c2490375Skettenis 		if (error)
951c2490375Skettenis 			continue;
952eebea054Skettenis 		count++;
953c2490375Skettenis 	}
954eebea054Skettenis 
955eebea054Skettenis 	return count ? 0 : ENXIO;
956f11bbe72Spatrick }
957470a8ce0Spatrick 
958470a8ce0Spatrick /* Digital audio interface support */
959470a8ce0Spatrick 
960470a8ce0Spatrick LIST_HEAD(, dai_device) dai_devices =
961470a8ce0Spatrick 	LIST_HEAD_INITIALIZER(dai_devices);
962470a8ce0Spatrick 
96357d4ae74Skettenis void *
dai_ep_get_cookie(void * cookie,struct endpoint * ep)96457d4ae74Skettenis dai_ep_get_cookie(void *cookie, struct endpoint *ep)
96557d4ae74Skettenis {
96657d4ae74Skettenis 	return cookie;
96757d4ae74Skettenis }
96857d4ae74Skettenis 
969470a8ce0Spatrick void
dai_register(struct dai_device * dd)970470a8ce0Spatrick dai_register(struct dai_device *dd)
971470a8ce0Spatrick {
972470a8ce0Spatrick 	dd->dd_phandle = OF_getpropint(dd->dd_node, "phandle", 0);
97357d4ae74Skettenis 	if (dd->dd_phandle != 0)
974470a8ce0Spatrick 		LIST_INSERT_HEAD(&dai_devices, dd, dd_list);
97557d4ae74Skettenis 
97657d4ae74Skettenis 	dd->dd_ports.dp_node = dd->dd_node;
97757d4ae74Skettenis 	dd->dd_ports.dp_cookie = dd;
97857d4ae74Skettenis 	dd->dd_ports.dp_ep_get_cookie = dai_ep_get_cookie;
97957d4ae74Skettenis 	device_ports_register(&dd->dd_ports, EP_DAI_DEVICE);
980470a8ce0Spatrick }
981470a8ce0Spatrick 
982470a8ce0Spatrick struct dai_device *
dai_byphandle(uint32_t phandle)983470a8ce0Spatrick dai_byphandle(uint32_t phandle)
984470a8ce0Spatrick {
985470a8ce0Spatrick 	struct dai_device *dd;
986470a8ce0Spatrick 
987470a8ce0Spatrick 	if (phandle == 0)
988470a8ce0Spatrick 		return NULL;
989470a8ce0Spatrick 
990470a8ce0Spatrick 	LIST_FOREACH(dd, &dai_devices, dd_list) {
991470a8ce0Spatrick 		if (dd->dd_phandle == phandle)
992470a8ce0Spatrick 			return dd;
993470a8ce0Spatrick 	}
994470a8ce0Spatrick 
995470a8ce0Spatrick 	return NULL;
996470a8ce0Spatrick }
997da5e0c17Spatrick 
998da5e0c17Spatrick /* MII support */
999da5e0c17Spatrick 
1000da5e0c17Spatrick LIST_HEAD(, mii_bus) mii_busses =
1001da5e0c17Spatrick 	LIST_HEAD_INITIALIZER(mii_busses);
1002da5e0c17Spatrick 
1003da5e0c17Spatrick void
mii_register(struct mii_bus * md)1004da5e0c17Spatrick mii_register(struct mii_bus *md)
1005da5e0c17Spatrick {
1006da5e0c17Spatrick 	LIST_INSERT_HEAD(&mii_busses, md, md_list);
1007da5e0c17Spatrick }
1008da5e0c17Spatrick 
1009da5e0c17Spatrick struct mii_bus *
mii_bynode(int node)10105368d553Skettenis mii_bynode(int node)
1011da5e0c17Spatrick {
1012da5e0c17Spatrick 	struct mii_bus *md;
10135368d553Skettenis 
10145368d553Skettenis 	LIST_FOREACH(md, &mii_busses, md_list) {
10155368d553Skettenis 		if (md->md_node == node)
10165368d553Skettenis 			return md;
10175368d553Skettenis 	}
10185368d553Skettenis 
10195368d553Skettenis 	return NULL;
10205368d553Skettenis }
10215368d553Skettenis 
10225368d553Skettenis struct mii_bus *
mii_byphandle(uint32_t phandle)10235368d553Skettenis mii_byphandle(uint32_t phandle)
10245368d553Skettenis {
1025da5e0c17Spatrick 	int node;
1026da5e0c17Spatrick 
1027da5e0c17Spatrick 	if (phandle == 0)
1028da5e0c17Spatrick 		return NULL;
1029da5e0c17Spatrick 
1030da5e0c17Spatrick 	node = OF_getnodebyphandle(phandle);
1031da5e0c17Spatrick 	if (node == 0)
1032da5e0c17Spatrick 		return NULL;
1033da5e0c17Spatrick 
1034da5e0c17Spatrick 	node = OF_parent(node);
1035da5e0c17Spatrick 	if (node == 0)
1036da5e0c17Spatrick 		return NULL;
1037da5e0c17Spatrick 
10385368d553Skettenis 	return mii_bynode(node);
1039da5e0c17Spatrick }
1040682b00caSkettenis 
1041682b00caSkettenis /* IOMMU support */
1042682b00caSkettenis 
1043682b00caSkettenis LIST_HEAD(, iommu_device) iommu_devices =
1044682b00caSkettenis 	LIST_HEAD_INITIALIZER(iommu_devices);
1045682b00caSkettenis 
1046682b00caSkettenis void
iommu_device_register(struct iommu_device * id)1047682b00caSkettenis iommu_device_register(struct iommu_device *id)
1048682b00caSkettenis {
1049682b00caSkettenis 	id->id_phandle = OF_getpropint(id->id_node, "phandle", 0);
1050682b00caSkettenis 	if (id->id_phandle == 0)
1051682b00caSkettenis 		return;
1052682b00caSkettenis 
1053682b00caSkettenis 	LIST_INSERT_HEAD(&iommu_devices, id, id_list);
1054682b00caSkettenis }
1055682b00caSkettenis 
1056682b00caSkettenis bus_dma_tag_t
iommu_device_do_map(uint32_t phandle,uint32_t * cells,bus_dma_tag_t dmat)1057682b00caSkettenis iommu_device_do_map(uint32_t phandle, uint32_t *cells, bus_dma_tag_t dmat)
1058682b00caSkettenis {
1059682b00caSkettenis 	struct iommu_device *id;
1060682b00caSkettenis 
1061682b00caSkettenis 	if (phandle == 0)
1062682b00caSkettenis 		return dmat;
1063682b00caSkettenis 
1064682b00caSkettenis 	LIST_FOREACH(id, &iommu_devices, id_list) {
1065682b00caSkettenis 		if (id->id_phandle == phandle)
1066682b00caSkettenis 			return id->id_map(id->id_cookie, cells, dmat);
1067682b00caSkettenis 	}
1068682b00caSkettenis 
1069682b00caSkettenis 	return dmat;
1070682b00caSkettenis }
1071682b00caSkettenis 
1072415019ceSpatrick int
iommu_device_lookup(int node,uint32_t * phandle,uint32_t * cells)107388ff5766Spatrick iommu_device_lookup(int node, uint32_t *phandle, uint32_t *cells)
1074ba301097Spatrick {
1075ba301097Spatrick 	uint32_t *cell;
1076ba301097Spatrick 	uint32_t *map;
1077ba301097Spatrick 	int len, icells, ncells;
1078415019ceSpatrick 	int ret = 1;
107988ff5766Spatrick 	int i;
1080ba301097Spatrick 
1081ba301097Spatrick 	len = OF_getproplen(node, "iommus");
1082ba301097Spatrick 	if (len <= 0)
1083415019ceSpatrick 		return ret;
1084ba301097Spatrick 
1085ba301097Spatrick 	map = malloc(len, M_TEMP, M_WAITOK);
1086ba301097Spatrick 	OF_getpropintarray(node, "iommus", map, len);
1087ba301097Spatrick 
1088ba301097Spatrick 	cell = map;
1089ba301097Spatrick 	ncells = len / sizeof(uint32_t);
1090ba301097Spatrick 	while (ncells > 1) {
1091ba301097Spatrick 		node = OF_getnodebyphandle(cell[0]);
1092ba301097Spatrick 		if (node == 0)
1093ba301097Spatrick 			goto out;
1094ba301097Spatrick 
1095ba301097Spatrick 		icells = OF_getpropint(node, "#iommu-cells", 1);
1096ba301097Spatrick 		if (ncells < icells + 1)
1097ba301097Spatrick 			goto out;
1098ba301097Spatrick 
109988ff5766Spatrick 		KASSERT(icells <= 2);
1100ba301097Spatrick 
1101415019ceSpatrick 		*phandle = cell[0];
110288ff5766Spatrick 		for (i = 0; i < icells; i++)
110388ff5766Spatrick 			cells[i] = cell[1 + i];
1104415019ceSpatrick 		ret = 0;
1105ba301097Spatrick 		break;
1106ba301097Spatrick 
1107ba301097Spatrick 		cell += (1 + icells);
1108ba301097Spatrick 		ncells -= (1 + icells);
1109ba301097Spatrick 	}
1110ba301097Spatrick 
1111ba301097Spatrick out:
1112ba301097Spatrick 	free(map, M_TEMP, len);
1113ba301097Spatrick 
1114415019ceSpatrick 	return ret;
1115ba301097Spatrick }
1116ba301097Spatrick 
1117415019ceSpatrick int
iommu_device_lookup_pci(int node,uint32_t rid,uint32_t * phandle,uint32_t * cells)1118415019ceSpatrick iommu_device_lookup_pci(int node, uint32_t rid, uint32_t *phandle,
111988ff5766Spatrick     uint32_t *cells)
1120682b00caSkettenis {
1121415019ceSpatrick 	uint32_t sid_base;
1122682b00caSkettenis 	uint32_t *cell;
1123682b00caSkettenis 	uint32_t *map;
1124682b00caSkettenis 	uint32_t mask, rid_base;
11250071848bSkettenis 	int len, length, icells, ncells;
1126415019ceSpatrick 	int ret = 1;
1127682b00caSkettenis 
1128682b00caSkettenis 	len = OF_getproplen(node, "iommu-map");
1129682b00caSkettenis 	if (len <= 0)
1130415019ceSpatrick 		return ret;
1131682b00caSkettenis 
1132682b00caSkettenis 	map = malloc(len, M_TEMP, M_WAITOK);
1133682b00caSkettenis 	OF_getpropintarray(node, "iommu-map", map, len);
1134682b00caSkettenis 
113544d82538Spatrick 	mask = OF_getpropint(node, "iommu-map-mask", 0xffff);
1136682b00caSkettenis 	rid = rid & mask;
1137682b00caSkettenis 
1138682b00caSkettenis 	cell = map;
1139682b00caSkettenis 	ncells = len / sizeof(uint32_t);
1140682b00caSkettenis 	while (ncells > 1) {
1141682b00caSkettenis 		node = OF_getnodebyphandle(cell[1]);
1142682b00caSkettenis 		if (node == 0)
1143682b00caSkettenis 			goto out;
1144682b00caSkettenis 
1145682b00caSkettenis 		icells = OF_getpropint(node, "#iommu-cells", 1);
1146682b00caSkettenis 		if (ncells < icells + 3)
1147682b00caSkettenis 			goto out;
1148682b00caSkettenis 
11490071848bSkettenis 		KASSERT(icells == 1);
1150682b00caSkettenis 
1151682b00caSkettenis 		rid_base = cell[0];
1152682b00caSkettenis 		sid_base = cell[2];
11530071848bSkettenis 		length = cell[3];
1154682b00caSkettenis 		if (rid >= rid_base && rid < rid_base + length) {
115588ff5766Spatrick 			cells[0] = sid_base + (rid - rid_base);
1156415019ceSpatrick 			*phandle = cell[1];
1157415019ceSpatrick 			ret = 0;
1158682b00caSkettenis 			break;
1159682b00caSkettenis 		}
1160682b00caSkettenis 
11610071848bSkettenis 		cell += 4;
11620071848bSkettenis 		ncells -= 4;
1163682b00caSkettenis 	}
1164682b00caSkettenis 
1165682b00caSkettenis out:
1166682b00caSkettenis 	free(map, M_TEMP, len);
1167682b00caSkettenis 
1168415019ceSpatrick 	return ret;
1169415019ceSpatrick }
1170415019ceSpatrick 
1171415019ceSpatrick bus_dma_tag_t
iommu_device_map(int node,bus_dma_tag_t dmat)1172415019ceSpatrick iommu_device_map(int node, bus_dma_tag_t dmat)
1173415019ceSpatrick {
117488ff5766Spatrick 	uint32_t phandle, cells[2] = {0};
1175415019ceSpatrick 
117688ff5766Spatrick 	if (iommu_device_lookup(node, &phandle, &cells[0]))
1177415019ceSpatrick 		return dmat;
1178415019ceSpatrick 
117988ff5766Spatrick 	return iommu_device_do_map(phandle, &cells[0], dmat);
1180682b00caSkettenis }
1181415019ceSpatrick 
1182415019ceSpatrick bus_dma_tag_t
iommu_device_map_pci(int node,uint32_t rid,bus_dma_tag_t dmat)1183415019ceSpatrick iommu_device_map_pci(int node, uint32_t rid, bus_dma_tag_t dmat)
1184415019ceSpatrick {
118588ff5766Spatrick 	uint32_t phandle, cells[2] = {0};
1186415019ceSpatrick 
118788ff5766Spatrick 	if (iommu_device_lookup_pci(node, rid, &phandle, &cells[0]))
1188415019ceSpatrick 		return dmat;
1189415019ceSpatrick 
119088ff5766Spatrick 	return iommu_device_do_map(phandle, &cells[0], dmat);
1191415019ceSpatrick }
1192415019ceSpatrick 
1193415019ceSpatrick void
iommu_device_do_reserve(uint32_t phandle,uint32_t * cells,bus_addr_t addr,bus_size_t size)1194415019ceSpatrick iommu_device_do_reserve(uint32_t phandle, uint32_t *cells, bus_addr_t addr,
1195415019ceSpatrick     bus_size_t size)
1196415019ceSpatrick {
1197415019ceSpatrick 	struct iommu_device *id;
1198415019ceSpatrick 
1199415019ceSpatrick 	if (phandle == 0)
1200415019ceSpatrick 		return;
1201415019ceSpatrick 
1202415019ceSpatrick 	LIST_FOREACH(id, &iommu_devices, id_list) {
1203415019ceSpatrick 		if (id->id_phandle == phandle) {
1204415019ceSpatrick 			id->id_reserve(id->id_cookie, cells, addr, size);
1205415019ceSpatrick 			break;
1206415019ceSpatrick 		}
1207415019ceSpatrick 	}
1208415019ceSpatrick }
1209415019ceSpatrick 
1210415019ceSpatrick void
iommu_reserve_region_pci(int node,uint32_t rid,bus_addr_t addr,bus_size_t size)1211415019ceSpatrick iommu_reserve_region_pci(int node, uint32_t rid, bus_addr_t addr,
1212415019ceSpatrick     bus_size_t size)
1213415019ceSpatrick {
121488ff5766Spatrick 	uint32_t phandle, cells[2] = {0};
1215415019ceSpatrick 
121688ff5766Spatrick 	if (iommu_device_lookup_pci(node, rid, &phandle, &cells[0]))
1217415019ceSpatrick 		return;
1218415019ceSpatrick 
121988ff5766Spatrick 	return iommu_device_do_reserve(phandle, &cells[0], addr, size);
1220415019ceSpatrick }
1221aa1f5e88Skettenis 
1222aa1f5e88Skettenis /*
1223aa1f5e88Skettenis  * Mailbox support.
1224aa1f5e88Skettenis  */
1225aa1f5e88Skettenis 
1226aa1f5e88Skettenis struct mbox_channel {
1227aa1f5e88Skettenis 	struct mbox_device	*mc_md;
1228aa1f5e88Skettenis 	void			*mc_cookie;
1229aa1f5e88Skettenis };
1230aa1f5e88Skettenis 
1231aa1f5e88Skettenis LIST_HEAD(, mbox_device) mbox_devices =
1232aa1f5e88Skettenis 	LIST_HEAD_INITIALIZER(mbox_devices);
1233aa1f5e88Skettenis 
1234aa1f5e88Skettenis void
mbox_register(struct mbox_device * md)1235aa1f5e88Skettenis mbox_register(struct mbox_device *md)
1236aa1f5e88Skettenis {
1237aa1f5e88Skettenis 	md->md_cells = OF_getpropint(md->md_node, "#mbox-cells", 0);
1238aa1f5e88Skettenis 	md->md_phandle = OF_getpropint(md->md_node, "phandle", 0);
1239aa1f5e88Skettenis 	if (md->md_phandle == 0)
1240aa1f5e88Skettenis 		return;
1241aa1f5e88Skettenis 
1242aa1f5e88Skettenis 	LIST_INSERT_HEAD(&mbox_devices, md, md_list);
1243aa1f5e88Skettenis }
1244aa1f5e88Skettenis 
1245aa1f5e88Skettenis struct mbox_channel *
mbox_channel_cells(uint32_t * cells,struct mbox_client * client)1246aa1f5e88Skettenis mbox_channel_cells(uint32_t *cells, struct mbox_client *client)
1247aa1f5e88Skettenis {
1248aa1f5e88Skettenis 	struct mbox_device *md;
1249aa1f5e88Skettenis 	struct mbox_channel *mc;
1250aa1f5e88Skettenis 	uint32_t phandle = cells[0];
1251aa1f5e88Skettenis 	void *cookie;
1252aa1f5e88Skettenis 
1253aa1f5e88Skettenis 	LIST_FOREACH(md, &mbox_devices, md_list) {
1254aa1f5e88Skettenis 		if (md->md_phandle == phandle)
1255aa1f5e88Skettenis 			break;
1256aa1f5e88Skettenis 	}
1257aa1f5e88Skettenis 
1258aa1f5e88Skettenis 	if (md && md->md_channel) {
1259aa1f5e88Skettenis 		cookie = md->md_channel(md->md_cookie, &cells[1], client);
1260aa1f5e88Skettenis 		if (cookie) {
1261aa1f5e88Skettenis 			mc = malloc(sizeof(*mc), M_DEVBUF, M_WAITOK);
1262aa1f5e88Skettenis 			mc->mc_md = md;
1263aa1f5e88Skettenis 			mc->mc_cookie = cookie;
1264aa1f5e88Skettenis 			return mc;
1265aa1f5e88Skettenis 		}
1266aa1f5e88Skettenis 	}
1267aa1f5e88Skettenis 
1268aa1f5e88Skettenis 	return NULL;
1269aa1f5e88Skettenis }
1270aa1f5e88Skettenis 
1271aa1f5e88Skettenis uint32_t *
mbox_next_mbox(uint32_t * cells)1272aa1f5e88Skettenis mbox_next_mbox(uint32_t *cells)
1273aa1f5e88Skettenis {
1274aa1f5e88Skettenis 	uint32_t phandle = cells[0];
1275aa1f5e88Skettenis 	int node, ncells;
1276aa1f5e88Skettenis 
1277aa1f5e88Skettenis 	node = OF_getnodebyphandle(phandle);
1278aa1f5e88Skettenis 	if (node == 0)
1279aa1f5e88Skettenis 		return NULL;
1280aa1f5e88Skettenis 
1281aa1f5e88Skettenis 	ncells = OF_getpropint(node, "#mbox-cells", 0);
1282aa1f5e88Skettenis 	return cells + ncells + 1;
1283aa1f5e88Skettenis }
1284aa1f5e88Skettenis 
1285aa1f5e88Skettenis struct mbox_channel *
mbox_channel_idx(int node,int idx,struct mbox_client * client)1286aa1f5e88Skettenis mbox_channel_idx(int node, int idx, struct mbox_client *client)
1287aa1f5e88Skettenis {
1288aa1f5e88Skettenis 	struct mbox_channel *mc = NULL;
1289aa1f5e88Skettenis 	uint32_t *mboxes;
1290aa1f5e88Skettenis 	uint32_t *mbox;
1291aa1f5e88Skettenis 	int len;
1292aa1f5e88Skettenis 
1293aa1f5e88Skettenis 	len = OF_getproplen(node, "mboxes");
1294aa1f5e88Skettenis 	if (len <= 0)
1295aa1f5e88Skettenis 		return NULL;
1296aa1f5e88Skettenis 
1297aa1f5e88Skettenis 	mboxes = malloc(len, M_TEMP, M_WAITOK);
1298aa1f5e88Skettenis 	OF_getpropintarray(node, "mboxes", mboxes, len);
1299aa1f5e88Skettenis 
1300aa1f5e88Skettenis 	mbox = mboxes;
1301aa1f5e88Skettenis 	while (mbox && mbox < mboxes + (len / sizeof(uint32_t))) {
1302aa1f5e88Skettenis 		if (idx == 0) {
1303aa1f5e88Skettenis 			mc = mbox_channel_cells(mbox, client);
1304aa1f5e88Skettenis 			break;
1305aa1f5e88Skettenis 		}
1306aa1f5e88Skettenis 		mbox = mbox_next_mbox(mbox);
1307aa1f5e88Skettenis 		idx--;
1308aa1f5e88Skettenis 	}
1309aa1f5e88Skettenis 
1310aa1f5e88Skettenis 	free(mboxes, M_TEMP, len);
1311aa1f5e88Skettenis 	return mc;
1312aa1f5e88Skettenis }
1313aa1f5e88Skettenis 
1314aa1f5e88Skettenis struct mbox_channel *
mbox_channel(int node,const char * name,struct mbox_client * client)1315aa1f5e88Skettenis mbox_channel(int node, const char *name, struct mbox_client *client)
1316aa1f5e88Skettenis {
1317aa1f5e88Skettenis 	int idx;
1318aa1f5e88Skettenis 
1319aa1f5e88Skettenis 	idx = OF_getindex(node, name, "mbox-names");
1320aa1f5e88Skettenis 	if (idx == -1)
1321aa1f5e88Skettenis 		return NULL;
1322aa1f5e88Skettenis 
1323aa1f5e88Skettenis 	return mbox_channel_idx(node, idx, client);
1324aa1f5e88Skettenis }
1325aa1f5e88Skettenis 
1326aa1f5e88Skettenis int
mbox_send(struct mbox_channel * mc,const void * data,size_t len)1327aa1f5e88Skettenis mbox_send(struct mbox_channel *mc, const void *data, size_t len)
1328aa1f5e88Skettenis {
1329aa1f5e88Skettenis 	struct mbox_device *md = mc->mc_md;
1330aa1f5e88Skettenis 
1331aa1f5e88Skettenis 	if (md->md_send)
1332aa1f5e88Skettenis 		return md->md_send(mc->mc_cookie, data, len);
1333aa1f5e88Skettenis 
1334aa1f5e88Skettenis 	return ENXIO;
1335aa1f5e88Skettenis }
1336aa1f5e88Skettenis 
1337aa1f5e88Skettenis int
mbox_recv(struct mbox_channel * mc,void * data,size_t len)1338aa1f5e88Skettenis mbox_recv(struct mbox_channel *mc, void *data, size_t len)
1339aa1f5e88Skettenis {
1340aa1f5e88Skettenis 	struct mbox_device *md = mc->mc_md;
1341aa1f5e88Skettenis 
1342aa1f5e88Skettenis 	if (md->md_recv)
1343aa1f5e88Skettenis 		return md->md_recv(mc->mc_cookie, data, len);
1344aa1f5e88Skettenis 
1345aa1f5e88Skettenis 	return ENXIO;
1346aa1f5e88Skettenis }
1347*949f008fSpatrick 
1348*949f008fSpatrick /* hwlock support */
1349*949f008fSpatrick 
1350*949f008fSpatrick LIST_HEAD(, hwlock_device) hwlock_devices =
1351*949f008fSpatrick 	LIST_HEAD_INITIALIZER(hwlock_devices);
1352*949f008fSpatrick 
1353*949f008fSpatrick void
hwlock_register(struct hwlock_device * hd)1354*949f008fSpatrick hwlock_register(struct hwlock_device *hd)
1355*949f008fSpatrick {
1356*949f008fSpatrick 	hd->hd_cells = OF_getpropint(hd->hd_node, "#hwlock-cells", 0);
1357*949f008fSpatrick 	hd->hd_phandle = OF_getpropint(hd->hd_node, "phandle", 0);
1358*949f008fSpatrick 	if (hd->hd_phandle == 0)
1359*949f008fSpatrick 		return;
1360*949f008fSpatrick 
1361*949f008fSpatrick 	LIST_INSERT_HEAD(&hwlock_devices, hd, hd_list);
1362*949f008fSpatrick }
1363*949f008fSpatrick 
1364*949f008fSpatrick int
hwlock_lock_cells(uint32_t * cells,int lock)1365*949f008fSpatrick hwlock_lock_cells(uint32_t *cells, int lock)
1366*949f008fSpatrick {
1367*949f008fSpatrick 	struct hwlock_device *hd;
1368*949f008fSpatrick 	uint32_t phandle = cells[0];
1369*949f008fSpatrick 
1370*949f008fSpatrick 	LIST_FOREACH(hd, &hwlock_devices, hd_list) {
1371*949f008fSpatrick 		if (hd->hd_phandle == phandle)
1372*949f008fSpatrick 			break;
1373*949f008fSpatrick 	}
1374*949f008fSpatrick 
1375*949f008fSpatrick 	if (hd && hd->hd_lock)
1376*949f008fSpatrick 		return hd->hd_lock(hd->hd_cookie, &cells[1], lock);
1377*949f008fSpatrick 
1378*949f008fSpatrick 	return ENXIO;
1379*949f008fSpatrick }
1380*949f008fSpatrick 
1381*949f008fSpatrick uint32_t *
hwlock_next_hwlock(uint32_t * cells)1382*949f008fSpatrick hwlock_next_hwlock(uint32_t *cells)
1383*949f008fSpatrick {
1384*949f008fSpatrick 	uint32_t phandle = cells[0];
1385*949f008fSpatrick 	int node, ncells;
1386*949f008fSpatrick 
1387*949f008fSpatrick 	node = OF_getnodebyphandle(phandle);
1388*949f008fSpatrick 	if (node == 0)
1389*949f008fSpatrick 		return NULL;
1390*949f008fSpatrick 
1391*949f008fSpatrick 	ncells = OF_getpropint(node, "#hwlock-cells", 0);
1392*949f008fSpatrick 	return cells + ncells + 1;
1393*949f008fSpatrick }
1394*949f008fSpatrick 
1395*949f008fSpatrick int
hwlock_do_lock_idx(int node,int idx,int lock)1396*949f008fSpatrick hwlock_do_lock_idx(int node, int idx, int lock)
1397*949f008fSpatrick {
1398*949f008fSpatrick 	uint32_t *hwlocks;
1399*949f008fSpatrick 	uint32_t *hwlock;
1400*949f008fSpatrick 	int rv = -1;
1401*949f008fSpatrick 	int len;
1402*949f008fSpatrick 
1403*949f008fSpatrick 	len = OF_getproplen(node, "hwlocks");
1404*949f008fSpatrick 	if (len <= 0)
1405*949f008fSpatrick 		return -1;
1406*949f008fSpatrick 
1407*949f008fSpatrick 	hwlocks = malloc(len, M_TEMP, M_WAITOK);
1408*949f008fSpatrick 	OF_getpropintarray(node, "hwlocks", hwlocks, len);
1409*949f008fSpatrick 
1410*949f008fSpatrick 	hwlock = hwlocks;
1411*949f008fSpatrick 	while (hwlock && hwlock < hwlocks + (len / sizeof(uint32_t))) {
1412*949f008fSpatrick 		if (idx <= 0)
1413*949f008fSpatrick 			rv = hwlock_lock_cells(hwlock, lock);
1414*949f008fSpatrick 		if (idx == 0)
1415*949f008fSpatrick 			break;
1416*949f008fSpatrick 		hwlock = hwlock_next_hwlock(hwlock);
1417*949f008fSpatrick 		idx--;
1418*949f008fSpatrick 	}
1419*949f008fSpatrick 
1420*949f008fSpatrick 	free(hwlocks, M_TEMP, len);
1421*949f008fSpatrick 	return rv;
1422*949f008fSpatrick }
1423*949f008fSpatrick 
1424*949f008fSpatrick int
hwlock_lock_idx(int node,int idx)1425*949f008fSpatrick hwlock_lock_idx(int node, int idx)
1426*949f008fSpatrick {
1427*949f008fSpatrick 	return hwlock_do_lock_idx(node, idx, 1);
1428*949f008fSpatrick }
1429*949f008fSpatrick 
1430*949f008fSpatrick int
hwlock_lock_idx_timeout(int node,int idx,int ms)1431*949f008fSpatrick hwlock_lock_idx_timeout(int node, int idx, int ms)
1432*949f008fSpatrick {
1433*949f008fSpatrick 	int i, ret = ENXIO;
1434*949f008fSpatrick 
1435*949f008fSpatrick 	for (i = 0; i <= ms; i++) {
1436*949f008fSpatrick 		ret = hwlock_do_lock_idx(node, idx, 1);
1437*949f008fSpatrick 		if (ret == EAGAIN) {
1438*949f008fSpatrick 			delay(1000);
1439*949f008fSpatrick 			continue;
1440*949f008fSpatrick 		}
1441*949f008fSpatrick 		break;
1442*949f008fSpatrick 	}
1443*949f008fSpatrick 
1444*949f008fSpatrick 	return ret;
1445*949f008fSpatrick }
1446*949f008fSpatrick 
1447*949f008fSpatrick int
hwlock_unlock_idx(int node,int idx)1448*949f008fSpatrick hwlock_unlock_idx(int node, int idx)
1449*949f008fSpatrick {
1450*949f008fSpatrick 	return hwlock_do_lock_idx(node, idx, 0);
1451*949f008fSpatrick }
1452