npcm_pcs.c 13 KB

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