当前位置:   article > 正文

在 imx6q 上适配 YT8531S

yt8531s

一、环境介绍

我适配的设备使用的是 RJ45 网口,phy 和 soc 之间的接口为 RGMII;

linux 版本:4.1.15

soc:imx6q 

二、硬件检查

1. 检查电源;

VDDL = DVDDL = AVDDL = 1.1V;由 32 脚 LX 输出;
AVDD33 检查是否为 3.3V 供电;
DVDD_RGMII 电压是否符合下表,由 36、37 脚来选择电压;

2. 检查时钟;

检查晶振能否正常输出 25Mhz 的信号;

3. 检查接线;

MDI、RGMII、MDIO、MDC 等是否正确连接;

4. 检查 phy 地址;

28、17 脚没有外部上拉的情况下,默认使用的是内部下拉,即值为 0;

 三、软件调试

1. phy id

phy 匹配过程大致如下,可以在 get_phy_device 函数里面判断是否正确读取 phy id;

  1. fec_probe
  2. fec_enet_mii_init
  3. of_mdiobus_register Loop over the child nodes and register a phy_device for each one
  4. of_mdiobus_register_phy
  5. get_phy_device ---get_phy_id(phy_device_create匹配phy_driver_register) ---- phy_device_create
  6. phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);

可以在 u-boot 处,进入命令行模式,用 mii info 命令来查看是否正确获取 phy id,如果没有获取,多半是 phy 硬件有问题;

正常情况下,在 u-boot 时候,可以获取两个 phy id,一个广播地址 00,一个 phy 地址;
对于 YT8531S 来说,操作广播地址与操作 phy 地址没差别;

硬件复位后,需要延时 100 ms,才能进行 MDIO 读写;

 在 drivers/net/ethernet/freescale/fec_main.c 的 fec_reset_phy 函数尾部加一个 100ms 延时;

  1. static void fec_reset_phy(struct platform_device *pdev)
  2. {
  3. int err, phy_reset;
  4. int msec = 1;
  5. struct device_node *np = pdev->dev.of_node;
  6. if (!np)
  7. return;
  8. err = of_property_read_u32(np, "phy-reset-duration", &msec);
  9. /* A sane reset duration should not be longer than 1s */
  10. if (!err && msec > 1000)
  11. msec = 1;
  12. phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0);
  13. if (!gpio_is_valid(phy_reset))
  14. return;
  15. err = devm_gpio_request_one(&pdev->dev, phy_reset,
  16. GPIOF_OUT_INIT_LOW, "phy-reset");
  17. if (err) {
  18. dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err);
  19. return;
  20. }
  21. msleep(msec);
  22. gpio_set_value(phy_reset, 1);
  23. msleep(100);
  24. }

2. ENET_TX_CLK

对于 imx6q,使用 RGMII 的时候,还需要一个 125M 的时钟;
将 phy 的外部寄存器 0xA012 改为 0x50 即可;

3. dts 和 driver

fec 节点如下:

  1. &fec {
  2. pinctrl-names = "default";
  3. pinctrl-0 = <&pinctrl_enet>;
  4. phy-mode = "rgmii";
  5. phy-reset-gpios = <&gpio1 25 GPIO_ACTIVE_LOW>;
  6. phy-reset-duration = <2>;
  7. fsl,magic-packet;
  8. status = "okay";
  9. };
  10. pinctrl_enet: enetgrp {
  11. fsl,pins = <
  12. MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
  13. MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
  14. MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0
  15. MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0
  16. MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0
  17. MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0
  18. MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0
  19. MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0
  20. MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0
  21. MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0
  22. MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0
  23. MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0
  24. MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0
  25. MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0
  26. MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
  27. /* Phy reset */
  28. MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x000b0
  29. >;

 motorcomm.c 修改如下:

  1. /*
  2. * drivers/net/phy/motorcomm.c
  3. *
  4. * Driver for Motorcomm PHYs
  5. *
  6. * Author: Leilei Zhao <leilei.zhao@motorcomm.com>
  7. *
  8. * Copyright (c) 2019 Motorcomm, Inc.
  9. *
  10. * This program is free software; you can redistribute it and/or modify it
  11. * under the terms of the GNU General Public License as published by the
  12. * Free Software Foundation; either version 2 of the License, or (at your
  13. * option) any later version.
  14. *
  15. * Support : Motorcomm Phys:
  16. * Giga phys: yt8511, yt8521
  17. * 100/10 Phys : yt8512, yt8512b, yt8510
  18. * Automotive 100Mb Phys : yt8010
  19. * Automotive 100/10 hyper range Phys: yt8510
  20. */
  21. #include <linux/kernel.h>
  22. #include <linux/module.h>
  23. #include <linux/phy.h>
  24. #include <linux/motorcomm_phy.h>
  25. #include <linux/of.h>
  26. #include <linux/clk.h>
  27. #ifndef LINUX_VERSION_CODE
  28. #include <linux/version.h>
  29. #else
  30. #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
  31. #endif
  32. /*for wol, 20210604*/
  33. #include <linux/netdevice.h>
  34. #include "yt8614-phy.h"
  35. /**** configuration section begin ***********/
  36. /* if system depends on ethernet packet to restore from sleep, please define this macro to 1
  37. * otherwise, define it to 0.
  38. */
  39. #define SYS_WAKEUP_BASED_ON_ETH_PKT 0
  40. /* to enable system WOL of phy, please define this macro to 1
  41. * otherwise, define it to 0.
  42. * NOTE: this macro will need macro SYS_WAKEUP_BASED_ON_ETH_PKT to set to 1
  43. */
  44. #define YTPHY_ENABLE_WOL 0
  45. /* some GMAC need clock input from PHY, for eg., 125M, please enable this macro
  46. * by degault, it is set to 0
  47. */
  48. #define GMAC_CLOCK_INPUT_NEEDED 0
  49. #define YT8521_PHY_MODE_FIBER 1 //fiber mode only
  50. #define YT8521_PHY_MODE_UTP 2 //utp mode only
  51. #define YT8521_PHY_MODE_POLL 3 //fiber and utp, poll mode
  52. /* please make choice according to system design
  53. * for Fiber only system, please define YT8521_PHY_MODE_CURR 1
  54. * for UTP only system, please define YT8521_PHY_MODE_CURR 2
  55. * for combo system, please define YT8521_PHY_MODE_CURR 3
  56. */
  57. #define YT8521_PHY_MODE_CURR 2
  58. /**** configuration section end ***********/
  59. /* no need to change below */
  60. #if (YTPHY_ENABLE_WOL)
  61. #undef SYS_WAKEUP_BASED_ON_ETH_PKT
  62. #define SYS_WAKEUP_BASED_ON_ETH_PKT 1
  63. #endif
  64. /* workaround for 8521 fiber 100m mode */
  65. static int link_mode_8521 = 0; //0: no link; 1: utp; 32: fiber. traced that 1000m fiber uses 32.
  66. static int link_mode_8614[4] = {0}; //0: no link; 1: utp; 32: fiber. traced that 1000m fiber uses 32.
  67. /* for multiple port phy, base phy address */
  68. static unsigned int yt_mport_base_phy_addr = 0xff; //0xff: invalid;
  69. static unsigned int yt_mport_base_phy_addr_8614 = 0xff; //0xff: invalid;
  70. static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
  71. {
  72. int ret;
  73. int val;
  74. ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
  75. if (ret < 0)
  76. return ret;
  77. val = phy_read(phydev, REG_DEBUG_DATA);
  78. return val;
  79. }
  80. static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
  81. {
  82. int ret;
  83. ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
  84. if (ret < 0)
  85. return ret;
  86. ret = phy_write(phydev, REG_DEBUG_DATA, val);
  87. return ret;
  88. }
  89. int yt8521_soft_reset(struct phy_device *phydev)
  90. {
  91. int ret;
  92. int val;
  93. ytphy_write_ext(phydev, 0xa000, 0);
  94. ret = genphy_soft_reset(phydev);
  95. if (ret < 0)
  96. return ret;
  97. ytphy_write_ext(phydev, 0xa000, 2);
  98. ret = genphy_soft_reset(phydev);
  99. if (ret < 0) {
  100. ytphy_write_ext(phydev, 0xa000, 0);
  101. return ret;
  102. }
  103. ret = ytphy_write_ext(phydev, 0xA012, 0x50);
  104. if (ret < 0)
  105. return ret;
  106. return 0;
  107. }
  108. #if (YTPHY_ENABLE_WOL)
  109. static int ytphy_switch_reg_space(struct phy_device *phydev, int space)
  110. {
  111. int ret;
  112. if (space == YTPHY_REG_SPACE_UTP) {
  113. ret = ytphy_write_ext(phydev, 0xa000, 0);
  114. } else {
  115. ret = ytphy_write_ext(phydev, 0xa000, 2);
  116. }
  117. return ret;
  118. }
  119. static int ytphy_wol_en_cfg(struct phy_device *phydev, ytphy_wol_cfg_t wol_cfg)
  120. {
  121. int ret = 0;
  122. int val = 0;
  123. val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG);
  124. if (val < 0)
  125. return val;
  126. if (wol_cfg.enable) {
  127. val |= YTPHY_WOL_CFG_EN;
  128. if (wol_cfg.type == YTPHY_WOL_TYPE_LEVEL) {
  129. val &= ~YTPHY_WOL_CFG_TYPE;
  130. val &= ~YTPHY_WOL_CFG_INTR_SEL;
  131. } else if (wol_cfg.type == YTPHY_WOL_TYPE_PULSE) {
  132. val |= YTPHY_WOL_CFG_TYPE;
  133. val |= YTPHY_WOL_CFG_INTR_SEL;
  134. if (wol_cfg.width == YTPHY_WOL_WIDTH_84MS) {
  135. val &= ~YTPHY_WOL_CFG_WIDTH1;
  136. val &= ~YTPHY_WOL_CFG_WIDTH2;
  137. } else if (wol_cfg.width == YTPHY_WOL_WIDTH_168MS) {
  138. val |= YTPHY_WOL_CFG_WIDTH1;
  139. val &= ~YTPHY_WOL_CFG_WIDTH2;
  140. } else if (wol_cfg.width == YTPHY_WOL_WIDTH_336MS) {
  141. val &= ~YTPHY_WOL_CFG_WIDTH1;
  142. val |= YTPHY_WOL_CFG_WIDTH2;
  143. } else if (wol_cfg.width == YTPHY_WOL_WIDTH_672MS) {
  144. val |= YTPHY_WOL_CFG_WIDTH1;
  145. val |= YTPHY_WOL_CFG_WIDTH2;
  146. }
  147. }
  148. } else {
  149. val &= ~YTPHY_WOL_CFG_EN;
  150. val &= ~YTPHY_WOL_CFG_INTR_SEL;
  151. }
  152. ret = ytphy_write_ext(phydev, YTPHY_WOL_CFG_REG, val);
  153. if (ret < 0)
  154. return ret;
  155. return 0;
  156. }
  157. static void ytphy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
  158. {
  159. int val = 0;
  160. wol->supported = WAKE_MAGIC;
  161. wol->wolopts = 0;
  162. val = ytphy_read_ext(phydev, YTPHY_WOL_CFG_REG);
  163. if (val < 0)
  164. return;
  165. if (val & YTPHY_WOL_CFG_EN)
  166. wol->wolopts |= WAKE_MAGIC;
  167. return;
  168. }
  169. static int ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
  170. {
  171. int ret, pre_page, val;
  172. ytphy_wol_cfg_t wol_cfg;
  173. struct net_device *p_attached_dev = phydev->attached_dev;
  174. memset(&wol_cfg, 0, sizeof(ytphy_wol_cfg_t));
  175. pre_page = ytphy_read_ext(phydev, 0xa000);
  176. if (pre_page < 0)
  177. return pre_page;
  178. /* Switch to phy UTP page */
  179. ret = ytphy_switch_reg_space(phydev, YTPHY_REG_SPACE_UTP);
  180. if (ret < 0)
  181. return ret;
  182. if (wol->wolopts & WAKE_MAGIC) {
  183. /* Enable the WOL interrupt */
  184. val = phy_read(phydev, YTPHY_UTP_INTR_REG);
  185. val |= YTPHY_WOL_INTR;
  186. ret = phy_write(phydev, YTPHY_UTP_INTR_REG, val);
  187. if (ret < 0)
  188. return ret;
  189. /* Set the WOL config */
  190. wol_cfg.enable = 1; //enable
  191. wol_cfg.type = YTPHY_WOL_TYPE_PULSE;
  192. wol_cfg.width = YTPHY_WOL_WIDTH_672MS;
  193. ret = ytphy_wol_en_cfg(phydev, wol_cfg);
  194. if (ret < 0)
  195. return ret;
  196. /* Store the device address for the magic packet */
  197. ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR2,
  198. ((p_attached_dev->dev_addr[0] << 8) |
  199. p_attached_dev->dev_addr[1]));
  200. if (ret < 0)
  201. return ret;
  202. ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR1,
  203. ((p_attached_dev->dev_addr[2] << 8) |
  204. p_attached_dev->dev_addr[3]));
  205. if (ret < 0)
  206. return ret;
  207. ret = ytphy_write_ext(phydev, YTPHY_MAGIC_PACKET_MAC_ADDR0,
  208. ((p_attached_dev->dev_addr[4] << 8) |
  209. p_attached_dev->dev_addr[5]));
  210. if (ret < 0)
  211. return ret;
  212. } else {
  213. wol_cfg.enable = 0; //disable
  214. wol_cfg.type = YTPHY_WOL_TYPE_MAX;
  215. wol_cfg.width = YTPHY_WOL_WIDTH_MAX;
  216. ret = ytphy_wol_en_cfg(phydev, wol_cfg);
  217. if (ret < 0)
  218. return ret;
  219. }
  220. /* Recover to previous register space page */
  221. ret = ytphy_switch_reg_space(phydev, pre_page);
  222. if (ret < 0)
  223. return ret;
  224. return 0;
  225. }
  226. #endif /*(YTPHY_ENABLE_WOL)*/
  227. static int yt8521_config_init(struct phy_device *phydev)
  228. {
  229. int ret;
  230. int val;
  231. phydev->irq = PHY_POLL;
  232. ytphy_write_ext(phydev, 0xa000, 0);
  233. ret = genphy_config_init(phydev);
  234. if (ret < 0)
  235. return ret;
  236. /* disable auto sleep */
  237. val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1);
  238. if (val < 0)
  239. return val;
  240. val &= (~BIT(YT8521_EN_SLEEP_SW_BIT));
  241. ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val);
  242. if (ret < 0)
  243. return ret;
  244. /* enable RXC clock when no wire plug */
  245. ret = ytphy_write_ext(phydev, 0xa000, 0);
  246. if (ret < 0)
  247. return ret;
  248. val = ytphy_read_ext(phydev, 0xc);
  249. if (val < 0)
  250. return val;
  251. val &= ~(1 << 12);
  252. ret = ytphy_write_ext(phydev, 0xc, val);
  253. if (ret < 0)
  254. return ret;
  255. //printk("8521 init call out...\n");
  256. return ret;
  257. }
  258. static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp)
  259. {
  260. int speed_mode, duplex;
  261. int speed = SPEED_UNKNOWN;
  262. //printk("8521 status adjust call in...\n");
  263. duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT;
  264. speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT;
  265. switch (speed_mode) {
  266. case 0:
  267. if (is_utp)
  268. speed = SPEED_10;
  269. break;
  270. case 1:
  271. speed = SPEED_100;
  272. break;
  273. case 2:
  274. speed = SPEED_1000;
  275. break;
  276. case 3:
  277. break;
  278. default:
  279. speed = SPEED_UNKNOWN;
  280. break;
  281. }
  282. phydev->speed = speed;
  283. phydev->duplex = duplex;
  284. //printk("8521 status adjust call out,regval=0x%04x,mode=%s,speed=%dm...\n", val,is_utp?"utp":"fiber", phydev->speed);
  285. return 0;
  286. }
  287. int yt8521_aneg_done (struct phy_device *phydev)
  288. {
  289. //printk("phy..YT8521 AN_done callin,speed=%dm,linkmoded=%d\n", phydev->speed,link_mode_8521);
  290. if ((32 == link_mode_8521) && (SPEED_100 == phydev->speed))
  291. {
  292. return 1/*link_mode_8521*/;
  293. }
  294. return genphy_aneg_done(phydev);
  295. }
  296. static int yt8521_read_status(struct phy_device *phydev)
  297. {
  298. int ret;
  299. volatile int val;
  300. volatile int link;
  301. int link_utp = 0;
  302. /* reading UTP */
  303. ret = ytphy_write_ext(phydev, 0xa000, 0);
  304. if (ret < 0)
  305. return ret;
  306. val = phy_read(phydev, REG_PHY_SPEC_STATUS);
  307. if (val < 0)
  308. return val;
  309. link = val & (BIT(YT8521_LINK_STATUS_BIT));
  310. if (link) {
  311. link_utp = 1;
  312. link_mode_8521 = 1;
  313. yt8521_adjust_status(phydev, val, 1);
  314. } else {
  315. link_utp = 0;
  316. }
  317. if (link_utp) {
  318. phydev->link = 1;
  319. } else {
  320. phydev->link = 0;
  321. link_mode_8521 = 0;
  322. }
  323. if (link_utp) {
  324. ytphy_write_ext(phydev, 0xa000, 0);
  325. }
  326. //printk("8521 read status call out, link=%d, linkmode=%d\n", phydev->link, link_mode_8521 );
  327. return 0;
  328. }
  329. int yt8521_suspend(struct phy_device *phydev)
  330. {
  331. #if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
  332. int value;
  333. #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
  334. mutex_lock(&phydev->lock);
  335. #else
  336. /* no need lock in 4.19 */
  337. #endif
  338. ytphy_write_ext(phydev, 0xa000, 0);
  339. value = phy_read(phydev, MII_BMCR);
  340. phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
  341. ytphy_write_ext(phydev, 0xa000, 2);
  342. value = phy_read(phydev, MII_BMCR);
  343. phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
  344. ytphy_write_ext(phydev, 0xa000, 0);
  345. #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
  346. mutex_unlock(&phydev->lock);
  347. #else
  348. /* no need lock/unlock in 4.19 */
  349. #endif
  350. #endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
  351. return 0;
  352. }
  353. int yt8521_resume(struct phy_device *phydev)
  354. {
  355. #if !(SYS_WAKEUP_BASED_ON_ETH_PKT)
  356. int value;
  357. #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
  358. mutex_lock(&phydev->lock);
  359. #else
  360. /* no need lock/unlock in 4.19 */
  361. #endif
  362. ytphy_write_ext(phydev, 0xa000, 0);
  363. value = phy_read(phydev, MII_BMCR);
  364. phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
  365. ytphy_write_ext(phydev, 0xa000, 2);
  366. value = phy_read(phydev, MII_BMCR);
  367. phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN);
  368. ytphy_write_ext(phydev, 0xa000, 0);
  369. #if ( LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) )
  370. mutex_unlock(&phydev->lock);
  371. #else
  372. /* no need lock/unlock in 4.19 */
  373. #endif
  374. #endif /*!(SYS_WAKEUP_BASED_ON_ETH_PKT)*/
  375. return 0;
  376. }
  377. static struct phy_driver ytphy_drvs[] = {
  378. {
  379. /* same as 8521 */
  380. .phy_id = PHY_ID_YT8531S,
  381. .name = "YT8531S Ethernet",
  382. .phy_id_mask = MOTORCOMM_PHY_ID_MASK,
  383. .features = PHY_BASIC_FEATURES | PHY_GBIT_FEATURES,
  384. .flags = PHY_POLL,
  385. .soft_reset = yt8521_soft_reset,
  386. .config_aneg = genphy_config_aneg,
  387. .aneg_done = yt8521_aneg_done,
  388. .config_init = yt8521_config_init,
  389. .read_status = yt8521_read_status,
  390. .suspend = yt8521_suspend,
  391. .resume = yt8521_resume,
  392. #if (YTPHY_ENABLE_WOL)
  393. .get_wol = &ytphy_get_wol,
  394. .set_wol = &ytphy_set_wol,
  395. #endif
  396. }
  397. };
  398. module_phy_driver(ytphy_drvs);
  399. MODULE_DESCRIPTION("Motorcomm PHY driver");
  400. MODULE_AUTHOR("Leilei Zhao");
  401. MODULE_LICENSE("GPL");
  402. static struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
  403. { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK },
  404. { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK },
  405. { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK },
  406. { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK },
  407. { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK },
  408. { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK },
  409. { PHY_ID_YT8531S, MOTORCOMM_PHY_ID_MASK },
  410. { PHY_ID_YT8531, MOTORCOMM_PHY_ID_MASK },
  411. { PHY_ID_YT8618, MOTORCOMM_MPHY_ID_MASK },
  412. { PHY_ID_YT8614, MOTORCOMM_MPHY_ID_MASK_8614 },
  413. { }
  414. };
  415. MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/967006
推荐阅读
相关标签
  

闽ICP备14008679号