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