1 /* 2 * Nuvoton NPCM8xx PCS Module 3 * 4 * Copyright 2022 Google LLC 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * for more details. 15 */ 16 17 /* 18 * Disclaimer: 19 * Currently we only implemented the default values of the registers and 20 * the soft reset feature. These are required to boot up the GMAC module 21 * in Linux kernel for NPCM845 boards. Other functionalities are not modeled. 22 */ 23 24 #include "qemu/osdep.h" 25 26 #include "exec/hwaddr.h" 27 #include "hw/registerfields.h" 28 #include "hw/net/npcm_pcs.h" 29 #include "migration/vmstate.h" 30 #include "qemu/log.h" 31 #include "qemu/units.h" 32 #include "trace.h" 33 34 #define NPCM_PCS_IND_AC_BA 0x1fe 35 #define NPCM_PCS_IND_SR_CTL 0x1e00 36 #define NPCM_PCS_IND_SR_MII 0x1f00 37 #define NPCM_PCS_IND_SR_TIM 0x1f07 38 #define NPCM_PCS_IND_VR_MII 0x1f80 39 40 REG16(NPCM_PCS_SR_CTL_ID1, 0x08) 41 REG16(NPCM_PCS_SR_CTL_ID2, 0x0a) 42 REG16(NPCM_PCS_SR_CTL_STS, 0x10) 43 44 REG16(NPCM_PCS_SR_MII_CTRL, 0x00) 45 REG16(NPCM_PCS_SR_MII_STS, 0x02) 46 REG16(NPCM_PCS_SR_MII_DEV_ID1, 0x04) 47 REG16(NPCM_PCS_SR_MII_DEV_ID2, 0x06) 48 REG16(NPCM_PCS_SR_MII_AN_ADV, 0x08) 49 REG16(NPCM_PCS_SR_MII_LP_BABL, 0x0a) 50 REG16(NPCM_PCS_SR_MII_AN_EXPN, 0x0c) 51 REG16(NPCM_PCS_SR_MII_EXT_STS, 0x1e) 52 53 REG16(NPCM_PCS_SR_TIM_SYNC_ABL, 0x10) 54 REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x12) 55 REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0x14) 56 REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x16) 57 REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0x18) 58 REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x1a) 59 REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0x1c) 60 REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x1e) 61 REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0x20) 62 63 REG16(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x000) 64 REG16(NPCM_PCS_VR_MII_AN_CTRL, 0x002) 65 REG16(NPCM_PCS_VR_MII_AN_INTR_STS, 0x004) 66 REG16(NPCM_PCS_VR_MII_TC, 0x006) 67 REG16(NPCM_PCS_VR_MII_DBG_CTRL, 0x00a) 68 REG16(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x00c) 69 REG16(NPCM_PCS_VR_MII_EEE_TXTIMER, 0x010) 70 REG16(NPCM_PCS_VR_MII_EEE_RXTIMER, 0x012) 71 REG16(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0x014) 72 REG16(NPCM_PCS_VR_MII_EEE_MCTRL1, 0x016) 73 REG16(NPCM_PCS_VR_MII_DIG_STS, 0x020) 74 REG16(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0x022) 75 REG16(NPCM_PCS_VR_MII_MISC_STS, 0x030) 76 REG16(NPCM_PCS_VR_MII_RX_LSTS, 0x040) 77 REG16(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x070) 78 REG16(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x074) 79 REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x07a) 80 REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0x07c) 81 REG16(NPCM_PCS_VR_MII_MP_TX_STS, 0x090) 82 REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0b0) 83 REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x0b2) 84 REG16(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x0ba) 85 REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0f0) 86 REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0f2) 87 REG16(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x110) 88 REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0x126) 89 REG16(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x130) 90 REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0x132) 91 REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0x134) 92 REG16(NPCM_PCS_VR_MII_DIG_CTRL2, 0x1c2) 93 REG16(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0x1c4) 94 95 /* Register Fields */ 96 #define NPCM_PCS_SR_MII_CTRL_RST BIT(15) 97 98 static const uint16_t npcm_pcs_sr_ctl_cold_reset_values[NPCM_PCS_NR_SR_CTLS] = { 99 [R_NPCM_PCS_SR_CTL_ID1] = 0x699e, 100 [R_NPCM_PCS_SR_CTL_STS] = 0x8000, 101 }; 102 103 static const uint16_t npcm_pcs_sr_mii_cold_reset_values[NPCM_PCS_NR_SR_MIIS] = { 104 [R_NPCM_PCS_SR_MII_CTRL] = 0x1140, 105 [R_NPCM_PCS_SR_MII_STS] = 0x0109, 106 [R_NPCM_PCS_SR_MII_DEV_ID1] = 0x699e, 107 [R_NPCM_PCS_SR_MII_DEV_ID2] = 0xced0, 108 [R_NPCM_PCS_SR_MII_AN_ADV] = 0x0020, 109 [R_NPCM_PCS_SR_MII_EXT_STS] = 0xc000, 110 }; 111 112 static const uint16_t npcm_pcs_sr_tim_cold_reset_values[NPCM_PCS_NR_SR_TIMS] = { 113 [R_NPCM_PCS_SR_TIM_SYNC_ABL] = 0x0003, 114 [R_NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR] = 0x0038, 115 [R_NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR] = 0x0038, 116 [R_NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR] = 0x0058, 117 [R_NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR] = 0x0048, 118 }; 119 120 static const uint16_t npcm_pcs_vr_mii_cold_reset_values[NPCM_PCS_NR_VR_MIIS] = { 121 [R_NPCM_PCS_VR_MII_MMD_DIG_CTRL1] = 0x2400, 122 [R_NPCM_PCS_VR_MII_AN_INTR_STS] = 0x000a, 123 [R_NPCM_PCS_VR_MII_EEE_MCTRL0] = 0x899c, 124 [R_NPCM_PCS_VR_MII_DIG_STS] = 0x0010, 125 [R_NPCM_PCS_VR_MII_MP_TX_BSTCTRL0] = 0x000a, 126 [R_NPCM_PCS_VR_MII_MP_TX_LVLCTRL0] = 0x007f, 127 [R_NPCM_PCS_VR_MII_MP_TX_GENCTRL0] = 0x0001, 128 [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL0] = 0x0100, 129 [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL1] = 0x1100, 130 [R_NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0] = 0x000e, 131 [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL0] = 0x0100, 132 [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL1] = 0x0032, 133 [R_NPCM_PCS_VR_MII_MP_MPLL_STS] = 0x0001, 134 [R_NPCM_PCS_VR_MII_MP_LVL_CTRL] = 0x0019, 135 }; 136 137 static void npcm_pcs_soft_reset(NPCMPCSState *s) 138 { 139 memcpy(s->sr_ctl, npcm_pcs_sr_ctl_cold_reset_values, 140 NPCM_PCS_NR_SR_CTLS * sizeof(uint16_t)); 141 memcpy(s->sr_mii, npcm_pcs_sr_mii_cold_reset_values, 142 NPCM_PCS_NR_SR_MIIS * sizeof(uint16_t)); 143 memcpy(s->sr_tim, npcm_pcs_sr_tim_cold_reset_values, 144 NPCM_PCS_NR_SR_TIMS * sizeof(uint16_t)); 145 memcpy(s->vr_mii, npcm_pcs_vr_mii_cold_reset_values, 146 NPCM_PCS_NR_VR_MIIS * sizeof(uint16_t)); 147 } 148 149 static uint16_t npcm_pcs_read_sr_ctl(NPCMPCSState *s, hwaddr offset) 150 { 151 hwaddr regno = offset / sizeof(uint16_t); 152 153 if (regno >= NPCM_PCS_NR_SR_CTLS) { 154 qemu_log_mask(LOG_GUEST_ERROR, 155 "%s: SR_CTL read offset 0x%04" HWADDR_PRIx 156 " is out of range.\n", 157 DEVICE(s)->canonical_path, offset); 158 return 0; 159 } 160 161 return s->sr_ctl[regno]; 162 } 163 164 static uint16_t npcm_pcs_read_sr_mii(NPCMPCSState *s, hwaddr offset) 165 { 166 hwaddr regno = offset / sizeof(uint16_t); 167 168 if (regno >= NPCM_PCS_NR_SR_MIIS) { 169 qemu_log_mask(LOG_GUEST_ERROR, 170 "%s: SR_MII read offset 0x%04" HWADDR_PRIx 171 " is out of range.\n", 172 DEVICE(s)->canonical_path, offset); 173 return 0; 174 } 175 176 return s->sr_mii[regno]; 177 } 178 179 static uint16_t npcm_pcs_read_sr_tim(NPCMPCSState *s, hwaddr offset) 180 { 181 hwaddr regno = offset / sizeof(uint16_t); 182 183 if (regno >= NPCM_PCS_NR_SR_TIMS) { 184 qemu_log_mask(LOG_GUEST_ERROR, 185 "%s: SR_TIM read offset 0x%04" HWADDR_PRIx 186 " is out of range.\n", 187 DEVICE(s)->canonical_path, offset); 188 return 0; 189 } 190 191 return s->sr_tim[regno]; 192 } 193 194 static uint16_t npcm_pcs_read_vr_mii(NPCMPCSState *s, hwaddr offset) 195 { 196 hwaddr regno = offset / sizeof(uint16_t); 197 198 if (regno >= NPCM_PCS_NR_VR_MIIS) { 199 qemu_log_mask(LOG_GUEST_ERROR, 200 "%s: VR_MII read offset 0x%04" HWADDR_PRIx 201 " is out of range.\n", 202 DEVICE(s)->canonical_path, offset); 203 return 0; 204 } 205 206 return s->vr_mii[regno]; 207 } 208 209 static void npcm_pcs_write_sr_ctl(NPCMPCSState *s, hwaddr offset, uint16_t v) 210 { 211 hwaddr regno = offset / sizeof(uint16_t); 212 213 if (regno >= NPCM_PCS_NR_SR_CTLS) { 214 qemu_log_mask(LOG_GUEST_ERROR, 215 "%s: SR_CTL write offset 0x%04" HWADDR_PRIx 216 " is out of range.\n", 217 DEVICE(s)->canonical_path, offset); 218 return; 219 } 220 221 s->sr_ctl[regno] = v; 222 } 223 224 static void npcm_pcs_write_sr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v) 225 { 226 hwaddr regno = offset / sizeof(uint16_t); 227 228 if (regno >= NPCM_PCS_NR_SR_MIIS) { 229 qemu_log_mask(LOG_GUEST_ERROR, 230 "%s: SR_MII write offset 0x%04" HWADDR_PRIx 231 " is out of range.\n", 232 DEVICE(s)->canonical_path, offset); 233 return; 234 } 235 236 s->sr_mii[regno] = v; 237 238 if ((offset == A_NPCM_PCS_SR_MII_CTRL) && (v & NPCM_PCS_SR_MII_CTRL_RST)) { 239 /* Trigger a soft reset */ 240 npcm_pcs_soft_reset(s); 241 } 242 } 243 244 static void npcm_pcs_write_sr_tim(NPCMPCSState *s, hwaddr offset, uint16_t v) 245 { 246 hwaddr regno = offset / sizeof(uint16_t); 247 248 if (regno >= NPCM_PCS_NR_SR_TIMS) { 249 qemu_log_mask(LOG_GUEST_ERROR, 250 "%s: SR_TIM write offset 0x%04" HWADDR_PRIx 251 " is out of range.\n", 252 DEVICE(s)->canonical_path, offset); 253 return; 254 } 255 256 s->sr_tim[regno] = v; 257 } 258 259 static void npcm_pcs_write_vr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v) 260 { 261 hwaddr regno = offset / sizeof(uint16_t); 262 263 if (regno >= NPCM_PCS_NR_VR_MIIS) { 264 qemu_log_mask(LOG_GUEST_ERROR, 265 "%s: VR_MII write offset 0x%04" HWADDR_PRIx 266 " is out of range.\n", 267 DEVICE(s)->canonical_path, offset); 268 return; 269 } 270 271 s->vr_mii[regno] = v; 272 } 273 274 static uint64_t npcm_pcs_read(void *opaque, hwaddr offset, unsigned size) 275 { 276 NPCMPCSState *s = opaque; 277 uint16_t v = 0; 278 279 if (offset == NPCM_PCS_IND_AC_BA) { 280 v = s->indirect_access_base; 281 } else { 282 switch (s->indirect_access_base) { 283 case NPCM_PCS_IND_SR_CTL: 284 v = npcm_pcs_read_sr_ctl(s, offset); 285 break; 286 287 case NPCM_PCS_IND_SR_MII: 288 v = npcm_pcs_read_sr_mii(s, offset); 289 break; 290 291 case NPCM_PCS_IND_SR_TIM: 292 v = npcm_pcs_read_sr_tim(s, offset); 293 break; 294 295 case NPCM_PCS_IND_VR_MII: 296 v = npcm_pcs_read_vr_mii(s, offset); 297 break; 298 299 default: 300 qemu_log_mask(LOG_GUEST_ERROR, 301 "%s: Read with invalid indirect address base: 0x%" 302 PRIx16 "\n", DEVICE(s)->canonical_path, 303 s->indirect_access_base); 304 } 305 } 306 307 trace_npcm_pcs_reg_read(DEVICE(s)->canonical_path, s->indirect_access_base, 308 offset, v); 309 return v; 310 } 311 312 static void npcm_pcs_write(void *opaque, hwaddr offset, 313 uint64_t v, unsigned size) 314 { 315 NPCMPCSState *s = opaque; 316 317 trace_npcm_pcs_reg_write(DEVICE(s)->canonical_path, s->indirect_access_base, 318 offset, v); 319 if (offset == NPCM_PCS_IND_AC_BA) { 320 s->indirect_access_base = v; 321 } else { 322 switch (s->indirect_access_base) { 323 case NPCM_PCS_IND_SR_CTL: 324 npcm_pcs_write_sr_ctl(s, offset, v); 325 break; 326 327 case NPCM_PCS_IND_SR_MII: 328 npcm_pcs_write_sr_mii(s, offset, v); 329 break; 330 331 case NPCM_PCS_IND_SR_TIM: 332 npcm_pcs_write_sr_tim(s, offset, v); 333 break; 334 335 case NPCM_PCS_IND_VR_MII: 336 npcm_pcs_write_vr_mii(s, offset, v); 337 break; 338 339 default: 340 qemu_log_mask(LOG_GUEST_ERROR, 341 "%s: Write with invalid indirect address base: 0x%02" 342 PRIx16 "\n", DEVICE(s)->canonical_path, 343 s->indirect_access_base); 344 } 345 } 346 } 347 348 static void npcm_pcs_enter_reset(Object *obj, ResetType type) 349 { 350 NPCMPCSState *s = NPCM_PCS(obj); 351 352 npcm_pcs_soft_reset(s); 353 } 354 355 static const struct MemoryRegionOps npcm_pcs_ops = { 356 .read = npcm_pcs_read, 357 .write = npcm_pcs_write, 358 .endianness = DEVICE_LITTLE_ENDIAN, 359 .valid = { 360 .min_access_size = 2, 361 .max_access_size = 2, 362 .unaligned = false, 363 }, 364 }; 365 366 static void npcm_pcs_realize(DeviceState *dev, Error **errp) 367 { 368 NPCMPCSState *pcs = NPCM_PCS(dev); 369 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 370 371 memory_region_init_io(&pcs->iomem, OBJECT(pcs), &npcm_pcs_ops, pcs, 372 TYPE_NPCM_PCS, 8 * KiB); 373 sysbus_init_mmio(sbd, &pcs->iomem); 374 } 375 376 static const VMStateDescription vmstate_npcm_pcs = { 377 .name = TYPE_NPCM_PCS, 378 .version_id = 0, 379 .minimum_version_id = 0, 380 .fields = (VMStateField[]) { 381 VMSTATE_UINT16(indirect_access_base, NPCMPCSState), 382 VMSTATE_UINT16_ARRAY(sr_ctl, NPCMPCSState, NPCM_PCS_NR_SR_CTLS), 383 VMSTATE_UINT16_ARRAY(sr_mii, NPCMPCSState, NPCM_PCS_NR_SR_MIIS), 384 VMSTATE_UINT16_ARRAY(sr_tim, NPCMPCSState, NPCM_PCS_NR_SR_TIMS), 385 VMSTATE_UINT16_ARRAY(vr_mii, NPCMPCSState, NPCM_PCS_NR_VR_MIIS), 386 VMSTATE_END_OF_LIST(), 387 }, 388 }; 389 390 static void npcm_pcs_class_init(ObjectClass *klass, void *data) 391 { 392 ResettableClass *rc = RESETTABLE_CLASS(klass); 393 DeviceClass *dc = DEVICE_CLASS(klass); 394 395 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 396 dc->desc = "NPCM PCS Controller"; 397 dc->realize = npcm_pcs_realize; 398 dc->vmsd = &vmstate_npcm_pcs; 399 rc->phases.enter = npcm_pcs_enter_reset; 400 } 401 402 static const TypeInfo npcm_pcs_types[] = { 403 { 404 .name = TYPE_NPCM_PCS, 405 .parent = TYPE_SYS_BUS_DEVICE, 406 .instance_size = sizeof(NPCMPCSState), 407 .class_init = npcm_pcs_class_init, 408 }, 409 }; 410 DEFINE_TYPES(npcm_pcs_types) 411