1. /********************************************************************
  2. * OK335xS LAN8710 phy driver hacking
  3. * 说明:
  4. * 本文主要是对OK335xS中的phy的驱动进行代码跟踪,并解决当前遇到
  5. * LAN8710上电后插入网线,会导致LAN8710无法自动握手,Link灯不亮,内核
  6. * 也检测不到LAN8710有状态发生了改变,最终问题定位于LAN8710的驱动初
  7. * 始化部分,本文解决办法选择注释掉对应的内容就行了。
  8. *
  9. * 2016-3-3 深圳 南山平山村 曾剑锋
  10. *******************************************************************/
  11.  
  12. 一、make menuconfig 配置:
  13. .config - Linux/arm 3.2. Kernel Configuration
  14. ──────────────────────────────────────────────────────────────────────────────
  15. ┌───────────────── PHY Device support and infrastructure ─────────────────┐
  16. Arrow keys navigate the menu. <Enter> selects submenus --->.
  17. Highlighted letters are hotkeys. Pressing <Y> includes, <N> excludes,
  18. <M> modularizes features. Press <Esc><Esc> to exit, <?> for Help, </>
  19. for Search. Legend: [*] built-in [ ] excluded <M> module < >
  20. ┌────^(-)─────────────────────────────────────────────────────────────┐
  21. < > Drivers for Davicom PHYs
  22. < > Drivers for Quality Semiconductor PHYs
  23. < > Drivers for the Intel LXT PHYs
  24. < > Drivers for the Cicada PHYs
  25. < > Drivers for the Vitesse PHYs
  26. <*> Drivers for SMSC PHYs
  27. < > Drivers for Broadcom PHYs
  28. < > Drivers for ICPlus PHYs
  29. < > Drivers for Realtek PHYs
  30. < > Drivers for National Semiconductor PHYs
  31. └────v(+)─────────────────────────────────────────────────────────────┘
  32. ├─────────────────────────────────────────────────────────────────────────┤
  33. <Select> < Exit > < Help >
  34. └─────────────────────────────────────────────────────────────────────────┘
  35.  
  36. 二、linux-3.2./drivers/net/phy/smsc.c 跟踪:
  37. static struct phy_driver lan8710_driver = { <---------+
  38. /* OUI=0x00800f, Model#=0x0f */ |
  39. .phy_id = 0x0007c0f0, |
  40. // mask导致phy_id=0x0007c0f1也行的 |
  41. .phy_id_mask = 0xfffffff0, |
  42. .name = "SMSC LAN8710/LAN8720", |
  43. |
  44. .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
  45. | SUPPORTED_Asym_Pause), |
  46. .flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG, |
  47. |
  48. /* basic functions */ |
  49. .config_aneg = genphy_config_aneg, --------*------+
  50. .read_status = genphy_read_status, --------*------*--+
  51. .config_init = smsc_phy_config_init, --------*------*--*-+
  52. | | | |
  53. /* IRQ related */ | | | |
  54. .ack_interrupt = smsc_phy_ack_interrupt, | | | |
  55. .config_intr = smsc_phy_config_intr, | | | |
  56. | | | |
  57. .suspend = genphy_suspend, | | | |
  58. .resume = genphy_resume, | | | |
  59. | | | |
  60. .driver = { .owner = THIS_MODULE, } | | | |
  61. }; | | | |
  62. | | | |
  63. static int __init smsc_init(void) <---------+ | | | |
  64. { | | | | |
  65. int ret; | | | | |
  66. | | | | |
  67. ret = phy_driver_register (&lan83c185_driver); | | | | |
  68. if (ret) | | | | |
  69. goto err1; | | | | |
  70. | | | | |
  71. ret = phy_driver_register (&lan8187_driver); | | | | |
  72. if (ret) | | | | |
  73. goto err2; | | | | |
  74. | | | | |
  75. ret = phy_driver_register (&lan8700_driver); | | | | |
  76. if (ret) | | | | |
  77. goto err3; | | | | |
  78. | | | | |
  79. ret = phy_driver_register (&lan911x_int_driver);| | | | |
  80. if (ret) | | | | |
  81. goto err4; | | | | |
  82. | | | | |
  83. ret = phy_driver_register (&lan8710_driver); | -----+ | | |
  84. if (ret) | | | |
  85. goto err5; | | | |
  86. | | | |
  87. return ; | | | |
  88. err5: | | | |
  89. phy_driver_unregister (&lan911x_int_driver); | | | |
  90. err4: | | | |
  91. phy_driver_unregister (&lan8700_driver); | | | |
  92. err3: | | | |
  93. phy_driver_unregister (&lan8187_driver); | | | |
  94. err2: | | | |
  95. phy_driver_unregister (&lan83c185_driver); | | | |
  96. err1: | | | |
  97. return ret; | | | |
  98. } | | | |
  99. | | | |
  100. static void __exit smsc_exit(void) | | | |
  101. { | | | |
  102. phy_driver_unregister (&lan8710_driver); | | | |
  103. phy_driver_unregister (&lan911x_int_driver); | | | |
  104. phy_driver_unregister (&lan8700_driver); | | | |
  105. phy_driver_unregister (&lan8187_driver); | | | |
  106. phy_driver_unregister (&lan83c185_driver); | | | |
  107. } | | | |
  108. | | | |
  109. MODULE_DESCRIPTION("SMSC PHY driver"); | | | |
  110. MODULE_AUTHOR("Herbert Valerio Riedel"); | | | |
  111. MODULE_LICENSE("GPL"); | | | |
  112. | | | |
  113. module_init(smsc_init); -----------+ | | |
  114. module_exit(smsc_exit); | | |
  115. | | |
  116. static struct mdio_device_id __maybe_unused smsc_tbl[] = { | | |
  117. { 0x0007c0a0, 0xfffffff0 }, | | |
  118. { 0x0007c0b0, 0xfffffff0 }, | | |
  119. { 0x0007c0c0, 0xfffffff0 }, | | |
  120. { 0x0007c0d0, 0xfffffff0 }, | | |
  121. { 0x0007c0f0, 0xfffffff0 }, | | |
  122. { } | | |
  123. }; | | |
  124. | | |
  125. MODULE_DEVICE_TABLE(mdio, smsc_tbl); | | |
  126. | | |
  127. | | |
  128. | | |
  129. static int __init phy_init(void) | | |
  130. { | | |
  131. int rc; | | |
  132. | | |
  133. rc = mdio_bus_init(); ------------------+ | | |
  134. if (rc) | | | |
  135. return rc; | | | |
  136. | | | |
  137. rc = phy_driver_register(&genphy_driver); ---*-----+ | | |
  138. if (rc) | | | | |
  139. mdio_bus_exit(); | | | | |
  140. | | | | |
  141. return rc; | | | | |
  142. } | | | | |
  143. | | | | |
  144. static void __exit phy_exit(void) | | | | |
  145. { | | | | |
  146. phy_driver_unregister(&genphy_driver); | | | | |
  147. mdio_bus_exit(); | | | | |
  148. } | | | | |
  149. | | | | |
  150. subsys_initcall(phy_init); | | | | |
  151. module_exit(phy_exit); | | | | |
  152. | | | | |
  153. struct bus_type mdio_bus_type = { <-------+ | | | | |
  154. .name = "mdio_bus", | | | | | |
  155. .match = mdio_bus_match, ------*-*-+ | | | |
  156. .pm = MDIO_BUS_PM_OPS, | | | | | | |
  157. }; | | | | | | |
  158. EXPORT_SYMBOL(mdio_bus_type); | | | | | | |
  159. | | | | | | |
  160. int __init mdio_bus_init(void) <-----------*-+ | | | | |
  161. { | | | | | |
  162. int ret; | | | | | |
  163. | | | | | |
  164. ret = class_register(&mdio_bus_class); | | | | | |
  165. if (!ret) { | | | | | |
  166. ret = bus_register(&mdio_bus_type); -----+ | | | | |
  167. if (ret) | | | | |
  168. class_unregister(&mdio_bus_class); | | | | |
  169. } | | | | |
  170. | | | | |
  171. return ret; | | | | |
  172. } | | | | |
  173. | | | | |
  174. void mdio_bus_exit(void) | | | | |
  175. { | | | | |
  176. class_unregister(&mdio_bus_class); | | | | |
  177. bus_unregister(&mdio_bus_type); | | | | |
  178. } | | | | |
  179. | | | | |
  180. static int mdio_bus_match(struct device *dev, <-----+ | | | |
  181. struct device_driver *drv) | | | |
  182. { | | | |
  183. struct phy_device *phydev = to_phy_device(dev); | | | |
  184. struct phy_driver *phydrv = to_phy_driver(drv); | | | |
  185. | | | |
  186. return ((phydrv->phy_id & phydrv->phy_id_mask) == | | | |
  187. (phydev->phy_id & phydrv->phy_id_mask)); | | | |
  188. } | | | |
  189. | | | |
  190. static struct phy_driver genphy_driver = { <---------+ | | |
  191. .phy_id = 0xffffffff, | | | |
  192. .phy_id_mask = 0xffffffff, | | | |
  193. .name = "Generic PHY", | | | |
  194. .config_init = genphy_config_init, ------*-------+ | | |
  195. .features = , | | | | |
  196. .config_aneg = genphy_config_aneg, ------*-------*-+ | |
  197. .read_status = genphy_read_status, ------*-------*-*--+ |
  198. .suspend = genphy_suspend, | | | | |
  199. .resume = genphy_resume, | | | | |
  200. .driver = {.owner= THIS_MODULE, }, | | | | |
  201. }; | | | | |
  202. | | | | |
  203. int phy_driver_register(struct phy_driver *new_driver) <--+ | | | |
  204. { | | | |
  205. int retval; | | | |
  206. | | | |
  207. new_driver->driver.name = new_driver->name; | | | |
  208. new_driver->driver.bus = &mdio_bus_type; | | | |
  209. new_driver->driver.probe = phy_probe; -----------+ | | | |
  210. new_driver->driver.remove = phy_remove; | | | | |
  211. | | | | |
  212. retval = driver_register(&new_driver->driver); | | | | |
  213. | | | | |
  214. if (retval) { | | | | |
  215. printk(KERN_ERR "%s: Error %d in registering driver\n", | | | | |
  216. new_driver->name, retval); | | | | |
  217. | | | | |
  218. return retval; | | | | |
  219. } | | | | |
  220. | | | | |
  221. pr_debug("%s: Registered new driver\n", new_driver->name); | | | | |
  222. | | | | |
  223. return ; | | | | |
  224. } | | | | |
  225. EXPORT_SYMBOL(phy_driver_register); | | | | |
  226. | | | | |
  227. static int phy_probe(struct device *dev) <-----------+ | | | |
  228. { | | | |
  229. struct phy_device *phydev; | | | |
  230. struct phy_driver *phydrv; | | | |
  231. struct device_driver *drv; | | | |
  232. int err = ; | | | |
  233. | | | |
  234. phydev = to_phy_device(dev); | | | |
  235. | | | |
  236. /* Make sure the driver is held. | | | |
  237. * XXX -- Is this correct? */ | | | |
  238. drv = get_driver(phydev->dev.driver); | | | |
  239. phydrv = to_phy_driver(drv); | | | |
  240. phydev->drv = phydrv; | | | |
  241. | | | |
  242. /* Disable the interrupt if the PHY doesn't support it */ | | | |
  243. if (!(phydrv->flags & PHY_HAS_INTERRUPT)) | | | |
  244. phydev->irq = PHY_POLL; | | | |
  245. | | | |
  246. mutex_lock(&phydev->lock); | | | |
  247. | | | |
  248. /* Start out supporting everything. Eventually, | | | |
  249. * a controller will attach, and may modify one | | | |
  250. * or both of these values */ | | | |
  251. phydev->supported = phydrv->features; | | | |
  252. phydev->advertising = phydrv->features; | | | |
  253. | | | |
  254. | | | |
  255. /* Set the state to READY by default */ | | | |
  256. phydev->state = PHY_READY; | | | |
  257. | | | |
  258. if (phydev->drv->probe) | | | |
  259. err = phydev->drv->probe(phydev); | | | |
  260. | | | |
  261. mutex_unlock(&phydev->lock); | | | |
  262. | | | |
  263. return err; | | | |
  264. | | | |
  265. } | | | |
  266. | | | |
  267. static int genphy_config_init(struct phy_device *phydev) <-------+ | | |
  268. { | | |
  269. int val; | | |
  270. u32 features; | | |
  271. | | |
  272. /* For now, I'll claim that the generic driver supports | | |
  273. * all possible port types */ | | |
  274. features = (SUPPORTED_TP | SUPPORTED_MII | | |
  275. | SUPPORTED_AUI | SUPPORTED_FIBRE | | | |
  276. SUPPORTED_BNC); | | |
  277. | | |
  278. /* Do we support autonegotiation? */ | | |
  279. val = phy_read(phydev, MII_BMSR); | | |
  280. | | |
  281. if (val < ) | | |
  282. return val; | | |
  283. | | |
  284. if (val & BMSR_ANEGCAPABLE) | | |
  285. features |= SUPPORTED_Autoneg; | | |
  286. | | |
  287. if (val & BMSR_100FULL) | | |
  288. features |= SUPPORTED_100baseT_Full; | | |
  289. if (val & BMSR_100HALF) | | |
  290. features |= SUPPORTED_100baseT_Half; | | |
  291. if (val & BMSR_10FULL) | | |
  292. features |= SUPPORTED_10baseT_Full; | | |
  293. if (val & BMSR_10HALF) | | |
  294. features |= SUPPORTED_10baseT_Half; | | |
  295. | | |
  296. if (val & BMSR_ESTATEN) { | | |
  297. val = phy_read(phydev, MII_ESTATUS); | | |
  298. | | |
  299. if (val < ) | | |
  300. return val; | | |
  301. | | |
  302. if (val & ESTATUS_1000_TFULL) | | |
  303. features |= SUPPORTED_1000baseT_Full; | | |
  304. if (val & ESTATUS_1000_THALF) | | |
  305. features |= SUPPORTED_1000baseT_Half; | | |
  306. } | | |
  307. | | |
  308. phydev->supported = features; | | |
  309. phydev->advertising = features; | | |
  310. | | |
  311. return ; | | |
  312. } | | |
  313. | | |
  314. int genphy_config_aneg(struct phy_device *phydev) <----------+ | |
  315. { | |
  316. int result; | |
  317. | |
  318. printk("zengjf check postion at %s.\n", __func__); | |
  319. | |
  320. if (AUTONEG_ENABLE != phydev->autoneg) | |
  321. return genphy_setup_forced(phydev); | |
  322. | |
  323. result = genphy_config_advert(phydev); | |
  324. | |
  325. if (result < ) /* error */ | |
  326. return result; | |
  327. | |
  328. if (result == ) { | |
  329. /* Advertisement hasn't changed, but maybe aneg was never on to| |
  330. * begin with? Or maybe phy was isolated? */ | |
  331. int ctl = phy_read(phydev, MII_BMCR); | |
  332. | |
  333. if (ctl < ) | |
  334. return ctl; | |
  335. | |
  336. if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) | |
  337. result = ; /* do restart aneg */ | |
  338. } | |
  339. | |
  340. /* Only restart aneg if we are advertising something different | |
  341. * than we were before. */ | |
  342. if (result > ) | |
  343. result = genphy_restart_aneg(phydev); | |
  344. | |
  345. return result; | |
  346. } | |
  347. EXPORT_SYMBOL(genphy_config_aneg); | |
  348. | |
  349. int genphy_read_status(struct phy_device *phydev) <----------+ |
  350. { |
  351. int adv; |
  352. int err; |
  353. int lpa; |
  354. int lpagb = ; |
  355. |
  356. /* Update the link, but return if there |
  357. * was an error */ |
  358. err = genphy_update_link(phydev); |
  359. if (err) |
  360. return err; |
  361. |
  362. if (AUTONEG_ENABLE == phydev->autoneg) { |
  363. if (phydev->supported & (SUPPORTED_1000baseT_Half |
  364. | SUPPORTED_1000baseT_Full)) { |
  365. lpagb = phy_read(phydev, MII_STAT1000); |
  366. |
  367. if (lpagb < ) |
  368. return lpagb; |
  369. |
  370. adv = phy_read(phydev, MII_CTRL1000); |
  371. |
  372. if (adv < ) |
  373. return adv; |
  374. |
  375. lpagb &= adv << ; |
  376. } |
  377. |
  378. lpa = phy_read(phydev, MII_LPA); |
  379. |
  380. if (lpa < ) |
  381. return lpa; |
  382. |
  383. adv = phy_read(phydev, MII_ADVERTISE); |
  384. |
  385. if (adv < ) |
  386. return adv; |
  387. |
  388. lpa &= adv; |
  389. |
  390. phydev->speed = SPEED_10; |
  391. phydev->duplex = DUPLEX_HALF; |
  392. phydev->pause = phydev->asym_pause = ; |
  393. |
  394. if (lpagb & (LPA_1000FULL | LPA_1000HALF)) { |
  395. phydev->speed = SPEED_1000; |
  396. |
  397. if (lpagb & LPA_1000FULL) |
  398. phydev->duplex = DUPLEX_FULL; |
  399. } else if (lpa & (LPA_100FULL | LPA_100HALF)) { |
  400. phydev->speed = SPEED_100; |
  401. |
  402. if (lpa & LPA_100FULL) |
  403. phydev->duplex = DUPLEX_FULL; |
  404. } else |
  405. if (lpa & LPA_10FULL) |
  406. phydev->duplex = DUPLEX_FULL; |
  407. |
  408. if (phydev->duplex == DUPLEX_FULL){ |
  409. phydev->pause = lpa & LPA_PAUSE_CAP ? : ; |
  410. phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? : ; |
  411. } |
  412. } else { |
  413. int bmcr = phy_read(phydev, MII_BMCR); |
  414. if (bmcr < ) |
  415. return bmcr; |
  416. |
  417. if (bmcr & BMCR_FULLDPLX) |
  418. phydev->duplex = DUPLEX_FULL; |
  419. else |
  420. phydev->duplex = DUPLEX_HALF; |
  421. |
  422. if (bmcr & BMCR_SPEED1000) |
  423. phydev->speed = SPEED_1000; |
  424. else if (bmcr & BMCR_SPEED100) |
  425. phydev->speed = SPEED_100; |
  426. else |
  427. phydev->speed = SPEED_10; |
  428. |
  429. phydev->pause = phydev->asym_pause = ; |
  430. } |
  431. |
  432. return ; |
  433. } |
  434. EXPORT_SYMBOL(genphy_read_status); |
  435. |
  436. |
  437. static int smsc_phy_config_init(struct phy_device *phydev) <---------+
  438. {
  439. printk("zengjf check position %s.\n", __func__);
  440. #if 0 // 这段代码会导致PHY无法自动识别到网线插入
  441. int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
  442. if (rc < )
  443. return rc;
  444.  
  445. /* Enable energy detect mode for this SMSC Transceivers */
  446. rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
  447. rc | MII_LAN83C185_EDPWRDOWN);
  448. if (rc < )
  449. return rc;
  450. #endif
  451.  
  452. return smsc_phy_ack_interrupt (phydev);
  453. }

OK335xS LAN8710 phy driver hacking的更多相关文章

  1. OK335xS davinci mdio driver hacking

    /******************************************************************************* * OK335xS davinci m ...

  2. OK335xS pwm buzzer Linux driver hacking

    /**************************************************************************** * OK335xS pwm buzzer L ...

  3. OK335xS knob driver hacking

    /************************************************************************* * OK335xS knob driver hac ...

  4. OK335xS pwm device register hacking

    /************************************************************************* * OK335xS pwm device regi ...

  5. OK335xS I2C device registe hacking

    /*************************************************************************** * OK335xS I2C device re ...

  6. I.MX6 PWM buzzer driver hacking with Demo test

    /***************************************************************************** * I.MX6 PWM buzzer dr ...

  7. OK335xS psplash make-image-header.sh hacking

    /***************************************************************************** * OK335xS psplash mak ...

  8. I.MX6 gpio-keys driver hacking

    /**************************************************************************** * I.MX6 gpio-keys driv ...

  9. I.MX6 bq27441 driver hacking

    /************************************************************************* * I.MX6 bq27441 driver ha ...

随机推荐

  1. 自动化运维——一键安装MySQL

    根据项目需要,前段时间在搞EMM系统各种安装包的自动化部署工作,主要包括一键安装和一键启动\停止功能.总结记录下来,以供后用. 本文主要是自动安装MySQL5.7.11版,Linux版脚本在CentO ...

  2. oracle的function和procedure返回值给shell

    本文演示两个关于如何在shell中调用oracle的function和procedure,并将返回值返回给shell. 1.首在package中创建function和procedure,脚本如下: G ...

  3. Kinetic使用注意点--ellipse

    new Ellipse(config) 参数: config:包含所有配置项的对象. { radius: "半径,可以用数字a.数组[a,b]或对象{x:a,y:b}来表示" } ...

  4. 【多路复用】I/O多路复用

    http://www.tuicool.com/articles/RBvqUz C#下用select方法实现socket服务端

  5. [译] ASP.NET 生命周期 – ASP.NET 请求生命周期(三)

    使用特殊方法处理请求生命周期事件 为了在全局应用类中处理这些事件,我们会创建一个名称以 Application_ 开头,以事件名称结尾的方法,比如 Application_BeginRequest.举 ...

  6. UITableView自定义单元格

    随手笔记: RootViewController代码 #import "RootViewController.h" #import "AddressContact.h&q ...

  7. Java中的break与continue区别

    break跳出当前循环执行循环下面的程序, 如果break出现在嵌套循环的内层循环, 则break语句只会跳出当前层的循环; 当程序执行到continue时时, 则跳过本次循环程序重新回到循环开始继续 ...

  8. 滤镜简单demo(转,供参考)

    NSURL *iamgeUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"default" ...

  9. Fail-fast的原因及解决方法

    [转载]:http://blog.csdn.net/chenssy/article/details/38151189 在JDK的Collection中我们时常会看到类似于这样的话: 例如,ArrayL ...

  10. PAT-乙级-1044. 火星数字(20)

    1044. 火星数字(20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 火星人是以13进制计数的: 地球人的 ...