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 * $FreeBSD: releng/11.0/sys/dev/extres/phy/phy.c 299714 2016-05-14 04:59:36Z gonzo $ 27 */ 28#include "opt_platform.h" 29#include <sys/cdefs.h> 30#include <sys/param.h> 31#include <sys/kernel.h> 32#include <sys/malloc.h> 33#include <sys/systm.h> 34 35#ifdef FDT 36#include <dev/ofw/ofw_bus.h> 37#include <dev/ofw/ofw_bus_subr.h> 38#endif 39 40#include <dev/extres/phy/phy.h> 41 42#include "phy_if.h" 43 44struct phy { 45 device_t consumer_dev; /* consumer device*/ 46 device_t provider_dev; /* provider device*/ 47 uintptr_t phy_id; /* phy id */ 48}; 49 50MALLOC_DEFINE(M_PHY, "phy", "Phy framework"); 51 52int 53phy_init(device_t consumer, phy_t phy) 54{ 55 56 return (PHY_INIT(phy->provider_dev, phy->phy_id, true)); 57} 58 59int 60phy_deinit(device_t consumer, phy_t phy) 61{ 62 63 return (PHY_INIT(phy->provider_dev, phy->phy_id, false)); 64} 65 66 67int 68phy_enable(device_t consumer, phy_t phy) 69{ 70 71 return (PHY_ENABLE(phy->provider_dev, phy->phy_id, true)); 72} 73 74int 75phy_disable(device_t consumer, phy_t phy) 76{ 77 78 return (PHY_ENABLE(phy->provider_dev, phy->phy_id, false)); 79} 80 81int 82phy_status(device_t consumer, phy_t phy, int *value) 83{ 84 85 return (PHY_STATUS(phy->provider_dev, phy->phy_id, value)); 86} 87 88int 89phy_get_by_id(device_t consumer_dev, device_t provider_dev, intptr_t id, 90 phy_t *phy_out) 91{ 92 phy_t phy; 93 94 /* Create handle */ 95 phy = malloc(sizeof(struct phy), M_PHY, 96 M_WAITOK | M_ZERO); 97 phy->consumer_dev = consumer_dev; 98 phy->provider_dev = provider_dev; 99 phy->phy_id = id; 100 *phy_out = phy; 101 return (0); 102} 103 104void 105phy_release(phy_t phy) 106{ 107 free(phy, M_PHY); 108} 109 110 111#ifdef FDT 112int phy_default_map(device_t provider, phandle_t xref, int ncells, 113 pcell_t *cells, intptr_t *id) 114{ 115 116 if (ncells == 0) 117 *id = 1; 118 else if (ncells == 1) 119 *id = cells[0]; 120 else 121 return (ERANGE); 122 123 return (0); 124} 125 126int 127phy_get_by_ofw_idx(device_t consumer_dev, int idx, phy_t *phy) 128{ 129 phandle_t cnode, xnode; 130 pcell_t *cells; 131 device_t phydev; 132 int ncells, rv; 133 intptr_t id; 134 135 cnode = ofw_bus_get_node(consumer_dev); 136 if (cnode <= 0) { 137 device_printf(consumer_dev, 138 "%s called on not ofw based device\n", __func__); 139 return (ENXIO); 140 } 141 rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx, 142 &xnode, &ncells, &cells); 143 if (rv != 0) 144 return (rv); 145 146 /* Tranlate provider to device. */ 147 phydev = OF_device_from_xref(xnode); 148 if (phydev == NULL) { 149 OF_prop_free(cells); 150 return (ENODEV); 151 } 152 /* Map phy to number. */ 153 rv = PHY_MAP(phydev, xnode, ncells, cells, &id); 154 OF_prop_free(cells); 155 if (rv != 0) 156 return (rv); 157 158 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 159} 160 161int 162phy_get_by_ofw_name(device_t consumer_dev, char *name, phy_t *phy) 163{ 164 int rv, idx; 165 phandle_t cnode; 166 167 cnode = ofw_bus_get_node(consumer_dev); 168 if (cnode <= 0) { 169 device_printf(consumer_dev, 170 "%s called on not ofw based device\n", __func__); 171 return (ENXIO); 172 } 173 rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx); 174 if (rv != 0) 175 return (rv); 176 return (phy_get_by_ofw_idx(consumer_dev, idx, phy)); 177} 178 179int 180phy_get_by_ofw_property(device_t consumer_dev, char *name, phy_t *phy) 181{ 182 phandle_t cnode; 183 pcell_t *cells; 184 device_t phydev; 185 int ncells, rv; 186 intptr_t id; 187 188 cnode = ofw_bus_get_node(consumer_dev); 189 if (cnode <= 0) { 190 device_printf(consumer_dev, 191 "%s called on not ofw based device\n", __func__); 192 return (ENXIO); 193 } 194 ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t), 195 (void **)&cells); 196 if (ncells < 1) 197 return (ENXIO); 198 199 /* Tranlate provider to device. */ 200 phydev = OF_device_from_xref(cells[0]); 201 if (phydev == NULL) { 202 OF_prop_free(cells); 203 return (ENODEV); 204 } 205 /* Map phy to number. */ 206 rv = PHY_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id); 207 OF_prop_free(cells); 208 if (rv != 0) 209 return (rv); 210 211 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 212} 213 214void 215phy_register_provider(device_t provider_dev) 216{ 217 phandle_t xref, node; 218 219 node = ofw_bus_get_node(provider_dev); 220 if (node <= 0) 221 panic("%s called on not ofw based device.\n", __func__); 222 223 xref = OF_xref_from_node(node); 224 OF_device_register_xref(xref, provider_dev); 225} 226 227void 228phy_unregister_provider(device_t provider_dev) 229{ 230 phandle_t xref; 231 232 xref = OF_xref_from_device(provider_dev); 233 OF_device_register_xref(xref, NULL); 234} 235#endif 236