1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (C) 2021 Maxlinear Corporation
3 * Copyright (C) 2020 Intel Corporation
5 * Drivers for Maxlinear Ethernet GPY
9 #include <linux/module.h>
10 #include <linux/bitfield.h>
11 #include <linux/phy.h>
12 #include <linux/netdevice.h>
15 #define PHY_ID_GPYx15B_MASK 0xFFFFFFFC
16 #define PHY_ID_GPY21xB_MASK 0xFFFFFFF9
17 #define PHY_ID_GPY2xx 0x67C9DC00
18 #define PHY_ID_GPY115B 0x67C9DF00
19 #define PHY_ID_GPY115C 0x67C9DF10
20 #define PHY_ID_GPY211B 0x67C9DE08
21 #define PHY_ID_GPY211C 0x67C9DE10
22 #define PHY_ID_GPY212B 0x67C9DE09
23 #define PHY_ID_GPY212C 0x67C9DE20
24 #define PHY_ID_GPY215B 0x67C9DF04
25 #define PHY_ID_GPY215C 0x67C9DF20
26 #define PHY_ID_GPY241B 0x67C9DE40
27 #define PHY_ID_GPY241BM 0x67C9DE80
28 #define PHY_ID_GPY245B 0x67C9DEC0
30 #define PHY_MIISTAT 0x18 /* MII state */
31 #define PHY_IMASK 0x19 /* interrupt mask */
32 #define PHY_ISTAT 0x1A /* interrupt status */
33 #define PHY_FWV 0x1E /* firmware version */
35 #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
36 #define PHY_MIISTAT_DPX BIT(3)
37 #define PHY_MIISTAT_LS BIT(10)
39 #define PHY_MIISTAT_SPD_10 0
40 #define PHY_MIISTAT_SPD_100 1
41 #define PHY_MIISTAT_SPD_1000 2
42 #define PHY_MIISTAT_SPD_2500 4
44 #define PHY_IMASK_WOL BIT(15) /* Wake-on-LAN */
45 #define PHY_IMASK_ANC BIT(10) /* Auto-Neg complete */
46 #define PHY_IMASK_ADSC BIT(5) /* Link auto-downspeed detect */
47 #define PHY_IMASK_DXMC BIT(2) /* Duplex mode change */
48 #define PHY_IMASK_LSPC BIT(1) /* Link speed change */
49 #define PHY_IMASK_LSTC BIT(0) /* Link state change */
50 #define PHY_IMASK_MASK (PHY_IMASK_LSTC | \
56 #define PHY_FWV_REL_MASK BIT(15)
57 #define PHY_FWV_TYPE_MASK GENMASK(11, 8)
58 #define PHY_FWV_MINOR_MASK GENMASK(7, 0)
61 #define VSPEC1_SGMII_CTRL 0x08
62 #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */
63 #define VSPEC1_SGMII_CTRL_ANRS BIT(9) /* Restart Aneg */
64 #define VSPEC1_SGMII_ANEN_ANRS (VSPEC1_SGMII_CTRL_ANEN | \
65 VSPEC1_SGMII_CTRL_ANRS)
68 #define VPSPEC2_WOL_CTL 0x0E06
69 #define VPSPEC2_WOL_AD01 0x0E08
70 #define VPSPEC2_WOL_AD23 0x0E09
71 #define VPSPEC2_WOL_AD45 0x0E0A
77 } ver_need_sgmii_reaneg[] = {
83 static int gpy_config_init(struct phy_device *phydev)
87 /* Mask all interrupts */
88 ret = phy_write(phydev, PHY_IMASK, 0);
92 /* Clear all pending interrupts */
93 ret = phy_read(phydev, PHY_ISTAT);
94 return ret < 0 ? ret : 0;
97 static int gpy_probe(struct phy_device *phydev)
101 if (!phydev->is_c45) {
102 ret = phy_get_c45_ids(phydev);
107 /* Show GPY PHY FW version in dmesg */
108 ret = phy_read(phydev, PHY_FWV);
112 phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret,
113 (ret & PHY_FWV_REL_MASK) ? "release" : "test");
118 static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
120 int fw_ver, fw_type, fw_minor;
123 fw_ver = phy_read(phydev, PHY_FWV);
127 fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
128 fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
130 for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
131 if (fw_type != ver_need_sgmii_reaneg[i].type)
133 if (fw_minor < ver_need_sgmii_reaneg[i].minor)
141 static bool gpy_2500basex_chk(struct phy_device *phydev)
145 ret = phy_read(phydev, PHY_MIISTAT);
147 phydev_err(phydev, "Error: MDIO register access failed: %d\n",
152 if (!(ret & PHY_MIISTAT_LS) ||
153 FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
156 phydev->speed = SPEED_2500;
157 phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
158 phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
159 VSPEC1_SGMII_CTRL_ANEN, 0);
163 static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
167 ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
169 phydev_err(phydev, "Error: MMD register access failed: %d\n",
174 return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
177 static int gpy_config_aneg(struct phy_device *phydev)
179 bool changed = false;
183 if (phydev->autoneg == AUTONEG_DISABLE) {
184 /* Configure half duplex with genphy_setup_forced,
185 * because genphy_c45_pma_setup_forced does not support.
187 return phydev->duplex != DUPLEX_FULL
188 ? genphy_setup_forced(phydev)
189 : genphy_c45_pma_setup_forced(phydev);
192 ret = genphy_c45_an_config_aneg(phydev);
198 adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
199 ret = phy_modify_changed(phydev, MII_CTRL1000,
200 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
207 ret = genphy_c45_check_and_restart_aneg(phydev, changed);
211 if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
212 phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
215 /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
218 if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
219 !gpy_sgmii_aneg_en(phydev))
222 /* There is a design constraint in GPY2xx device where SGMII AN is
223 * only triggered when there is change of speed. If, PHY link
224 * partner`s speed is still same even after PHY TPI is down and up
225 * again, SGMII AN is not triggered and hence no new in-band message
226 * from GPY to MAC side SGMII.
227 * This could cause an issue during power up, when PHY is up prior to
228 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
229 * wouldn`t receive new in-band message from GPY with correct link
230 * status, speed and duplex info.
232 * 1) If PHY is already up and TPI link status is still down (such as
233 * hard reboot), TPI link status is polled for 4 seconds before
234 * retriggerring SGMII AN.
235 * 2) If PHY is already up and TPI link status is also up (such as soft
236 * reboot), polling of TPI link status is not needed and SGMII AN is
237 * immediately retriggered.
238 * 3) Other conditions such as PHY is down, speed change etc, skip
239 * retriggering SGMII AN. Note: in case of speed change, GPY FW will
243 if (phydev->state != PHY_UP)
246 ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
247 20000, 4000000, false);
248 if (ret == -ETIMEDOUT)
253 /* Trigger SGMII AN. */
254 return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
255 VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
258 static void gpy_update_interface(struct phy_device *phydev)
262 /* Interface mode is fixed for USXGMII and integrated PHY */
263 if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
264 phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
267 /* Automatically switch SERDES interface between SGMII and 2500-BaseX
268 * according to speed. Disable ANEG in 2500-BaseX mode.
270 switch (phydev->speed) {
272 phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
273 ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
274 VSPEC1_SGMII_CTRL_ANEN, 0);
277 "Error: Disable of SGMII ANEG failed: %d\n",
283 phydev->interface = PHY_INTERFACE_MODE_SGMII;
284 if (gpy_sgmii_aneg_en(phydev))
286 /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
287 * if ANEG is disabled (in 2500-BaseX mode).
289 ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
290 VSPEC1_SGMII_ANEN_ANRS,
291 VSPEC1_SGMII_ANEN_ANRS);
294 "Error: Enable of SGMII ANEG failed: %d\n",
300 static int gpy_read_status(struct phy_device *phydev)
304 ret = genphy_update_link(phydev);
308 phydev->speed = SPEED_UNKNOWN;
309 phydev->duplex = DUPLEX_UNKNOWN;
311 phydev->asym_pause = 0;
313 if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
314 ret = genphy_c45_read_lpa(phydev);
318 /* Read the link partner's 1G advertisement */
319 ret = phy_read(phydev, MII_STAT1000);
322 mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
323 } else if (phydev->autoneg == AUTONEG_DISABLE) {
324 linkmode_zero(phydev->lp_advertising);
327 ret = phy_read(phydev, PHY_MIISTAT);
331 phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
332 phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
333 switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
334 case PHY_MIISTAT_SPD_10:
335 phydev->speed = SPEED_10;
337 case PHY_MIISTAT_SPD_100:
338 phydev->speed = SPEED_100;
340 case PHY_MIISTAT_SPD_1000:
341 phydev->speed = SPEED_1000;
343 case PHY_MIISTAT_SPD_2500:
344 phydev->speed = SPEED_2500;
349 gpy_update_interface(phydev);
354 static int gpy_config_intr(struct phy_device *phydev)
358 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
359 mask = PHY_IMASK_MASK;
361 return phy_write(phydev, PHY_IMASK, mask);
364 static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
368 reg = phy_read(phydev, PHY_ISTAT);
374 if (!(reg & PHY_IMASK_MASK))
377 phy_trigger_machine(phydev);
382 static int gpy_set_wol(struct phy_device *phydev,
383 struct ethtool_wolinfo *wol)
385 struct net_device *attach_dev = phydev->attached_dev;
388 if (wol->wolopts & WAKE_MAGIC) {
389 /* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
390 * VPSPEC2_WOL_AD45 = Byte0:Byte1
391 * VPSPEC2_WOL_AD23 = Byte2:Byte3
392 * VPSPEC2_WOL_AD01 = Byte4:Byte5
394 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
396 ((attach_dev->dev_addr[0] << 8) |
397 attach_dev->dev_addr[1]));
401 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
403 ((attach_dev->dev_addr[2] << 8) |
404 attach_dev->dev_addr[3]));
408 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
410 ((attach_dev->dev_addr[4] << 8) |
411 attach_dev->dev_addr[5]));
415 /* Enable the WOL interrupt */
416 ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
420 /* Enable magic packet matching */
421 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
427 /* Clear the interrupt status register.
428 * Only WoL is enabled so clear all.
430 ret = phy_read(phydev, PHY_ISTAT);
434 /* Disable magic packet matching */
435 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
442 if (wol->wolopts & WAKE_PHY) {
443 /* Enable the link state change interrupt */
444 ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
448 /* Clear the interrupt status register */
449 ret = phy_read(phydev, PHY_ISTAT);
453 if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
454 phy_trigger_machine(phydev);
459 /* Disable the link state change interrupt */
460 return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
463 static void gpy_get_wol(struct phy_device *phydev,
464 struct ethtool_wolinfo *wol)
468 wol->supported = WAKE_MAGIC | WAKE_PHY;
471 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
473 wol->wolopts |= WAKE_MAGIC;
475 ret = phy_read(phydev, PHY_IMASK);
476 if (ret & PHY_IMASK_LSTC)
477 wol->wolopts |= WAKE_PHY;
480 static int gpy_loopback(struct phy_device *phydev, bool enable)
484 ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
485 enable ? BMCR_LOOPBACK : 0);
487 /* It takes some time for PHY device to switch
488 * into/out-of loopback mode.
496 static struct phy_driver gpy_drivers[] = {
498 PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
499 .name = "Maxlinear Ethernet GPY2xx",
500 .get_features = genphy_c45_pma_read_abilities,
501 .config_init = gpy_config_init,
503 .suspend = genphy_suspend,
504 .resume = genphy_resume,
505 .config_aneg = gpy_config_aneg,
506 .aneg_done = genphy_c45_aneg_done,
507 .read_status = gpy_read_status,
508 .config_intr = gpy_config_intr,
509 .handle_interrupt = gpy_handle_interrupt,
510 .set_wol = gpy_set_wol,
511 .get_wol = gpy_get_wol,
512 .set_loopback = gpy_loopback,
515 .phy_id = PHY_ID_GPY115B,
516 .phy_id_mask = PHY_ID_GPYx15B_MASK,
517 .name = "Maxlinear Ethernet GPY115B",
518 .get_features = genphy_c45_pma_read_abilities,
519 .config_init = gpy_config_init,
521 .suspend = genphy_suspend,
522 .resume = genphy_resume,
523 .config_aneg = gpy_config_aneg,
524 .aneg_done = genphy_c45_aneg_done,
525 .read_status = gpy_read_status,
526 .config_intr = gpy_config_intr,
527 .handle_interrupt = gpy_handle_interrupt,
528 .set_wol = gpy_set_wol,
529 .get_wol = gpy_get_wol,
530 .set_loopback = gpy_loopback,
533 PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
534 .name = "Maxlinear Ethernet GPY115C",
535 .get_features = genphy_c45_pma_read_abilities,
536 .config_init = gpy_config_init,
538 .suspend = genphy_suspend,
539 .resume = genphy_resume,
540 .config_aneg = gpy_config_aneg,
541 .aneg_done = genphy_c45_aneg_done,
542 .read_status = gpy_read_status,
543 .config_intr = gpy_config_intr,
544 .handle_interrupt = gpy_handle_interrupt,
545 .set_wol = gpy_set_wol,
546 .get_wol = gpy_get_wol,
547 .set_loopback = gpy_loopback,
550 .phy_id = PHY_ID_GPY211B,
551 .phy_id_mask = PHY_ID_GPY21xB_MASK,
552 .name = "Maxlinear Ethernet GPY211B",
553 .get_features = genphy_c45_pma_read_abilities,
554 .config_init = gpy_config_init,
556 .suspend = genphy_suspend,
557 .resume = genphy_resume,
558 .config_aneg = gpy_config_aneg,
559 .aneg_done = genphy_c45_aneg_done,
560 .read_status = gpy_read_status,
561 .config_intr = gpy_config_intr,
562 .handle_interrupt = gpy_handle_interrupt,
563 .set_wol = gpy_set_wol,
564 .get_wol = gpy_get_wol,
565 .set_loopback = gpy_loopback,
568 PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
569 .name = "Maxlinear Ethernet GPY211C",
570 .get_features = genphy_c45_pma_read_abilities,
571 .config_init = gpy_config_init,
573 .suspend = genphy_suspend,
574 .resume = genphy_resume,
575 .config_aneg = gpy_config_aneg,
576 .aneg_done = genphy_c45_aneg_done,
577 .read_status = gpy_read_status,
578 .config_intr = gpy_config_intr,
579 .handle_interrupt = gpy_handle_interrupt,
580 .set_wol = gpy_set_wol,
581 .get_wol = gpy_get_wol,
582 .set_loopback = gpy_loopback,
585 .phy_id = PHY_ID_GPY212B,
586 .phy_id_mask = PHY_ID_GPY21xB_MASK,
587 .name = "Maxlinear Ethernet GPY212B",
588 .get_features = genphy_c45_pma_read_abilities,
589 .config_init = gpy_config_init,
591 .suspend = genphy_suspend,
592 .resume = genphy_resume,
593 .config_aneg = gpy_config_aneg,
594 .aneg_done = genphy_c45_aneg_done,
595 .read_status = gpy_read_status,
596 .config_intr = gpy_config_intr,
597 .handle_interrupt = gpy_handle_interrupt,
598 .set_wol = gpy_set_wol,
599 .get_wol = gpy_get_wol,
600 .set_loopback = gpy_loopback,
603 PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
604 .name = "Maxlinear Ethernet GPY212C",
605 .get_features = genphy_c45_pma_read_abilities,
606 .config_init = gpy_config_init,
608 .suspend = genphy_suspend,
609 .resume = genphy_resume,
610 .config_aneg = gpy_config_aneg,
611 .aneg_done = genphy_c45_aneg_done,
612 .read_status = gpy_read_status,
613 .config_intr = gpy_config_intr,
614 .handle_interrupt = gpy_handle_interrupt,
615 .set_wol = gpy_set_wol,
616 .get_wol = gpy_get_wol,
617 .set_loopback = gpy_loopback,
620 .phy_id = PHY_ID_GPY215B,
621 .phy_id_mask = PHY_ID_GPYx15B_MASK,
622 .name = "Maxlinear Ethernet GPY215B",
623 .get_features = genphy_c45_pma_read_abilities,
624 .config_init = gpy_config_init,
626 .suspend = genphy_suspend,
627 .resume = genphy_resume,
628 .config_aneg = gpy_config_aneg,
629 .aneg_done = genphy_c45_aneg_done,
630 .read_status = gpy_read_status,
631 .config_intr = gpy_config_intr,
632 .handle_interrupt = gpy_handle_interrupt,
633 .set_wol = gpy_set_wol,
634 .get_wol = gpy_get_wol,
635 .set_loopback = gpy_loopback,
638 PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
639 .name = "Maxlinear Ethernet GPY215C",
640 .get_features = genphy_c45_pma_read_abilities,
641 .config_init = gpy_config_init,
643 .suspend = genphy_suspend,
644 .resume = genphy_resume,
645 .config_aneg = gpy_config_aneg,
646 .aneg_done = genphy_c45_aneg_done,
647 .read_status = gpy_read_status,
648 .config_intr = gpy_config_intr,
649 .handle_interrupt = gpy_handle_interrupt,
650 .set_wol = gpy_set_wol,
651 .get_wol = gpy_get_wol,
652 .set_loopback = gpy_loopback,
655 PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
656 .name = "Maxlinear Ethernet GPY241B",
657 .get_features = genphy_c45_pma_read_abilities,
658 .config_init = gpy_config_init,
660 .suspend = genphy_suspend,
661 .resume = genphy_resume,
662 .config_aneg = gpy_config_aneg,
663 .aneg_done = genphy_c45_aneg_done,
664 .read_status = gpy_read_status,
665 .config_intr = gpy_config_intr,
666 .handle_interrupt = gpy_handle_interrupt,
667 .set_wol = gpy_set_wol,
668 .get_wol = gpy_get_wol,
669 .set_loopback = gpy_loopback,
672 PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
673 .name = "Maxlinear Ethernet GPY241BM",
674 .get_features = genphy_c45_pma_read_abilities,
675 .config_init = gpy_config_init,
677 .suspend = genphy_suspend,
678 .resume = genphy_resume,
679 .config_aneg = gpy_config_aneg,
680 .aneg_done = genphy_c45_aneg_done,
681 .read_status = gpy_read_status,
682 .config_intr = gpy_config_intr,
683 .handle_interrupt = gpy_handle_interrupt,
684 .set_wol = gpy_set_wol,
685 .get_wol = gpy_get_wol,
686 .set_loopback = gpy_loopback,
689 PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
690 .name = "Maxlinear Ethernet GPY245B",
691 .get_features = genphy_c45_pma_read_abilities,
692 .config_init = gpy_config_init,
694 .suspend = genphy_suspend,
695 .resume = genphy_resume,
696 .config_aneg = gpy_config_aneg,
697 .aneg_done = genphy_c45_aneg_done,
698 .read_status = gpy_read_status,
699 .config_intr = gpy_config_intr,
700 .handle_interrupt = gpy_handle_interrupt,
701 .set_wol = gpy_set_wol,
702 .get_wol = gpy_get_wol,
703 .set_loopback = gpy_loopback,
706 module_phy_driver(gpy_drivers);
708 static struct mdio_device_id __maybe_unused gpy_tbl[] = {
709 {PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
710 {PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
711 {PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
712 {PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
713 {PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
714 {PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
715 {PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
716 {PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
717 {PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
718 {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
719 {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
720 {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
723 MODULE_DEVICE_TABLE(mdio, gpy_tbl);
725 MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
726 MODULE_AUTHOR("Xu Liang");
727 MODULE_LICENSE("GPL");