phycommon.c (12323B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: phycommon.c 8 * Purpose: common infrastructure for the various phy methods, 9 * callback registrations, etc. 10 */ 11 #include <soc/drv.h> 12 #include <soc/debug.h> 13 #include <soc/error.h> 14 #include <soc/phyreg.h> 15 16 #include <soc/phy.h> 17 #include <soc/phy/phyctrl.h> 18 #include <soc/phy/drv.h> 19 20 typedef struct phycommon_info_s { 21 /* 22 * Reset overloading tables 23 */ 24 soc_port_phy_reset_cb_t phy_reset_default; 25 soc_port_phy_reset_cb_t phy_reset; 26 void *phy_reset_arg; 27 /* 28 * Medium change notification tables 29 */ 30 soc_port_medium_status_cb_t medium_status_cb; 31 void *medium_status_cb_arg; 32 } phycommon_info_t; 33 34 phycommon_info_t *pi[SOC_MAX_NUM_DEVICES]; 35 36 #define PHYCOMMON_INIT(unit) \ 37 if (pi[unit] == NULL) { return SOC_E_INIT; } 38 39 /* Port translation call-back function */ 40 int (*soc_phy_cb_xlate_port_func)(int, int *); 41 42 /* 43 * Function: 44 * soc_phy_common_detach 45 * Description: 46 * De-allocates memory and initializes data structures needed for common 47 * PHY code 48 * Parameter: 49 * unit -- unit number 50 * Returns: 51 * SOC_E_NONE 52 * SOC_E_MEMORY 53 */ 54 int 55 soc_phy_common_detach(int unit) 56 { 57 sal_free(pi[unit]); 58 pi[unit] = NULL; 59 return SOC_E_NONE; 60 } 61 62 /* 63 * Function: 64 * soc_phy_common_init 65 * Description: 66 * Allocates memory and initializes data structures needed for common 67 * PHY code 68 * Parameter: 69 * unit -- unit number 70 * Returns: 71 * SOC_E_NONE 72 * SOC_E_MEMORY 73 */ 74 int 75 soc_phy_common_init(int unit) 76 { 77 if (pi[unit] == NULL) { 78 pi[unit] = sal_alloc(sizeof(phycommon_info_t) * SOC_MAX_NUM_PORTS, 79 "phycommon_info"); 80 if (pi[unit] == NULL) { 81 return SOC_E_MEMORY; 82 } 83 } 84 85 sal_memset(pi[unit], 0, sizeof(phycommon_info_t) * SOC_MAX_NUM_PORTS); 86 return SOC_E_NONE; 87 } 88 89 /* 90 * Function: 91 * soc_phy_cb_xlate_port 92 * Description: 93 * Provides port translation for API call-backs 94 * Parameter: 95 * unit -- unit number 96 * port -- port number to be translated 97 * Returns: 98 * SOC_E_xxx 99 */ 100 int 101 soc_phy_cb_xlate_port(int unit, int *port) 102 { 103 if (soc_phy_cb_xlate_port_func != NULL) { 104 return soc_phy_cb_xlate_port_func(unit, port); 105 } 106 return SOC_E_NONE; 107 } 108 109 /* 110 * Function: 111 * soc_port_phy_reset_register 112 * Description: 113 * Register a callback function to be called whenever a PHY driver 114 * needs to perform a PHY reset 115 * Parameters: 116 * unit - Device number 117 * port - port number 118 * callback - The callback function to call 119 * user_data - An opaque cookie to pass to callback function 120 * whenever it is called 121 * reg_default -- This parameter should be set to TRUE when a default 122 * reset function is being registered from _bcm_port_init 123 * Returns: 124 * SOC_E_NONE 125 * SOC_E_PARAM -- Bad {unit, port} combination 126 * SOC_E_NOT_FOUND -- The specified {unit, port, callback, user_data} 127 * combination have not been registered before 128 * Notes: 129 * The function registers the reset function for the PHY. During the 130 * system initialization, the function will be used to register the 131 * original, PHY-specific reset function (this will be a standard 132 * phy_fe_ge_reset() or phy_null_reset function) with the reg_default 133 * parameter set to TRUE. The user_data parameter will be ignored in this 134 * case. 135 * Then the user can register another reset function if he wants to (with 136 * reg_default = 0). 137 * This allows us to provide support for the customers that want to do 138 * additional PHY programming at the reset time. 139 * 140 * The curent implementation allows to register only one callback per 141 * {unit, port} 142 */ 143 int 144 soc_phy_reset_register(int unit, 145 soc_port_t port, 146 soc_port_phy_reset_cb_t callback, 147 void *user_data, 148 int reg_default) 149 { 150 int rv; 151 phycommon_info_t *pip; 152 153 PHYCOMMON_INIT(unit); 154 155 pip = &pi[unit][port]; 156 157 if ((0 <= unit && unit < SOC_MAX_NUM_DEVICES) && 158 (0 <= port && port < SOC_MAX_NUM_PORTS) && 159 callback != NULL) { 160 if (reg_default) { 161 pip->phy_reset_default = callback; 162 rv = SOC_E_NONE; 163 } else { 164 if (pip->phy_reset == NULL ) { 165 pip->phy_reset = callback; 166 pip->phy_reset_arg = user_data; 167 rv = SOC_E_NONE; 168 } else { 169 if (pip->phy_reset == callback && 170 pip->phy_reset_arg == user_data) { 171 rv = SOC_E_EXISTS; 172 } else { 173 rv = SOC_E_FULL; 174 } 175 } 176 } 177 } else { 178 rv = SOC_E_PARAM; 179 } 180 181 return (rv); 182 } 183 184 /* 185 * Function: 186 * soc_port_phy_reset_unregister 187 * Description: 188 * Unregister a callback function to be called whenever a PHY driver 189 * needs to perform a PHY reset 190 * Parameters: 191 * unit - Device number 192 * port - port number 193 * callback - The callback function to call 194 * user_data - An opaque cookie to pass to callback function 195 * whenever it is called 196 * Returns: 197 * SOC_E_NONE 198 * SOC_E_PARAM -- Bad {unit, port} combination 199 * SOC_E_NOT_FOUND -- The specified {unit, port, callback, user_data} 200 * combination have not been registered before 201 * SOC_E_EXISTS -- The same callback function with the same parameter 202 * have already been registered 203 * Notes: 204 * This function verifies that the specified callback has really been 205 * registered before. 206 */ 207 int 208 soc_phy_reset_unregister(int unit, 209 soc_port_t port, 210 soc_port_phy_reset_cb_t callback, 211 void *user_data) 212 { 213 int rv; 214 phycommon_info_t *pip; 215 216 PHYCOMMON_INIT(unit); 217 218 pip = &pi[unit][port]; 219 220 221 if ((0 <= unit && unit < SOC_MAX_NUM_DEVICES) && 222 (0 <= port && port < SOC_MAX_NUM_PORTS)) { 223 if ((pip->phy_reset == callback) && 224 (pip->phy_reset_arg == user_data)) { 225 pip->phy_reset = NULL; 226 pip->phy_reset_arg = NULL; 227 rv = SOC_E_NONE; 228 } else { 229 rv = SOC_E_NOT_FOUND; 230 } 231 } else { 232 rv = SOC_E_PARAM; 233 } 234 235 return (rv); 236 } 237 238 /* 239 * Function: 240 * soc_phy_reset 241 * Description: 242 * This functions is called to perform the device reset using the reset 243 * callback function(s) previously registered with soc_phy_reset_register. 244 * Parameters: 245 * unit -- Device number 246 * port -- Port number 247 * Returns: 248 * SOC_E_XXX 249 * Note: 250 * During system initialization the code actually registers the original 251 * PHY reset function. It will be called unless the user had registered 252 * another reset function. 253 */ 254 int 255 soc_phy_reset(int unit, soc_port_t port) 256 { 257 int rv; 258 phycommon_info_t *pip; 259 260 PHYCOMMON_INIT(unit); 261 262 pip = &pi[unit][port]; 263 264 265 if ((0 <= unit && unit < SOC_MAX_NUM_DEVICES) && 266 (0 <= port && port < SOC_MAX_NUM_PORTS)) { 267 if (pip->phy_reset == NULL) { 268 if (pip->phy_reset_default != NULL) { 269 rv = pip->phy_reset_default(unit, port, NULL); 270 } else { 271 rv = SOC_E_INTERNAL; 272 } 273 } else { 274 soc_phy_cb_xlate_port(unit, &port); 275 rv = pip->phy_reset(unit, port, pip->phy_reset_arg); 276 } 277 } else { 278 rv = SOC_E_PARAM; 279 } 280 281 return (rv); 282 } 283 284 /* 285 * Function: 286 * soc_phy_medium_status_register 287 * Description: 288 * Register a callback function to be called on medium change event 289 * Parameters: 290 * unit - Device number 291 * port - port number 292 * callback - The callback function to call 293 * user_data - An opaque cookie to pass to callback function 294 * whenever it is called 295 * Returns: 296 * SOC_E_NONE 297 * SOC_E_PARAM -- NULL function pointer or bad {unit, port} combination 298 * SOC_E_FULL -- Cannot register more than 1 callback per {unit, port} 299 * SOC_E_EXISTS -- The same callback function with the same parameter 300 * have already been registered 301 * Notes: 302 * The current implementation supports only one callback function per 303 * {unit, port} 304 */ 305 int 306 soc_phy_medium_status_register(int unit, 307 soc_port_t port, 308 soc_port_medium_status_cb_t callback, 309 void *user_data) 310 { 311 int rv; 312 phycommon_info_t *pip; 313 314 PHYCOMMON_INIT(unit); 315 316 pip = &pi[unit][port]; 317 318 319 if ((0 <= unit && unit < SOC_MAX_NUM_DEVICES) && 320 (0 <= port && port < SOC_MAX_NUM_PORTS) && 321 callback != NULL) { 322 if (pip->medium_status_cb == NULL) { 323 pip->medium_status_cb = callback; 324 pip->medium_status_cb_arg = user_data; 325 rv = SOC_E_NONE; 326 } else { 327 if (pip->medium_status_cb == callback && 328 pip->medium_status_cb_arg == user_data) { 329 rv = SOC_E_EXISTS; 330 } else { 331 rv = SOC_E_FULL; 332 } 333 } 334 } else { 335 return SOC_E_PARAM; 336 } 337 338 return (rv); 339 } 340 341 /* 342 * Function: 343 * soc_phy_medium_status_unregister 344 * Description: 345 * Unegister a callback function to be called on medium change event 346 * Parameters: 347 * unit - Device number 348 * port - port number 349 * callback - The callback function to call 350 * user_data - An opaque cookie to pass to callback function 351 * whenever it is called 352 * Returns: 353 * SOC_E_NONE 354 * SOC_E_PARAM -- Bad {unit, port} combination 355 * SOC_E_NOT_FOUND -- The specified {unit, port, callback, user_data} 356 * combination have not been registered before 357 */ 358 int 359 soc_phy_medium_status_unregister(int unit, 360 soc_port_t port, 361 soc_port_medium_status_cb_t callback, 362 void *user_data) 363 { 364 int rv; 365 phycommon_info_t *pip; 366 367 PHYCOMMON_INIT(unit); 368 369 pip = &pi[unit][port]; 370 371 if ((0 <= unit && unit < SOC_MAX_NUM_DEVICES) && 372 (0 <= port && port < SOC_MAX_NUM_PORTS)) { 373 if ((pip->medium_status_cb == callback) && 374 (pip->medium_status_cb_arg == user_data)) { 375 pip->medium_status_cb = NULL; 376 pip->medium_status_cb_arg = NULL; 377 rv = SOC_E_NONE; 378 } else { 379 rv = SOC_E_NOT_FOUND; 380 } 381 } else { 382 return SOC_E_PARAM; 383 } 384 385 return (rv); 386 } 387 388 /* 389 * Function: 390 * soc_phy_medium_status_notify 391 * Description: 392 * This function is used to invoke the callback(s) registered via 393 * soc_phy_medium_status_register(). 394 * Parameters: 395 * unit -- Device number 396 * port -- Port number 397 * medium -- New active medium 398 * Returns: 399 * Nothing 400 */ 401 void 402 soc_phy_medium_status_notify(int unit, soc_port_t port, 403 soc_port_medium_t medium) 404 { 405 phycommon_info_t *pip; 406 407 /* 408 * Since this function returns void, we can't use PHYCOMMON_INIT check 409 * here. Replace it with an explicit one 410 */ 411 if (pi[unit] == NULL) { 412 return; 413 } 414 415 pip = &pi[unit][port]; 416 417 if ((0 <= unit && unit < SOC_MAX_NUM_DEVICES) && 418 (0 <= port && port < SOC_MAX_NUM_PORTS) && 419 pip->medium_status_cb != NULL) { 420 pip->medium_status_cb(unit, port, medium, pip->medium_status_cb_arg); 421 } 422 }