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, phandle_t cnode, int idx, phy_t *phy) 128 { 129 phandle_t xnode; 130 pcell_t *cells; 131 device_t phydev; 132 int ncells, rv; 133 intptr_t id; 134 135 if (cnode <= 0) 136 cnode = ofw_bus_get_node(consumer_dev); 137 if (cnode <= 0) { 138 device_printf(consumer_dev, 139 "%s called on not ofw based device\n", __func__); 140 return (ENXIO); 141 } 142 rv = ofw_bus_parse_xref_list_alloc(cnode, "phys", "#phy-cells", idx, 143 &xnode, &ncells, &cells); 144 if (rv != 0) 145 return (rv); 146 147 /* Tranlate provider to device. */ 148 phydev = OF_device_from_xref(xnode); 149 if (phydev == NULL) { 150 OF_prop_free(cells); 151 return (ENODEV); 152 } 153 /* Map phy to number. */ 154 rv = PHY_MAP(phydev, xnode, ncells, cells, &id); 155 OF_prop_free(cells); 156 if (rv != 0) 157 return (rv); 158 159 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 160 } 161 162 int 163 phy_get_by_ofw_name(device_t consumer_dev, phandle_t cnode, char *name, 164 phy_t *phy) 165 { 166 int rv, idx; 167 168 if (cnode <= 0) 169 cnode = ofw_bus_get_node(consumer_dev); 170 if (cnode <= 0) { 171 device_printf(consumer_dev, 172 "%s called on not ofw based device\n", __func__); 173 return (ENXIO); 174 } 175 rv = ofw_bus_find_string_index(cnode, "phy-names", name, &idx); 176 if (rv != 0) 177 return (rv); 178 return (phy_get_by_ofw_idx(consumer_dev, cnode, idx, phy)); 179 } 180 181 int 182 phy_get_by_ofw_property(device_t consumer_dev, phandle_t cnode, char *name, 183 phy_t *phy) 184 { 185 pcell_t *cells; 186 device_t phydev; 187 int ncells, rv; 188 intptr_t id; 189 190 if (cnode <= 0) 191 cnode = ofw_bus_get_node(consumer_dev); 192 if (cnode <= 0) { 193 device_printf(consumer_dev, 194 "%s called on not ofw based device\n", __func__); 195 return (ENXIO); 196 } 197 ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t), 198 (void **)&cells); 199 if (ncells < 1) 200 return (ENXIO); 201 202 /* Tranlate provider to device. */ 203 phydev = OF_device_from_xref(cells[0]); 204 if (phydev == NULL) { 205 OF_prop_free(cells); 206 return (ENODEV); 207 } 208 /* Map phy to number. */ 209 rv = PHY_MAP(phydev, cells[0], ncells - 1 , cells + 1, &id); 210 OF_prop_free(cells); 211 if (rv != 0) 212 return (rv); 213 214 return (phy_get_by_id(consumer_dev, phydev, id, phy)); 215 } 216 217 void 218 phy_register_provider(device_t provider_dev) 219 { 220 phandle_t xref, node; 221 222 node = ofw_bus_get_node(provider_dev); 223 if (node <= 0) 224 panic("%s called on not ofw based device.\n", __func__); 225 226 xref = OF_xref_from_node(node); 227 OF_device_register_xref(xref, provider_dev); 228 } 229 230 void 231 phy_unregister_provider(device_t provider_dev) 232 { 233 phandle_t xref; 234 235 xref = OF_xref_from_device(provider_dev); 236 OF_device_register_xref(xref, NULL); 237 } 238 #endif 239