1dddfadd7SLouis-Alexis Eyraud // SPDX-License-Identifier: GPL-2.0+ 2dddfadd7SLouis-Alexis Eyraud /* 3dddfadd7SLouis-Alexis Eyraud * Airoha Ethernet PHY common library 4dddfadd7SLouis-Alexis Eyraud * 5dddfadd7SLouis-Alexis Eyraud * Copyright (C) 2026 Airoha Technology Corp. 6dddfadd7SLouis-Alexis Eyraud * Copyright (C) 2026 Collabora Ltd. 7dddfadd7SLouis-Alexis Eyraud * Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com> 8dddfadd7SLouis-Alexis Eyraud */ 9dddfadd7SLouis-Alexis Eyraud 10dddfadd7SLouis-Alexis Eyraud #include <linux/export.h> 11dddfadd7SLouis-Alexis Eyraud #include <linux/module.h> 12dddfadd7SLouis-Alexis Eyraud #include <linux/phy.h> 135226bb66SLouis-Alexis Eyraud #include <linux/wordpart.h> 14dddfadd7SLouis-Alexis Eyraud 15dddfadd7SLouis-Alexis Eyraud #include "air_phy_lib.h" 16dddfadd7SLouis-Alexis Eyraud 175226bb66SLouis-Alexis Eyraud static int __air_buckpbus_reg_read(struct phy_device *phydev, 185226bb66SLouis-Alexis Eyraud u32 pbus_address, u32 *pbus_data) 195226bb66SLouis-Alexis Eyraud { 205226bb66SLouis-Alexis Eyraud int pbus_data_low, pbus_data_high; 215226bb66SLouis-Alexis Eyraud int ret; 225226bb66SLouis-Alexis Eyraud 235226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); 245226bb66SLouis-Alexis Eyraud if (ret < 0) 255226bb66SLouis-Alexis Eyraud return ret; 265226bb66SLouis-Alexis Eyraud 275226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, 285226bb66SLouis-Alexis Eyraud upper_16_bits(pbus_address)); 295226bb66SLouis-Alexis Eyraud if (ret < 0) 305226bb66SLouis-Alexis Eyraud return ret; 315226bb66SLouis-Alexis Eyraud 325226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, 335226bb66SLouis-Alexis Eyraud lower_16_bits(pbus_address)); 345226bb66SLouis-Alexis Eyraud if (ret < 0) 355226bb66SLouis-Alexis Eyraud return ret; 365226bb66SLouis-Alexis Eyraud 375226bb66SLouis-Alexis Eyraud pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); 385226bb66SLouis-Alexis Eyraud if (pbus_data_high < 0) 395226bb66SLouis-Alexis Eyraud return pbus_data_high; 405226bb66SLouis-Alexis Eyraud 415226bb66SLouis-Alexis Eyraud pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); 425226bb66SLouis-Alexis Eyraud if (pbus_data_low < 0) 435226bb66SLouis-Alexis Eyraud return pbus_data_low; 445226bb66SLouis-Alexis Eyraud 455226bb66SLouis-Alexis Eyraud *pbus_data = pbus_data_low | (pbus_data_high << 16); 465226bb66SLouis-Alexis Eyraud return 0; 475226bb66SLouis-Alexis Eyraud } 485226bb66SLouis-Alexis Eyraud 495226bb66SLouis-Alexis Eyraud static int __air_buckpbus_reg_write(struct phy_device *phydev, 505226bb66SLouis-Alexis Eyraud u32 pbus_address, u32 pbus_data) 515226bb66SLouis-Alexis Eyraud { 525226bb66SLouis-Alexis Eyraud int ret; 535226bb66SLouis-Alexis Eyraud 545226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); 555226bb66SLouis-Alexis Eyraud if (ret < 0) 565226bb66SLouis-Alexis Eyraud return ret; 575226bb66SLouis-Alexis Eyraud 585226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, 595226bb66SLouis-Alexis Eyraud upper_16_bits(pbus_address)); 605226bb66SLouis-Alexis Eyraud if (ret < 0) 615226bb66SLouis-Alexis Eyraud return ret; 625226bb66SLouis-Alexis Eyraud 635226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, 645226bb66SLouis-Alexis Eyraud lower_16_bits(pbus_address)); 655226bb66SLouis-Alexis Eyraud if (ret < 0) 665226bb66SLouis-Alexis Eyraud return ret; 675226bb66SLouis-Alexis Eyraud 685226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, 695226bb66SLouis-Alexis Eyraud upper_16_bits(pbus_data)); 705226bb66SLouis-Alexis Eyraud if (ret < 0) 715226bb66SLouis-Alexis Eyraud return ret; 725226bb66SLouis-Alexis Eyraud 735226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, 745226bb66SLouis-Alexis Eyraud lower_16_bits(pbus_data)); 755226bb66SLouis-Alexis Eyraud if (ret < 0) 765226bb66SLouis-Alexis Eyraud return ret; 775226bb66SLouis-Alexis Eyraud 785226bb66SLouis-Alexis Eyraud return 0; 795226bb66SLouis-Alexis Eyraud } 805226bb66SLouis-Alexis Eyraud 815226bb66SLouis-Alexis Eyraud static int __air_buckpbus_reg_modify(struct phy_device *phydev, 825226bb66SLouis-Alexis Eyraud u32 pbus_address, u32 mask, u32 set) 835226bb66SLouis-Alexis Eyraud { 845226bb66SLouis-Alexis Eyraud int pbus_data_low, pbus_data_high; 855226bb66SLouis-Alexis Eyraud u32 pbus_data_old, pbus_data_new; 865226bb66SLouis-Alexis Eyraud int ret; 875226bb66SLouis-Alexis Eyraud 885226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED); 895226bb66SLouis-Alexis Eyraud if (ret < 0) 905226bb66SLouis-Alexis Eyraud return ret; 915226bb66SLouis-Alexis Eyraud 925226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH, 935226bb66SLouis-Alexis Eyraud upper_16_bits(pbus_address)); 945226bb66SLouis-Alexis Eyraud if (ret < 0) 955226bb66SLouis-Alexis Eyraud return ret; 965226bb66SLouis-Alexis Eyraud 975226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW, 985226bb66SLouis-Alexis Eyraud lower_16_bits(pbus_address)); 995226bb66SLouis-Alexis Eyraud if (ret < 0) 1005226bb66SLouis-Alexis Eyraud return ret; 1015226bb66SLouis-Alexis Eyraud 1025226bb66SLouis-Alexis Eyraud pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH); 1035226bb66SLouis-Alexis Eyraud if (pbus_data_high < 0) 1045226bb66SLouis-Alexis Eyraud return pbus_data_high; 1055226bb66SLouis-Alexis Eyraud 1065226bb66SLouis-Alexis Eyraud pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW); 1075226bb66SLouis-Alexis Eyraud if (pbus_data_low < 0) 1085226bb66SLouis-Alexis Eyraud return pbus_data_low; 1095226bb66SLouis-Alexis Eyraud 1105226bb66SLouis-Alexis Eyraud pbus_data_old = pbus_data_low | (pbus_data_high << 16); 1115226bb66SLouis-Alexis Eyraud pbus_data_new = (pbus_data_old & ~mask) | set; 1125226bb66SLouis-Alexis Eyraud if (pbus_data_new == pbus_data_old) 1135226bb66SLouis-Alexis Eyraud return 0; 1145226bb66SLouis-Alexis Eyraud 1155226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH, 1165226bb66SLouis-Alexis Eyraud upper_16_bits(pbus_address)); 1175226bb66SLouis-Alexis Eyraud if (ret < 0) 1185226bb66SLouis-Alexis Eyraud return ret; 1195226bb66SLouis-Alexis Eyraud 1205226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW, 1215226bb66SLouis-Alexis Eyraud lower_16_bits(pbus_address)); 1225226bb66SLouis-Alexis Eyraud if (ret < 0) 1235226bb66SLouis-Alexis Eyraud return ret; 1245226bb66SLouis-Alexis Eyraud 1255226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH, 1265226bb66SLouis-Alexis Eyraud upper_16_bits(pbus_data_new)); 1275226bb66SLouis-Alexis Eyraud if (ret < 0) 1285226bb66SLouis-Alexis Eyraud return ret; 1295226bb66SLouis-Alexis Eyraud 1305226bb66SLouis-Alexis Eyraud ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW, 1315226bb66SLouis-Alexis Eyraud lower_16_bits(pbus_data_new)); 1325226bb66SLouis-Alexis Eyraud if (ret < 0) 1335226bb66SLouis-Alexis Eyraud return ret; 1345226bb66SLouis-Alexis Eyraud 1355226bb66SLouis-Alexis Eyraud return 0; 1365226bb66SLouis-Alexis Eyraud } 1375226bb66SLouis-Alexis Eyraud 138*e08f0ea6SLouis-Alexis Eyraud int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address, 1395226bb66SLouis-Alexis Eyraud u32 *pbus_data) 1405226bb66SLouis-Alexis Eyraud { 1415226bb66SLouis-Alexis Eyraud int saved_page; 1425226bb66SLouis-Alexis Eyraud int ret = 0; 1435226bb66SLouis-Alexis Eyraud 1445226bb66SLouis-Alexis Eyraud saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); 1455226bb66SLouis-Alexis Eyraud 1465226bb66SLouis-Alexis Eyraud if (saved_page >= 0) { 1475226bb66SLouis-Alexis Eyraud ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data); 1485226bb66SLouis-Alexis Eyraud if (ret < 0) 1495226bb66SLouis-Alexis Eyraud phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, 1505226bb66SLouis-Alexis Eyraud pbus_address, ret); 1515226bb66SLouis-Alexis Eyraud } 1525226bb66SLouis-Alexis Eyraud 1535226bb66SLouis-Alexis Eyraud return phy_restore_page(phydev, saved_page, ret); 1545226bb66SLouis-Alexis Eyraud } 155*e08f0ea6SLouis-Alexis Eyraud EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_read); 1565226bb66SLouis-Alexis Eyraud 157*e08f0ea6SLouis-Alexis Eyraud int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address, 1585226bb66SLouis-Alexis Eyraud u32 pbus_data) 1595226bb66SLouis-Alexis Eyraud { 1605226bb66SLouis-Alexis Eyraud int saved_page; 1615226bb66SLouis-Alexis Eyraud int ret = 0; 1625226bb66SLouis-Alexis Eyraud 1635226bb66SLouis-Alexis Eyraud saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); 1645226bb66SLouis-Alexis Eyraud 1655226bb66SLouis-Alexis Eyraud if (saved_page >= 0) { 1665226bb66SLouis-Alexis Eyraud ret = __air_buckpbus_reg_write(phydev, pbus_address, 1675226bb66SLouis-Alexis Eyraud pbus_data); 1685226bb66SLouis-Alexis Eyraud if (ret < 0) 1695226bb66SLouis-Alexis Eyraud phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, 1705226bb66SLouis-Alexis Eyraud pbus_address, ret); 1715226bb66SLouis-Alexis Eyraud } 1725226bb66SLouis-Alexis Eyraud 1735226bb66SLouis-Alexis Eyraud return phy_restore_page(phydev, saved_page, ret); 1745226bb66SLouis-Alexis Eyraud } 175*e08f0ea6SLouis-Alexis Eyraud EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_write); 1765226bb66SLouis-Alexis Eyraud 177*e08f0ea6SLouis-Alexis Eyraud int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address, 1785226bb66SLouis-Alexis Eyraud u32 mask, u32 set) 1795226bb66SLouis-Alexis Eyraud { 1805226bb66SLouis-Alexis Eyraud int saved_page; 1815226bb66SLouis-Alexis Eyraud int ret = 0; 1825226bb66SLouis-Alexis Eyraud 1835226bb66SLouis-Alexis Eyraud saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4); 1845226bb66SLouis-Alexis Eyraud 1855226bb66SLouis-Alexis Eyraud if (saved_page >= 0) { 1865226bb66SLouis-Alexis Eyraud ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask, 1875226bb66SLouis-Alexis Eyraud set); 1885226bb66SLouis-Alexis Eyraud if (ret < 0) 1895226bb66SLouis-Alexis Eyraud phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__, 1905226bb66SLouis-Alexis Eyraud pbus_address, ret); 1915226bb66SLouis-Alexis Eyraud } 1925226bb66SLouis-Alexis Eyraud 1935226bb66SLouis-Alexis Eyraud return phy_restore_page(phydev, saved_page, ret); 1945226bb66SLouis-Alexis Eyraud } 195*e08f0ea6SLouis-Alexis Eyraud EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_modify); 1965226bb66SLouis-Alexis Eyraud 197dddfadd7SLouis-Alexis Eyraud int air_phy_read_page(struct phy_device *phydev) 198dddfadd7SLouis-Alexis Eyraud { 199dddfadd7SLouis-Alexis Eyraud return __phy_read(phydev, AIR_EXT_PAGE_ACCESS); 200dddfadd7SLouis-Alexis Eyraud } 201dddfadd7SLouis-Alexis Eyraud EXPORT_SYMBOL_GPL(air_phy_read_page); 202dddfadd7SLouis-Alexis Eyraud 203dddfadd7SLouis-Alexis Eyraud int air_phy_write_page(struct phy_device *phydev, int page) 204dddfadd7SLouis-Alexis Eyraud { 205dddfadd7SLouis-Alexis Eyraud return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page); 206dddfadd7SLouis-Alexis Eyraud } 207dddfadd7SLouis-Alexis Eyraud EXPORT_SYMBOL_GPL(air_phy_write_page); 208dddfadd7SLouis-Alexis Eyraud 209dddfadd7SLouis-Alexis Eyraud MODULE_DESCRIPTION("Airoha PHY Library"); 210dddfadd7SLouis-Alexis Eyraud MODULE_LICENSE("GPL"); 211dddfadd7SLouis-Alexis Eyraud MODULE_AUTHOR("Louis-Alexis Eyraud"); 212