1 /*- 2 * Copyright 2016 Michal Meloun <[email protected]> 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$ 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 44 struct phy { 45 device_t consumer_dev; /* consumer device*/ 46 device_t provider_dev; /* provider device*/ 47 uintptr_t phy_id; /* phy id */ 48 }; 49 50 MALLOC_DEFINE(M_PHY, "phy", "Phy framework"); 51 52 int 53 phy_init(device_t consumer, phy_t phy) 54 { 55 56 return (PHY_INIT(phy->provider_dev, phy->phy_id, true)); 57 } 58 59 int 60 phy_deinit(device_t consumer, phy_t phy) 61 { 62 63 return (PHY_INIT(phy->provider_dev, phy->phy_id, false)); 64 } 65 66 67 int 68 phy_enable(device_t consumer, phy_t phy) 69 { 70 71 return (PHY_ENABLE(phy->provider_dev, phy->phy_id, true)); 72 } 73 74 int 75 phy_disable(device_t consumer, phy_t phy) 76 { 77 78 return (PHY_ENABLE(phy->provider_dev, phy->phy_id, false)); 79 } 80 81 int 82 phy_status(device_t consumer, phy_t phy, int *value) 83 { 84 85 return (PHY_STATUS(phy->provider_dev, phy->phy_id, value)); 86 } 87 88 int 89 phy_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 104 void 105 phy_release(phy_t phy) 106 { 107 free(phy, M_PHY); 108 } 109 110 111 #ifdef FDT 112 int 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 126 int 127 phy_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 161 int 162 phy_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 179 int 180 phy_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 214 void 215 phy_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 227 void 228 phy_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