xref: /linux/drivers/net/phy/air_phy_lib.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
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