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
npcm_pcs_soft_reset(NPCMPCSState * s)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
npcm_pcs_read_sr_ctl(NPCMPCSState * s,hwaddr offset)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
npcm_pcs_read_sr_mii(NPCMPCSState * s,hwaddr offset)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
npcm_pcs_read_sr_tim(NPCMPCSState * s,hwaddr offset)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
npcm_pcs_read_vr_mii(NPCMPCSState * s,hwaddr offset)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
npcm_pcs_write_sr_ctl(NPCMPCSState * s,hwaddr offset,uint16_t v)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
npcm_pcs_write_sr_mii(NPCMPCSState * s,hwaddr offset,uint16_t v)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
npcm_pcs_write_sr_tim(NPCMPCSState * s,hwaddr offset,uint16_t v)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
npcm_pcs_write_vr_mii(NPCMPCSState * s,hwaddr offset,uint16_t v)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
npcm_pcs_read(void * opaque,hwaddr offset,unsigned size)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
npcm_pcs_write(void * opaque,hwaddr offset,uint64_t v,unsigned size)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
npcm_pcs_enter_reset(Object * obj,ResetType type)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
npcm_pcs_realize(DeviceState * dev,Error ** errp)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
npcm_pcs_class_init(ObjectClass * klass,const void * data)390 static void npcm_pcs_class_init(ObjectClass *klass, const 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