/* * drivers/net/phy/motorcomm.c * * Driver for Motorcomm PHYs * * Author: Leilei Zhao * * Copyright (c) 2019 Motorcomm, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * Support : Motorcomm Phys: * Giga phys: yt8511, yt8521 * 100/10 Phys : yt8512, yt8512b, yt8510 * Automotive 100Mb Phys : yt8010 * Automotive 100/10 hyper range Phys: yt8510 */ #include #include #include #include #include #include "motorcomm_phy.h" static int ytphy_read_ext(struct phy_device *phydev, u32 regnum) { int ret; int val; ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum); if (ret < 0) return ret; val = phy_read(phydev, REG_DEBUG_DATA); return val; } static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val) { int ret; ret = phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum); if (ret < 0) return ret; ret = phy_write(phydev, REG_DEBUG_DATA, val); return ret; } static int yt8010_config_aneg(struct phy_device *phydev) { phydev->speed = SPEED_100; return 0; } static int yt8511_led_init(struct phy_device *phydev) { u32 val; struct device_node *of_node; of_node = phydev->mdio.dev.of_node; if (!of_node) return 0; if (!of_property_read_u32(of_node, "motorcomm,led0_cfg", &val)) { val &= 0xffff; ytphy_write_ext(phydev, YT8511_LED0_CFG, val); } if (!of_property_read_u32(of_node, "motorcomm,led1_cfg", &val)) { val &= 0xffff; ytphy_write_ext(phydev, YT8511_LED1_CFG, val); } if (!of_property_read_u32(of_node, "motorcomm,led2_cfg", &val)) { val &= 0xffff; ytphy_write_ext(phydev, YT8511_LED2_CFG, val); } return 0; } static int yt8511_config_init(struct phy_device *phydev) { int ret; int val; phydev->irq = PHY_POLL; /* disable auto sleep */ val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); if (val < 0) return val; val &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val); if (ret < 0) return ret; /* enable RXC clock when no wire plug */ ret = ytphy_write_ext(phydev, 0xa000, 0); if (ret < 0) return ret; val = ytphy_read_ext(phydev, 0xc); if (val < 0) return val; val &= ~(1 << 12); /*phy outpu 125MHz*/ val |= (1 << 1); val |= (1 << 2); /*tx delay 850ps tx delay= N*150-350 ps*/ // val |= (1 << 4); // val |= (1 << 5); // val |= (1 << 6); val |= (1 << 7); ret = ytphy_write_ext(phydev, 0xc, val); if (ret < 0) return ret; yt8511_led_init(phydev); return ret; } static int yt8512_clk_init(struct phy_device *phydev) { int ret; int val; val = ytphy_read_ext(phydev, YT8512_EXTREG_AFE_PLL); if (val < 0) return val; val |= YT8512_CONFIG_PLL_REFCLK_SEL_EN; ret = ytphy_write_ext(phydev, YT8512_EXTREG_AFE_PLL, val); if (ret < 0) return ret; val = ytphy_read_ext(phydev, YT8512_EXTREG_EXTEND_COMBO); if (val < 0) return val; val |= YT8512_CONTROL1_RMII_EN; ret = ytphy_write_ext(phydev, YT8512_EXTREG_EXTEND_COMBO, val); if (ret < 0) return ret; val = phy_read(phydev, MII_BMCR); if (val < 0) return val; val |= YT_SOFTWARE_RESET; ret = phy_write(phydev, MII_BMCR, val); return ret; } static int yt8512_led_init(struct phy_device *phydev) { int ret; int val; int mask; val = ytphy_read_ext(phydev, YT8512_EXTREG_LED0); if (val < 0) return val; val |= YT8512_LED0_ACT_BLK_IND; mask = YT8512_LED0_DIS_LED_AN_TRY | YT8512_LED0_BT_BLK_EN | YT8512_LED0_HT_BLK_EN | YT8512_LED0_COL_BLK_EN | YT8512_LED0_BT_ON_EN; val &= ~mask; ret = ytphy_write_ext(phydev, YT8512_EXTREG_LED0, val); if (ret < 0) return ret; val = ytphy_read_ext(phydev, YT8512_EXTREG_LED1); if (val < 0) return val; val |= YT8512_LED1_BT_ON_EN; mask = YT8512_LED1_TXACT_BLK_EN | YT8512_LED1_RXACT_BLK_EN; val &= ~mask; ret = ytphy_write_ext(phydev, YT8512_LED1_BT_ON_EN, val); return ret; } static int yt8512_config_init(struct phy_device *phydev) { int ret; int val; ret = yt8512_clk_init(phydev); if (ret < 0) return ret; ret = yt8512_led_init(phydev); /* disable auto sleep */ val = ytphy_read_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1); if (val < 0) return val; val &= (~BIT(YT8512_EN_SLEEP_SW_BIT)); ret = ytphy_write_ext(phydev, YT8512_EXTREG_SLEEP_CONTROL1, val); if (ret < 0) return ret; return ret; } static int yt8512_read_status(struct phy_device *phydev) { int ret; int val; int speed, speed_mode, duplex; ret = genphy_update_link(phydev); if (ret) return ret; val = phy_read(phydev, REG_PHY_SPEC_STATUS); if (val < 0) return val; duplex = (val & YT8512_DUPLEX) >> YT8512_DUPLEX_BIT; speed_mode = (val & YT8512_SPEED_MODE) >> YT8512_SPEED_MODE_BIT; switch (speed_mode) { case 0: speed = SPEED_10; break; case 1: speed = SPEED_100; break; case 2: case 3: default: speed = SPEED_UNKNOWN; break; } phydev->speed = speed; phydev->duplex = duplex; return 0; } static int yt8521_config_init(struct phy_device *phydev) { int ret; int val; phydev->irq = PHY_POLL; /* disable auto sleep */ val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); if (val < 0) return val; val &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val); if (ret < 0) return ret; /* enable RXC clock when no wire plug */ ret = ytphy_write_ext(phydev, 0xa000, 0); if (ret < 0) return ret; val = ytphy_read_ext(phydev, 0xc); if (val < 0) return val; val &= ~(1 << 12); /*tx delay 850ps tx delay= N*150-350 ps*/ // val |= (1 << 4); // val |= (1 << 5); // val |= (1 << 6); val |= (1 << 7); ret = ytphy_write_ext(phydev, 0xc, val); if (ret < 0) return ret; return ret; } static int yt8521_adjust_status(struct phy_device *phydev, int val, int is_utp) { int speed_mode, duplex; int speed = SPEED_UNKNOWN; duplex = (val & YT8512_DUPLEX) >> YT8521_DUPLEX_BIT; speed_mode = (val & YT8521_SPEED_MODE) >> YT8521_SPEED_MODE_BIT; switch (speed_mode) { case 0: if (is_utp) speed = SPEED_10; break; case 1: speed = SPEED_100; break; case 2: speed = SPEED_1000; break; case 3: break; default: speed = SPEED_UNKNOWN; break; } phydev->speed = speed; phydev->duplex = duplex; return 0; } static int yt8521_read_status(struct phy_device *phydev) { int ret; int val; int link; int link_utp, link_fiber; /* reading UTP */ ret = ytphy_write_ext(phydev, 0xa000, 0); if (ret < 0) return ret; val = phy_read(phydev, REG_PHY_SPEC_STATUS); if (val < 0) return val; link = val & (BIT(YT8521_LINK_STATUS_BIT)); if (link) { link_utp = 1; yt8521_adjust_status(phydev, val, 1); } else { link_utp = 0; } /* reading Fiber */ ret = ytphy_write_ext(phydev, 0xa000, 2); if (ret < 0) return ret; val = phy_read(phydev, REG_PHY_SPEC_STATUS); if (val < 0) return val; link = val & (BIT(YT8521_LINK_STATUS_BIT)); if (link) { link_fiber = 1; yt8521_adjust_status(phydev, val, 0); } else { link_fiber = 0; } if (link_utp || link_fiber) { phydev->link = 1; } else { phydev->link = 0; } if (link_utp) { ytphy_write_ext(phydev, 0xa000, 0); } return 0; } static int yt8531_led_init(struct phy_device *phydev) { u32 val; struct device_node *of_node; of_node = phydev->mdio.dev.of_node; if (!of_node) return 0; if (!of_property_read_u32(of_node, "motorcomm,led0_cfg", &val)) { val &= 0xffff; ytphy_write_ext(phydev, YT8531_LED0_CFG, val); } if (!of_property_read_u32(of_node, "motorcomm,led1_cfg", &val)) { val &= 0xffff; ytphy_write_ext(phydev, YT8531_LED1_CFG, val); } if (!of_property_read_u32(of_node, "motorcomm,led2_cfg", &val)) { val &= 0xffff; ytphy_write_ext(phydev, YT8531_LED2_CFG, val); } return 0; } static int yt8531H_config_init(struct phy_device *phydev) { // 这是 yt8511_config_init 来的 int ret; int val; phydev->irq = PHY_POLL; /* disable auto sleep */ val = ytphy_read_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1); if (val < 0) return val; val &= (~BIT(YT8521_EN_SLEEP_SW_BIT)); ret = ytphy_write_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1, val); if (ret < 0) return ret; /* enable RXC clock when no wire plug */ ret = ytphy_write_ext(phydev, 0xa000, 0); if (ret < 0) return ret; val = ytphy_read_ext(phydev, 0xc); if (val < 0) return val; val &= ~(1 << 12); /*phy outpu 125MHz*/ val |= (1 << 1); val |= (1 << 2); /*tx delay 850ps tx delay= N*150-350 ps*/ // val |= (1 << 4); // val |= (1 << 5); // val |= (1 << 6); val |= (1 << 7); ret = ytphy_write_ext(phydev, 0xc, val); if (ret < 0) return ret; yt8531_led_init(phydev); return ret; } static struct phy_driver ytphy_drvs[] = { { .phy_id = PHY_ID_YT8010, .name = "YT8010 Automotive Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_BASIC_FEATURES, .flags = PHY_IS_INTERNAL, .config_aneg = yt8010_config_aneg, .read_status = genphy_read_status, }, { .phy_id = PHY_ID_YT8510, .name = "YT8510 100/10Mb Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_BASIC_FEATURES, .flags = PHY_IS_INTERNAL, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { .phy_id = PHY_ID_YT8511, .name = "YT8511 Gigabit Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_GBIT_FEATURES, .flags = PHY_IS_INTERNAL, .config_aneg = genphy_config_aneg, .config_init = yt8511_config_init, .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, }, { .phy_id = PHY_ID_YT8512, .name = "YT8512 Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_BASIC_FEATURES, .flags = PHY_IS_INTERNAL, .config_aneg = genphy_config_aneg, .config_init = yt8512_config_init, .read_status = yt8512_read_status, .suspend = genphy_suspend, .resume = genphy_resume, }, { .phy_id = PHY_ID_YT8512B, .name = "YT8512B Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_BASIC_FEATURES, .flags = PHY_IS_INTERNAL, .config_aneg = genphy_config_aneg, .config_init = yt8512_config_init, .read_status = yt8512_read_status, .suspend = genphy_suspend, .resume = genphy_resume, }, { .phy_id = PHY_ID_YT8521, .name = "YT8521 Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_GBIT_FEATURES, .flags = PHY_POLL, // .config_aneg = genphy_config_aneg, .config_init = yt8521_config_init, .read_status = yt8521_read_status, .suspend = genphy_suspend, .resume = genphy_resume, }, { .phy_id = PHY_ID_YT8531, .name = "YT8531 Gigabit Ethernet", .phy_id_mask = MOTORCOMM_PHY_ID_MASK, .features = PHY_GBIT_FEATURES, .flags = PHY_POLL, //.config_aneg = genphy_config_aneg, .config_init = yt8531H_config_init, .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, }, }; module_phy_driver(ytphy_drvs); MODULE_DESCRIPTION("Motorcomm PHY driver"); MODULE_AUTHOR("Leilei Zhao"); MODULE_LICENSE("GPL"); static struct mdio_device_id __maybe_unused motorcomm_tbl[] = { { PHY_ID_YT8010, MOTORCOMM_PHY_ID_MASK }, { PHY_ID_YT8510, MOTORCOMM_PHY_ID_MASK }, { PHY_ID_YT8511, MOTORCOMM_PHY_ID_MASK }, { PHY_ID_YT8512, MOTORCOMM_PHY_ID_MASK }, { PHY_ID_YT8512B, MOTORCOMM_PHY_ID_MASK }, { PHY_ID_YT8521, MOTORCOMM_PHY_ID_MASK }, { PHY_ID_YT8531, MOTORCOMM_PHY_ID_MASK }, { } }; MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);