1 /* 2 * ASIX AX88172/AX88772/AX88178 USB 2.0 Ethernet Driver. 3 * Copyright (c) 2008, 2011 S.Zharski <imker@gmx.li> 4 * Distributed under the terms of the MIT license. 5 * 6 * Heavily based on code of the 7 * Driver for USB Ethernet Control Model devices 8 * Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch> 9 * Distributed under the terms of the MIT license. 10 * 11 */ 12 13 14 #include "MIIBus.h" 15 16 #include "ASIXVendorRequests.h" 17 #include "Driver.h" 18 #include "Settings.h" 19 20 21 #define MII_OUI(id1, id2) (((id1) << 6) | ((id2) >> 10)) 22 #define MII_MODEL(id2) (((id2) & 0x03f0) >> 4) 23 #define MII_REV(id2) ((id2) & 0x000f) 24 25 26 MIIBus::MIIBus() 27 : 28 fStatus(B_NO_INIT), 29 fDevice(0), 30 fSelectedPHY(CurrentPHY) 31 { 32 for (size_t i = 0; i < PHYsCount; i++) { 33 fPHYs[i] = PHYNotInstalled; 34 } 35 } 36 37 38 status_t 39 MIIBus::Init(usb_device device) 40 { 41 // reset to default state 42 fDevice = 0; 43 fSelectedPHY = CurrentPHY; 44 for (size_t i = 0; i < PHYsCount; i++) { 45 fPHYs[i] = PHYNotInstalled; 46 } 47 48 size_t actual_length = 0; 49 status_t result = gUSBModule->send_request(device, 50 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_PHYID, 0, 0, 51 sizeof(fPHYs), fPHYs, &actual_length); 52 53 if (result != B_OK) { 54 TRACE_ALWAYS("Request of the PHYIDs failed:%#010x\n", result); 55 return result; 56 } 57 58 if (sizeof(fPHYs) != actual_length) { 59 TRACE_ALWAYS("Mismatch of reading %d PHYIDs bytes instead of %d.\n", 60 actual_length, sizeof(fPHYs)); 61 } 62 63 TRACE("PHYIDs are:%#02x:%#02x\n", fPHYs[0], fPHYs[1]); 64 65 // simply tactic - we use first available PHY 66 if (PHYType(PrimaryPHY) != PHYNotInstalled) { 67 fSelectedPHY = PrimaryPHY; 68 } else 69 if (PHYType(SecondaryPHY) != PHYNotInstalled) { 70 fSelectedPHY = SecondaryPHY; 71 } 72 73 TRACE("PHYs are configured: Selected:%#02x; Primary:%#02x; 2ndary:%#02x\n", 74 PHYID(CurrentPHY), PHYID(PrimaryPHY), PHYID(SecondaryPHY)); 75 if (fSelectedPHY == CurrentPHY) { 76 TRACE_ALWAYS("No PHYs found!\n"); 77 return B_ENTRY_NOT_FOUND; 78 } 79 80 fDevice = device; 81 fStatus = result; 82 83 return fStatus; 84 } 85 86 87 status_t 88 MIIBus::SetupPHY() 89 { 90 uint16 control = 0; 91 status_t result = Read(MII_BMCR, &control); 92 if (result != B_OK) { 93 TRACE_ALWAYS("Error of reading control word:%#010x.\n", result); 94 return result; 95 } 96 97 TRACE("MII Control word is %#04x\n", control); 98 99 control &= ~BMCR_Isolate; 100 result = Write(MII_BMCR, control); 101 if (result != B_OK) { 102 TRACE_ALWAYS("Error of writing control word %#04x:%#010x.\n", 103 control, result); 104 } 105 106 result = Write(MII_BMCR, BMCR_Reset); 107 if (result != B_OK) { 108 TRACE_ALWAYS("Error of resetting PHY:%#010x.\n", result); 109 } 110 111 uint16 id01 = 0, id02 = 0; 112 result = Read(MII_PHYID0, &id01); 113 if (result != B_OK) { 114 TRACE_ALWAYS("Error of reading PHY ID1:%#010x.\n", result); 115 } 116 117 result = Read(MII_PHYID1, &id02); 118 if (result != B_OK) { 119 TRACE_ALWAYS("Error of reading PHY ID2:%#010x.\n", result); 120 } 121 122 TRACE("MII Info: OUI:%04x; Model:%04x; rev:%02x.\n", 123 MII_OUI(id01, id02), MII_MODEL(id02), MII_REV(id02)); 124 125 // Dump(); 126 127 return result; 128 } 129 130 131 status_t 132 MIIBus::InitCheck() 133 { 134 if (fSelectedPHY == CurrentPHY) { 135 return B_ENTRY_NOT_FOUND; 136 } 137 138 return fStatus; 139 } 140 141 142 uint8 143 MIIBus::PHYID(PHYIndex phyIndex /*= CurrentPHY*/) 144 { 145 if (phyIndex == CurrentPHY) { 146 return (fSelectedPHY == CurrentPHY 147 ? 0 : fPHYs[fSelectedPHY]) & PHYIDMask; 148 } 149 150 return fPHYs[phyIndex] & PHYIDMask; 151 } 152 153 154 uint8 155 MIIBus::PHYType(PHYIndex phyIndex /*= CurrentPHY*/) 156 { 157 if (phyIndex == CurrentPHY) { 158 return (fSelectedPHY == CurrentPHY 159 ? PHYNotInstalled : fPHYs[fSelectedPHY]) & PHYTypeMask; 160 } 161 162 return fPHYs[phyIndex] & PHYTypeMask; 163 } 164 165 166 status_t 167 MIIBus::Read(uint16 miiRegister, uint16 *value, PHYIndex phyIndex /*= CurrPHY*/) 168 { 169 status_t result = InitCheck(); 170 if (B_OK != result) { 171 TRACE_ALWAYS("Error: MII is not ready:%#010x\n", result); 172 return result; 173 } 174 175 if (PHYType(phyIndex) == PHYNotInstalled) { 176 TRACE_ALWAYS("Error: Invalid PHY index:%#02x.\n", phyIndex); 177 return B_ENTRY_NOT_FOUND; 178 } 179 180 uint16 phyId = PHYID(phyIndex); 181 182 size_t actual_length = 0; 183 // switch to SW operation mode 184 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 185 | USB_REQTYPE_DEVICE_OUT, SW_MII_OP, 0, 0, 0, 0, &actual_length); 186 187 if (result != B_OK) { 188 TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result); 189 return result; 190 } 191 192 // read register value 193 status_t op_result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 194 | USB_REQTYPE_DEVICE_IN, READ_MII, phyId, miiRegister, sizeof(*value), 195 value, &actual_length); 196 197 if (op_result != B_OK) { 198 TRACE_ALWAYS("Error of reading MII reg.%d at PHY%d:%#010x.\n", 199 miiRegister, phyId, op_result); 200 } 201 202 if (sizeof(*value) != actual_length) { 203 TRACE_ALWAYS("Mismatch of reading MII reg.%d at PHY %d. " 204 "Read %d bytes instead of %d.\n", miiRegister, phyId, 205 actual_length, sizeof(*value)); 206 } 207 208 // switch to HW operation mode 209 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 210 | USB_REQTYPE_DEVICE_OUT, HW_MII_OP, 0, 0, 0, 0, &actual_length); 211 212 if (result != B_OK) { 213 TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result); 214 } 215 216 return op_result; 217 } 218 219 220 status_t 221 MIIBus::Write(uint16 miiRegister, uint16 value, PHYIndex phyIndex /*= CurrPHY*/) 222 { 223 size_t actual_length = 0; 224 225 status_t result = InitCheck(); 226 if (B_OK != result) { 227 TRACE_ALWAYS("Error: MII is not ready:%#010x\n", result); 228 return result; 229 } 230 231 if (PHYType(phyIndex) == PHYNotInstalled) { 232 TRACE_ALWAYS("Error: Invalid PHY index:%#02x\n", phyIndex); 233 return B_ENTRY_NOT_FOUND; 234 } 235 236 uint16 phyId = PHYID(phyIndex); 237 238 // switch to SW operation mode 239 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 240 | USB_REQTYPE_DEVICE_OUT, SW_MII_OP, 0, 0, 0, 0, &actual_length); 241 242 if (result != B_OK) { 243 TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result); 244 return result; 245 } 246 247 // write register value 248 status_t op_result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 249 | USB_REQTYPE_DEVICE_OUT, WRITE_MII, phyId, miiRegister, sizeof(value), 250 &value, &actual_length); 251 252 if (op_result != B_OK) { 253 TRACE_ALWAYS("Error of writing MII reg.%d at PHY %d:%#010x.\n", 254 miiRegister, phyId, op_result); 255 } 256 257 if (sizeof(value) != actual_length) { 258 TRACE_ALWAYS("Mismatch of writing MII reg.%d at PHY %d." 259 "Write %d bytes instead of %d.\n", miiRegister, phyId, 260 actual_length, sizeof(value)); 261 } 262 263 // switch to HW operation mode 264 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 265 | USB_REQTYPE_DEVICE_OUT, HW_MII_OP, 0, 0, 0, 0, &actual_length); 266 267 if (result != B_OK) { 268 TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result); 269 } 270 271 return op_result; 272 } 273 274 275 status_t 276 MIIBus::Status(uint16 *status, PHYIndex phyIndex /*= CurrentPHY*/) 277 { 278 return Read(MII_BMSR, status, phyIndex); 279 } 280 281 282 status_t 283 MIIBus::Dump() 284 { 285 status_t result = InitCheck(); 286 if (B_OK != result) { 287 TRACE_ALWAYS("Error: MII is not ready:%#010x.\n", result); 288 return result; 289 } 290 291 if (PHYType(CurrentPHY) == PHYNotInstalled) { 292 TRACE_ALWAYS("Error: Current PHY index is invalid!\n"); 293 return B_ENTRY_NOT_FOUND; 294 } 295 296 uint16 phyId = PHYID(CurrentPHY); 297 298 size_t actual_length = 0; 299 // switch to SW operation mode 300 result = gUSBModule->send_request(fDevice, 301 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, 302 SW_MII_OP, 0, 0, 0, 0, &actual_length); 303 304 if (result != B_OK) { 305 TRACE_ALWAYS("Error of switching MII to SW op.mode: %#010x\n", result); 306 return result; 307 } 308 309 uint8 regs[] = { MII_BMCR, MII_BMSR, MII_PHYID0, 310 MII_PHYID1, MII_ANAR, MII_ANLPAR/*, MII_ANER*/}; 311 uint16 value = 0; 312 for (size_t i = 0; i < sizeof(regs)/ sizeof(regs[0]); i++) { 313 314 // read register value 315 status_t op_result = gUSBModule->send_request(fDevice, 316 USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, READ_MII, phyId, 317 regs[i], sizeof(value), &value, &actual_length); 318 319 if (op_result != B_OK) { 320 TRACE_ALWAYS("Error of reading MII reg.%d at PHY%d:%#010x.\n", 321 regs[i], phyId, op_result); 322 } 323 324 if (sizeof(value) != actual_length) { 325 TRACE_ALWAYS("Mismatch of reading MII reg.%d at PHY%d." 326 " Read %d bytes instead of %d.\n", regs[i], phyId, 327 actual_length, sizeof(value)); 328 } 329 330 TRACE_ALWAYS("MII reg: %d has %#04x\n", regs[i], value); 331 } 332 333 // switch to HW operation mode 334 result = gUSBModule->send_request(fDevice, USB_REQTYPE_VENDOR 335 | USB_REQTYPE_DEVICE_OUT, HW_MII_OP, 0, 0, 0, 0, &actual_length); 336 337 if (result != B_OK) { 338 TRACE_ALWAYS("Error of switching MII to HW op.mode: %#010x\n", result); 339 } 340 341 return result; 342 343 } 344