phydev.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*!
  2. * Copyright (C) Fraunhofer-Institut for Photonic Microsystems (IPMS)
  3. * Maria-Reiche-Str. 2
  4. * 01109 Dresden
  5. *
  6. * Unauthorized copying of this file, via any medium is strictly prohibited
  7. * Proprietary and confidential
  8. *
  9. * \file phydev.c
  10. * \author phil.seipt@ipms.fraunhofer.de
  11. * \date 08.06.2021
  12. * \brief PHY devices
  13. *
  14. */
  15. #include "phydev.h"
  16. #include "mdiobus.h"
  17. #include <kernel/base/kernel.h>
  18. #include <kernel/base/klibc.h>
  19. #include <kernel/driver/phy/phy.h>
  20. // ----------------------------------------------------------------
  21. static const char *PHY_CONTEXT = "PHY";
  22. static uint32_t _phy_dev_cnt = 0;
  23. static list_data_t *phy_list;
  24. static TaskHandle_t phy_task;
  25. static void _taskPhyMonitor(void *pvParameters);
  26. static int32_t _phydev_mdiobus_probe_from(struct mdiobus_s *bus, uint32_t from, uint32_t *id);
  27. static void _phydev_generic_aneg_enable(struct phydev_s *phy, int enable);
  28. static void _phydev_generic_force_speed(struct phydev_s *phy, int speed);
  29. static void _phydev_generic_restart(struct phydev_s *phy);
  30. static void _phydev_generic_reg_dump(struct phydev_s *phy);
  31. static void _phydev_generic_reg_modify(struct phydev_s *phy, enum phydev_reg_access access, uint32_t reg, uint32_t *value);
  32. // ----------------------------------------------------------------
  33. /**
  34. * \brief Initialize the PHY device system, is called from the kernel
  35. */
  36. void phydev_init(void)
  37. {
  38. phy_list = list_create();
  39. kassert(phy_list != NULL);
  40. /* create sys task */
  41. kTaskCreate_a("KERNEL", _taskPhyMonitor, "phy_task", configMINIMAL_STACK_SIZE, (void *)phy_list, KERNEL_PRIO_BG, &phy_task);
  42. }
  43. /**
  44. * \brief Allocate and register MDIO bus device driver
  45. *
  46. * @param netdev ptr to netdev device
  47. * @param mdio ptr to MDIO bus device driver
  48. *
  49. * @return pointer to driver structure, NULL on error
  50. */
  51. struct phydev_s *phydev_register(struct netdev_s *netdev, struct mdiobus_s *mdio)
  52. {
  53. struct phydev_s *p;
  54. // alloc struct
  55. p = (struct phydev_s *)kallocz(sizeof(struct phydev_s));
  56. if (!p)
  57. return p;
  58. list_add(phy_list, p);
  59. netdev->phy = p;
  60. // set fields
  61. p->netdev = netdev;
  62. p->mdio = mdio;
  63. p->id = _phy_dev_cnt;
  64. _phy_dev_cnt++;
  65. p->aneg_enable = _phydev_generic_aneg_enable;
  66. p->force_speed = _phydev_generic_force_speed;
  67. p->restart = _phydev_generic_restart;
  68. p->reg_dump = _phydev_generic_reg_dump;
  69. p->reg_modify = _phydev_generic_reg_modify;
  70. p->open = NULL;
  71. return p;
  72. }
  73. /**
  74. * \brief Start PHY
  75. *
  76. * @param phy ptr to PHY device
  77. * @param phyad PHY address, PHY_MAX_ADDR start automatic address search
  78. *
  79. */
  80. void phydev_start(struct phydev_s *phy, uint32_t phyad)
  81. {
  82. int32_t tmp;
  83. uint32_t phyid;
  84. if (phy->mdio == NULL) {
  85. logk(LOG_ALWAYS, PHY_CONTEXT, "NO MDIO bus device registered!\n");
  86. } else {
  87. if (phyad == PHY_MAX_ADDR) {
  88. logs(LOG_ALWAYS, PHY_CONTEXT, "Probe PHYs on mdio%d ... ", phy->mdio->id);
  89. tmp = _phydev_mdiobus_probe_from(phy->mdio, 0, &phyid);
  90. if (tmp < 0) {
  91. loge(LOG_ALWAYS, "NO PHY!\n");
  92. } else {
  93. loge(LOG_ALWAYS, "ad=%d id=%08X\n", tmp, phyid);
  94. }
  95. phy->phyad = tmp;
  96. } else {
  97. if (_phydev_mdiobus_probe_from(phy->mdio, phyad, &phyid) != phyad)
  98. logk(LOG_ALWAYS, PHY_CONTEXT, "NO PHY at %d!\n", phyad);
  99. phy->phyad = phyad;
  100. }
  101. }
  102. if (phy->open)
  103. phy->open(phy, NULL);
  104. }
  105. /**
  106. * \brief Update link status of PHY
  107. *
  108. * @param phy pointer to device driver
  109. */
  110. void phydev_update_link(struct phydev_s *phy)
  111. {
  112. if (!phy)
  113. return;
  114. if (phy->update_link)
  115. phy->update_link(phy);
  116. }
  117. /**
  118. * \brief Restart auto-negotiation of PHY
  119. *
  120. * @param phy pointer to device driver
  121. * @param enable enable/disable autonegotiation
  122. */
  123. void phydev_aneg_enable(struct phydev_s *phy, int enable)
  124. {
  125. if (!phy)
  126. return;
  127. if (phy->aneg_enable)
  128. phy->aneg_enable(phy, enable);
  129. }
  130. /**
  131. * \brief Force PHY to given link speed
  132. *
  133. * @param phy pointer to device driver
  134. * @param speed speed (10/100/1000)
  135. */
  136. void phydev_force_speed(struct phydev_s *phy, int speed)
  137. {
  138. if (!phy)
  139. return;
  140. if (phy->force_speed)
  141. phy->force_speed(phy, speed);
  142. }
  143. /**
  144. * \brief PHY software reset
  145. *
  146. * @param phy pointer to device driver
  147. */
  148. void phydev_reset(struct phydev_s *phy)
  149. {
  150. phydev_modify(phy, PHY_REG_BMCR, 0, BMCR_RESET);
  151. }
  152. /**
  153. * \brief Force PHY to given link speed
  154. *
  155. * @param phy pointer to device driver
  156. */
  157. void phydev_reg_dump(struct phydev_s *phy)
  158. {
  159. if (!phy)
  160. return;
  161. if (phy->reg_dump)
  162. phy->reg_dump(phy);
  163. }
  164. int phydev_rreg(struct phydev_s *phy, uint32_t reg)
  165. {
  166. uint32_t value = -1;
  167. if (!phy)
  168. return value;
  169. if (phy->reg_modify)
  170. phy->reg_modify(phy, PHYDEV_ACCESS_GET, reg, &value);
  171. return value;
  172. }
  173. void phydev_wreg(struct phydev_s *phy, uint32_t reg, uint32_t value)
  174. {
  175. if (!phy)
  176. return;
  177. if (phy->reg_modify)
  178. phy->reg_modify(phy, PHYDEV_ACCESS_SET, reg, &value);
  179. }
  180. /**
  181. * \brief Force PHY to restart
  182. *
  183. * @param phy pointer to device driver
  184. */
  185. void phydev_restart(struct phydev_s *phy)
  186. {
  187. if (!phy)
  188. return;
  189. if (phy->reg_dump)
  190. phy->restart(phy);
  191. }
  192. /**
  193. * \brief Read-Modify-Write PHY register
  194. *
  195. * @param phy pointer to device driver
  196. * @param regad PHY register address
  197. * @param clrmask clear bit vector of read value
  198. * @param setmask set bit vector to value before write-back
  199. * @return
  200. */
  201. int32_t phydev_modify(struct phydev_s *phy, uint32_t regad, uint16_t clrmask, uint16_t setmask)
  202. {
  203. int32_t value;
  204. value = mdiobus_read(phy->mdio, phy->phyad, regad);
  205. if (value < 0)
  206. return value;
  207. value &= ~clrmask;
  208. value |= setmask;
  209. return mdiobus_write(phy->mdio, phy->phyad, regad, value);
  210. }
  211. // ----------------------------------------------------------------
  212. static void _taskPhyMonitor(void *pvParameters)
  213. {
  214. list_data_t *_phy_list = (list_data_t *)pvParameters;
  215. list_data_t *el;
  216. struct phydev_s *phy;
  217. int lastspeed;
  218. while (1) {
  219. el = _phy_list;
  220. vTaskDelay(PHY_UPDATE_DELAY_MS);
  221. do {
  222. if (el->data != NULL) {
  223. phy = (struct phydev_s *)el->data;
  224. lastspeed = phy->speed;
  225. phydev_update_link(phy);
  226. if (lastspeed != phy->speed) {
  227. // print information
  228. logk(LOG_ALWAYS, PHY_CONTEXT, "speed changed on %s = %d (fd=%d)\n", phy->netdev->name, phy->speed, phy->fullduplex);
  229. // update netdev
  230. netdev_adjust_link(phy->netdev, phy->speed);
  231. }
  232. }
  233. el = list_next(_phy_list, el);
  234. } while (!list_end(_phy_list, el));
  235. }
  236. }
  237. /**
  238. * \brief Probe for PHY, start probing from given PHY address
  239. *
  240. * @param bus pointer to MDIO bus device driver
  241. * @param from starting PHY address
  242. * @param id pointer to found PHY ID (reg 2/3 of PHY)
  243. * @return >= 0 found PHY address, <0 otherwise
  244. */
  245. static int32_t _phydev_mdiobus_probe_from(struct mdiobus_s *bus, uint32_t from, uint32_t *id)
  246. {
  247. uint32_t phyid;
  248. uint32_t phyad;
  249. int32_t value;
  250. for (phyad = from; phyad < PHY_MAX_ADDR; ++phyad) {
  251. value = bus->read(bus, phyad, PHY_REG_ID1);
  252. if (value < 0)
  253. continue;
  254. if (value != 0xFFFF) {
  255. // store phyID1
  256. phyid = (value << 16);
  257. // read phyID2
  258. value = bus->read(bus, phyad, PHY_REG_ID2);
  259. phyid |= (value & 0xFFFF);
  260. if (id)
  261. *id = phyid;
  262. return phyad;
  263. }
  264. }
  265. return -1;
  266. }
  267. static void _phydev_generic_aneg_enable(struct phydev_s *phy, int enable)
  268. {
  269. if (enable)
  270. phydev_modify(phy, PHY_REG_BMCR, 0, BMCR_ANENABLE | BMCR_ANRESTART);
  271. else
  272. phydev_modify(phy, PHY_REG_BMCR, BMCR_ANENABLE, 0);
  273. phy->aneg_on = enable;
  274. }
  275. static void _phydev_generic_force_speed(struct phydev_s *phy, int speed)
  276. {
  277. uint16_t ctl = 0;
  278. if (speed == 1000) {
  279. ctl = BMCR_SPEED1000;
  280. } else if (speed == 100) {
  281. ctl = BMCR_SPEED100;
  282. }
  283. // always force to full duplex
  284. ctl |= BMCR_FULLDPLX;
  285. // clear ALL but loopback
  286. phydev_modify(phy, PHY_REG_BMCR, ~(BMCR_LOOPBACK), ctl);
  287. phy->aneg_on = 0;
  288. }
  289. static void _phydev_generic_restart(struct phydev_s *phy)
  290. {
  291. // clear ALL but loopback
  292. phydev_modify(phy, PHY_REG_BMCR, 0, BMCR_RESET);
  293. phy->aneg_on = 1;
  294. if (phy->open)
  295. phy->open(phy, NULL);
  296. }
  297. static void _phydev_generic_reg_dump(struct phydev_s *phy)
  298. {
  299. logk(LOG_ALWAYS, PHY_CONTEXT, "%02u = 0x%04X\n", PHY_REG_BMCR, mdiobus_read(phy->mdio, phy->phyad, PHY_REG_BMCR));
  300. logk(LOG_ALWAYS, PHY_CONTEXT, "%02u = 0x%04X\n", PHY_REG_BMSR, mdiobus_read(phy->mdio, phy->phyad, PHY_REG_BMSR));
  301. logk(LOG_ALWAYS, PHY_CONTEXT, "%02u = 0x%04X\n", PHY_REG_ID1, mdiobus_read(phy->mdio, phy->phyad, PHY_REG_ID1));
  302. logk(LOG_ALWAYS, PHY_CONTEXT, "%02u = 0x%04X\n", PHY_REG_ID2, mdiobus_read(phy->mdio, phy->phyad, PHY_REG_ID2));
  303. logk(LOG_ALWAYS, PHY_CONTEXT, "%02u = 0x%04X\n", PHY_REG_ADV, mdiobus_read(phy->mdio, phy->phyad, PHY_REG_ADV));
  304. }
  305. static void _phydev_generic_reg_modify(struct phydev_s *phy, enum phydev_reg_access access, uint32_t reg, uint32_t *value)
  306. {
  307. if (access == PHYDEV_ACCESS_GET) {
  308. *value = mdiobus_read(phy->mdio, phy->phyad, reg);
  309. } else if (access == PHYDEV_ACCESS_SET) {
  310. mdiobus_write(phy->mdio, phy->phyad, reg, *value);
  311. }
  312. }