123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- /*
- * Nuvoton NPCM8xx PCS Module
- *
- * Copyright 2022 Google LLC
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
- /*
- * Disclaimer:
- * Currently we only implemented the default values of the registers and
- * the soft reset feature. These are required to boot up the GMAC module
- * in Linux kernel for NPCM845 boards. Other functionalities are not modeled.
- */
- #include "qemu/osdep.h"
- #include "exec/hwaddr.h"
- #include "hw/registerfields.h"
- #include "hw/net/npcm_pcs.h"
- #include "migration/vmstate.h"
- #include "qemu/log.h"
- #include "qemu/units.h"
- #include "trace.h"
- #define NPCM_PCS_IND_AC_BA 0x1fe
- #define NPCM_PCS_IND_SR_CTL 0x1e00
- #define NPCM_PCS_IND_SR_MII 0x1f00
- #define NPCM_PCS_IND_SR_TIM 0x1f07
- #define NPCM_PCS_IND_VR_MII 0x1f80
- REG16(NPCM_PCS_SR_CTL_ID1, 0x08)
- REG16(NPCM_PCS_SR_CTL_ID2, 0x0a)
- REG16(NPCM_PCS_SR_CTL_STS, 0x10)
- REG16(NPCM_PCS_SR_MII_CTRL, 0x00)
- REG16(NPCM_PCS_SR_MII_STS, 0x02)
- REG16(NPCM_PCS_SR_MII_DEV_ID1, 0x04)
- REG16(NPCM_PCS_SR_MII_DEV_ID2, 0x06)
- REG16(NPCM_PCS_SR_MII_AN_ADV, 0x08)
- REG16(NPCM_PCS_SR_MII_LP_BABL, 0x0a)
- REG16(NPCM_PCS_SR_MII_AN_EXPN, 0x0c)
- REG16(NPCM_PCS_SR_MII_EXT_STS, 0x1e)
- REG16(NPCM_PCS_SR_TIM_SYNC_ABL, 0x10)
- REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x12)
- REG16(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0x14)
- REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x16)
- REG16(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0x18)
- REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x1a)
- REG16(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0x1c)
- REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x1e)
- REG16(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0x20)
- REG16(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x000)
- REG16(NPCM_PCS_VR_MII_AN_CTRL, 0x002)
- REG16(NPCM_PCS_VR_MII_AN_INTR_STS, 0x004)
- REG16(NPCM_PCS_VR_MII_TC, 0x006)
- REG16(NPCM_PCS_VR_MII_DBG_CTRL, 0x00a)
- REG16(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x00c)
- REG16(NPCM_PCS_VR_MII_EEE_TXTIMER, 0x010)
- REG16(NPCM_PCS_VR_MII_EEE_RXTIMER, 0x012)
- REG16(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0x014)
- REG16(NPCM_PCS_VR_MII_EEE_MCTRL1, 0x016)
- REG16(NPCM_PCS_VR_MII_DIG_STS, 0x020)
- REG16(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0x022)
- REG16(NPCM_PCS_VR_MII_MISC_STS, 0x030)
- REG16(NPCM_PCS_VR_MII_RX_LSTS, 0x040)
- REG16(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x070)
- REG16(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x074)
- REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x07a)
- REG16(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0x07c)
- REG16(NPCM_PCS_VR_MII_MP_TX_STS, 0x090)
- REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0b0)
- REG16(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x0b2)
- REG16(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x0ba)
- REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0f0)
- REG16(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0f2)
- REG16(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x110)
- REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0x126)
- REG16(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x130)
- REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0x132)
- REG16(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0x134)
- REG16(NPCM_PCS_VR_MII_DIG_CTRL2, 0x1c2)
- REG16(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0x1c4)
- /* Register Fields */
- #define NPCM_PCS_SR_MII_CTRL_RST BIT(15)
- static const uint16_t npcm_pcs_sr_ctl_cold_reset_values[NPCM_PCS_NR_SR_CTLS] = {
- [R_NPCM_PCS_SR_CTL_ID1] = 0x699e,
- [R_NPCM_PCS_SR_CTL_STS] = 0x8000,
- };
- static const uint16_t npcm_pcs_sr_mii_cold_reset_values[NPCM_PCS_NR_SR_MIIS] = {
- [R_NPCM_PCS_SR_MII_CTRL] = 0x1140,
- [R_NPCM_PCS_SR_MII_STS] = 0x0109,
- [R_NPCM_PCS_SR_MII_DEV_ID1] = 0x699e,
- [R_NPCM_PCS_SR_MII_DEV_ID2] = 0xced0,
- [R_NPCM_PCS_SR_MII_AN_ADV] = 0x0020,
- [R_NPCM_PCS_SR_MII_EXT_STS] = 0xc000,
- };
- static const uint16_t npcm_pcs_sr_tim_cold_reset_values[NPCM_PCS_NR_SR_TIMS] = {
- [R_NPCM_PCS_SR_TIM_SYNC_ABL] = 0x0003,
- [R_NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR] = 0x0038,
- [R_NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR] = 0x0038,
- [R_NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR] = 0x0058,
- [R_NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR] = 0x0048,
- };
- static const uint16_t npcm_pcs_vr_mii_cold_reset_values[NPCM_PCS_NR_VR_MIIS] = {
- [R_NPCM_PCS_VR_MII_MMD_DIG_CTRL1] = 0x2400,
- [R_NPCM_PCS_VR_MII_AN_INTR_STS] = 0x000a,
- [R_NPCM_PCS_VR_MII_EEE_MCTRL0] = 0x899c,
- [R_NPCM_PCS_VR_MII_DIG_STS] = 0x0010,
- [R_NPCM_PCS_VR_MII_MP_TX_BSTCTRL0] = 0x000a,
- [R_NPCM_PCS_VR_MII_MP_TX_LVLCTRL0] = 0x007f,
- [R_NPCM_PCS_VR_MII_MP_TX_GENCTRL0] = 0x0001,
- [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL0] = 0x0100,
- [R_NPCM_PCS_VR_MII_MP_RX_GENCTRL1] = 0x1100,
- [R_NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0] = 0x000e,
- [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL0] = 0x0100,
- [R_NPCM_PCS_VR_MII_MP_MPLL_CTRL1] = 0x0032,
- [R_NPCM_PCS_VR_MII_MP_MPLL_STS] = 0x0001,
- [R_NPCM_PCS_VR_MII_MP_LVL_CTRL] = 0x0019,
- };
- static void npcm_pcs_soft_reset(NPCMPCSState *s)
- {
- memcpy(s->sr_ctl, npcm_pcs_sr_ctl_cold_reset_values,
- NPCM_PCS_NR_SR_CTLS * sizeof(uint16_t));
- memcpy(s->sr_mii, npcm_pcs_sr_mii_cold_reset_values,
- NPCM_PCS_NR_SR_MIIS * sizeof(uint16_t));
- memcpy(s->sr_tim, npcm_pcs_sr_tim_cold_reset_values,
- NPCM_PCS_NR_SR_TIMS * sizeof(uint16_t));
- memcpy(s->vr_mii, npcm_pcs_vr_mii_cold_reset_values,
- NPCM_PCS_NR_VR_MIIS * sizeof(uint16_t));
- }
- static uint16_t npcm_pcs_read_sr_ctl(NPCMPCSState *s, hwaddr offset)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_SR_CTLS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: SR_CTL read offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return 0;
- }
- return s->sr_ctl[regno];
- }
- static uint16_t npcm_pcs_read_sr_mii(NPCMPCSState *s, hwaddr offset)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_SR_MIIS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: SR_MII read offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return 0;
- }
- return s->sr_mii[regno];
- }
- static uint16_t npcm_pcs_read_sr_tim(NPCMPCSState *s, hwaddr offset)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_SR_TIMS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: SR_TIM read offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return 0;
- }
- return s->sr_tim[regno];
- }
- static uint16_t npcm_pcs_read_vr_mii(NPCMPCSState *s, hwaddr offset)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_VR_MIIS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: VR_MII read offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return 0;
- }
- return s->vr_mii[regno];
- }
- static void npcm_pcs_write_sr_ctl(NPCMPCSState *s, hwaddr offset, uint16_t v)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_SR_CTLS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: SR_CTL write offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return;
- }
- s->sr_ctl[regno] = v;
- }
- static void npcm_pcs_write_sr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_SR_MIIS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: SR_MII write offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return;
- }
- s->sr_mii[regno] = v;
- if ((offset == A_NPCM_PCS_SR_MII_CTRL) && (v & NPCM_PCS_SR_MII_CTRL_RST)) {
- /* Trigger a soft reset */
- npcm_pcs_soft_reset(s);
- }
- }
- static void npcm_pcs_write_sr_tim(NPCMPCSState *s, hwaddr offset, uint16_t v)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_SR_TIMS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: SR_TIM write offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return;
- }
- s->sr_tim[regno] = v;
- }
- static void npcm_pcs_write_vr_mii(NPCMPCSState *s, hwaddr offset, uint16_t v)
- {
- hwaddr regno = offset / sizeof(uint16_t);
- if (regno >= NPCM_PCS_NR_VR_MIIS) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: VR_MII write offset 0x%04" HWADDR_PRIx
- " is out of range.\n",
- DEVICE(s)->canonical_path, offset);
- return;
- }
- s->vr_mii[regno] = v;
- }
- static uint64_t npcm_pcs_read(void *opaque, hwaddr offset, unsigned size)
- {
- NPCMPCSState *s = opaque;
- uint16_t v = 0;
- if (offset == NPCM_PCS_IND_AC_BA) {
- v = s->indirect_access_base;
- } else {
- switch (s->indirect_access_base) {
- case NPCM_PCS_IND_SR_CTL:
- v = npcm_pcs_read_sr_ctl(s, offset);
- break;
- case NPCM_PCS_IND_SR_MII:
- v = npcm_pcs_read_sr_mii(s, offset);
- break;
- case NPCM_PCS_IND_SR_TIM:
- v = npcm_pcs_read_sr_tim(s, offset);
- break;
- case NPCM_PCS_IND_VR_MII:
- v = npcm_pcs_read_vr_mii(s, offset);
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: Read with invalid indirect address base: 0x%"
- PRIx16 "\n", DEVICE(s)->canonical_path,
- s->indirect_access_base);
- }
- }
- trace_npcm_pcs_reg_read(DEVICE(s)->canonical_path, s->indirect_access_base,
- offset, v);
- return v;
- }
- static void npcm_pcs_write(void *opaque, hwaddr offset,
- uint64_t v, unsigned size)
- {
- NPCMPCSState *s = opaque;
- trace_npcm_pcs_reg_write(DEVICE(s)->canonical_path, s->indirect_access_base,
- offset, v);
- if (offset == NPCM_PCS_IND_AC_BA) {
- s->indirect_access_base = v;
- } else {
- switch (s->indirect_access_base) {
- case NPCM_PCS_IND_SR_CTL:
- npcm_pcs_write_sr_ctl(s, offset, v);
- break;
- case NPCM_PCS_IND_SR_MII:
- npcm_pcs_write_sr_mii(s, offset, v);
- break;
- case NPCM_PCS_IND_SR_TIM:
- npcm_pcs_write_sr_tim(s, offset, v);
- break;
- case NPCM_PCS_IND_VR_MII:
- npcm_pcs_write_vr_mii(s, offset, v);
- break;
- default:
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: Write with invalid indirect address base: 0x%02"
- PRIx16 "\n", DEVICE(s)->canonical_path,
- s->indirect_access_base);
- }
- }
- }
- static void npcm_pcs_enter_reset(Object *obj, ResetType type)
- {
- NPCMPCSState *s = NPCM_PCS(obj);
- npcm_pcs_soft_reset(s);
- }
- static const struct MemoryRegionOps npcm_pcs_ops = {
- .read = npcm_pcs_read,
- .write = npcm_pcs_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid = {
- .min_access_size = 2,
- .max_access_size = 2,
- .unaligned = false,
- },
- };
- static void npcm_pcs_realize(DeviceState *dev, Error **errp)
- {
- NPCMPCSState *pcs = NPCM_PCS(dev);
- SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
- memory_region_init_io(&pcs->iomem, OBJECT(pcs), &npcm_pcs_ops, pcs,
- TYPE_NPCM_PCS, 8 * KiB);
- sysbus_init_mmio(sbd, &pcs->iomem);
- }
- static const VMStateDescription vmstate_npcm_pcs = {
- .name = TYPE_NPCM_PCS,
- .version_id = 0,
- .minimum_version_id = 0,
- .fields = (VMStateField[]) {
- VMSTATE_UINT16(indirect_access_base, NPCMPCSState),
- VMSTATE_UINT16_ARRAY(sr_ctl, NPCMPCSState, NPCM_PCS_NR_SR_CTLS),
- VMSTATE_UINT16_ARRAY(sr_mii, NPCMPCSState, NPCM_PCS_NR_SR_MIIS),
- VMSTATE_UINT16_ARRAY(sr_tim, NPCMPCSState, NPCM_PCS_NR_SR_TIMS),
- VMSTATE_UINT16_ARRAY(vr_mii, NPCMPCSState, NPCM_PCS_NR_VR_MIIS),
- VMSTATE_END_OF_LIST(),
- },
- };
- static void npcm_pcs_class_init(ObjectClass *klass, void *data)
- {
- ResettableClass *rc = RESETTABLE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
- dc->desc = "NPCM PCS Controller";
- dc->realize = npcm_pcs_realize;
- dc->vmsd = &vmstate_npcm_pcs;
- rc->phases.enter = npcm_pcs_enter_reset;
- }
- static const TypeInfo npcm_pcs_types[] = {
- {
- .name = TYPE_NPCM_PCS,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(NPCMPCSState),
- .class_init = npcm_pcs_class_init,
- },
- };
- DEFINE_TYPES(npcm_pcs_types)
|