shbde_mdio.c (5166B)
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 8 #include <shbde_mdio.h> 9 10 /* iProc MDIO register offset */ 11 #define MII_MGMT_CTRL 0x0 12 #define MII_MGMT_CMD_DATA 0x4 13 14 /* iProc MII register with fields definition */ 15 #define MII_MGMT_CTRLr_MDCDIVf_SHFT 0 16 #define MII_MGMT_CTRLr_MDCDIVf_MASK 0x7f 17 #define MII_MGMT_CTRLr_BSYf_SHFT 8 18 #define MII_MGMT_CTRLr_BSYf_MASK 0x1 19 20 #define MII_MGMT_CMD_DATAr_DATAf_SHFT 0 21 #define MII_MGMT_CMD_DATAr_DATAf_MASK 0xffff 22 #define MII_MGMT_CMD_DATAr_TAf_SHFT 16 23 #define MII_MGMT_CMD_DATAr_TAf_MASK 0x3 24 #define MII_MGMT_CMD_DATAr_RAf_SHFT 18 25 #define MII_MGMT_CMD_DATAr_RAf_MASK 0x1f 26 #define MII_MGMT_CMD_DATAr_PAf_SHFT 23 27 #define MII_MGMT_CMD_DATAr_PAf_MASK 0x1f 28 #define MII_MGMT_CMD_DATAr_OPf_SHFT 28 29 #define MII_MGMT_CMD_DATAr_OPf_MASK 0x3 30 #define MII_MGMT_CMD_DATAr_SBf_SHFT 30 31 #define MII_MGMT_CMD_DATAr_SBf_MASK 0x3 32 33 /* Register field value set/get */ 34 #define REG_FIELD_SET(_r, _f, _r_val, _f_val) \ 35 _r_val = ((_r_val) & ~(_r##_##_f##_MASK << _r##_##_f##_SHFT)) | \ 36 (((_f_val) & _r##_##_f##_MASK) << _r##_##_f##_SHFT) 37 #define REG_FIELD_GET(_r, _f, _r_val) \ 38 (((_r_val) >> _r##_##_f##_SHFT) & _r##_##_f##_MASK) 39 40 #define LOG_OUT(_shbde, _lvl, _str, _prm) \ 41 if ((_shbde)->log_func) { \ 42 (_shbde)->log_func(_lvl, _str, _prm); \ 43 } 44 #define LOG_ERR(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_ERR, _str, _prm) 45 #define LOG_WARN(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_WARN, _str, _prm) 46 #define LOG_DBG(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_DBG, _str, _prm) 47 48 static unsigned int 49 mdio32_read(shbde_mdio_ctrl_t *smc, unsigned int offset) 50 { 51 if (!smc || !smc->io32_read) { 52 return 0; 53 } 54 return smc->io32_read(smc->shbde, smc->regs, smc->base_addr + offset); 55 } 56 57 static void 58 mdio32_write(shbde_mdio_ctrl_t *smc, unsigned int offset, unsigned int data) 59 { 60 if (!smc || !smc->io32_read) { 61 return; 62 } 63 smc->io32_write(smc->shbde, smc->regs, smc->base_addr + offset, data); 64 } 65 66 static void 67 wait_usec(shbde_mdio_ctrl_t *smc, int usec) 68 { 69 shbde_hal_t *shbde = smc->shbde; 70 71 if (shbde && shbde->usleep) { 72 shbde->usleep(usec); 73 } else { 74 int idx; 75 volatile int count; 76 for (idx = 0; idx < usec; idx++) { 77 for (count = 0; count < 100; count++); 78 } 79 } 80 } 81 82 static int 83 iproc_mdio_wait_for_busy(shbde_mdio_ctrl_t *smc) 84 { 85 int mii_busy; 86 unsigned int reg_val; 87 int count = 1000; 88 89 /* Wait until MII is not busy */ 90 do { 91 reg_val = mdio32_read(smc, MII_MGMT_CTRL); 92 mii_busy = REG_FIELD_GET(MII_MGMT_CTRLr, BSYf, reg_val); 93 if (!mii_busy) { 94 break; 95 } 96 wait_usec(smc, 10); 97 count --; 98 } while (count > 0); 99 100 return mii_busy; 101 } 102 103 int 104 shbde_iproc_mdio_init(shbde_mdio_ctrl_t *smc) 105 { 106 shbde_hal_t *shbde = smc->shbde; 107 unsigned int reg_val = 0; 108 109 /* Enable the iProc internal MDIO interface */ 110 REG_FIELD_SET(MII_MGMT_CTRLr, MDCDIVf, reg_val, 0x7f); 111 mdio32_write(smc, MII_MGMT_CTRL, reg_val); 112 113 if (shbde && !shbde->usleep) { 114 LOG_DBG(shbde, "shbde_mdio: no registration of usleep vector", 0); 115 } 116 117 wait_usec(smc, 100); 118 119 return 0; 120 } 121 122 int 123 shbde_iproc_mdio_read(shbde_mdio_ctrl_t *smc, unsigned int phy_addr, 124 unsigned int reg, unsigned int *val) 125 { 126 unsigned int reg_val = 0; 127 128 REG_FIELD_SET(MII_MGMT_CMD_DATAr, SBf, reg_val, 0x1); 129 REG_FIELD_SET(MII_MGMT_CMD_DATAr, TAf, reg_val, 0x2); 130 REG_FIELD_SET(MII_MGMT_CMD_DATAr, OPf, reg_val, 0x2); 131 REG_FIELD_SET(MII_MGMT_CMD_DATAr, PAf, reg_val, phy_addr); 132 REG_FIELD_SET(MII_MGMT_CMD_DATAr, RAf, reg_val, reg); 133 mdio32_write(smc, MII_MGMT_CMD_DATA, reg_val); 134 135 if (iproc_mdio_wait_for_busy(smc)) { 136 *val = 0; 137 LOG_DBG(smc->shbde, "shbde_iproc_mdio_read busy", reg); 138 return -1; 139 } 140 141 reg_val = mdio32_read(smc, MII_MGMT_CMD_DATA); 142 *val = REG_FIELD_GET(MII_MGMT_CMD_DATAr, DATAf, reg_val); 143 144 return 0; 145 } 146 147 int 148 shbde_iproc_mdio_write(shbde_mdio_ctrl_t *smc, unsigned int phy_addr, 149 unsigned int reg, unsigned int val) 150 { 151 unsigned int reg_val = 0; 152 153 REG_FIELD_SET(MII_MGMT_CMD_DATAr, SBf, reg_val, 0x1); 154 REG_FIELD_SET(MII_MGMT_CMD_DATAr, TAf, reg_val, 0x2); 155 REG_FIELD_SET(MII_MGMT_CMD_DATAr, OPf, reg_val, 0x1); 156 REG_FIELD_SET(MII_MGMT_CMD_DATAr, PAf, reg_val, phy_addr); 157 REG_FIELD_SET(MII_MGMT_CMD_DATAr, RAf, reg_val, reg); 158 REG_FIELD_SET(MII_MGMT_CMD_DATAr, DATAf, reg_val, val); 159 mdio32_write(smc, MII_MGMT_CMD_DATA, reg_val); 160 161 if (iproc_mdio_wait_for_busy(smc)) { 162 LOG_DBG(smc->shbde, "shbde_iproc_mdio_write busy", reg); 163 return -1; 164 } 165 166 /* Wait for some time for the write to take effect */ 167 wait_usec(smc, 100); 168 169 return 0; 170 } 171