xref: /qemu/hw/net/npcm_pcs.c (revision f07a5674cf97b8473e5d06d7b1df9b51e97d553f)
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