projects/linux·blob master · refs

211 lines · 5.0 KiB

1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Airoha Ethernet PHY common library
4 *
5 * Copyright (C) 2026 Airoha Technology Corp.
6 * Copyright (C) 2026 Collabora Ltd.
7 * Louis-Alexis Eyraud <[email protected]>
8 */
9
10#include <linux/export.h>
11#include <linux/module.h>
12#include <linux/phy.h>
13#include <linux/wordpart.h>
14
15#include "air_phy_lib.h"
16
17static int __air_buckpbus_reg_read(struct phy_device *phydev,
18 u32 pbus_address, u32 *pbus_data)
19{
20 int pbus_data_low, pbus_data_high;
21 int ret;
22
23 ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
24 if (ret < 0)
25 return ret;
26
27 ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
28 upper_16_bits(pbus_address));
29 if (ret < 0)
30 return ret;
31
32 ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
33 lower_16_bits(pbus_address));
34 if (ret < 0)
35 return ret;
36
37 pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
38 if (pbus_data_high < 0)
39 return pbus_data_high;
40
41 pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
42 if (pbus_data_low < 0)
43 return pbus_data_low;
44
45 *pbus_data = pbus_data_low | (pbus_data_high << 16);
46 return 0;
47}
48
49static int __air_buckpbus_reg_write(struct phy_device *phydev,
50 u32 pbus_address, u32 pbus_data)
51{
52 int ret;
53
54 ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
55 if (ret < 0)
56 return ret;
57
58 ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
59 upper_16_bits(pbus_address));
60 if (ret < 0)
61 return ret;
62
63 ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
64 lower_16_bits(pbus_address));
65 if (ret < 0)
66 return ret;
67
68 ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
69 upper_16_bits(pbus_data));
70 if (ret < 0)
71 return ret;
72
73 ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
74 lower_16_bits(pbus_data));
75 if (ret < 0)
76 return ret;
77
78 return 0;
79}
80
81static int __air_buckpbus_reg_modify(struct phy_device *phydev,
82 u32 pbus_address, u32 mask, u32 set)
83{
84 int pbus_data_low, pbus_data_high;
85 u32 pbus_data_old, pbus_data_new;
86 int ret;
87
88 ret = __phy_write(phydev, AIR_BPBUS_MODE, AIR_BPBUS_MODE_ADDR_FIXED);
89 if (ret < 0)
90 return ret;
91
92 ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_HIGH,
93 upper_16_bits(pbus_address));
94 if (ret < 0)
95 return ret;
96
97 ret = __phy_write(phydev, AIR_BPBUS_RD_ADDR_LOW,
98 lower_16_bits(pbus_address));
99 if (ret < 0)
100 return ret;
101
102 pbus_data_high = __phy_read(phydev, AIR_BPBUS_RD_DATA_HIGH);
103 if (pbus_data_high < 0)
104 return pbus_data_high;
105
106 pbus_data_low = __phy_read(phydev, AIR_BPBUS_RD_DATA_LOW);
107 if (pbus_data_low < 0)
108 return pbus_data_low;
109
110 pbus_data_old = pbus_data_low | (pbus_data_high << 16);
111 pbus_data_new = (pbus_data_old & ~mask) | set;
112 if (pbus_data_new == pbus_data_old)
113 return 0;
114
115 ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_HIGH,
116 upper_16_bits(pbus_address));
117 if (ret < 0)
118 return ret;
119
120 ret = __phy_write(phydev, AIR_BPBUS_WR_ADDR_LOW,
121 lower_16_bits(pbus_address));
122 if (ret < 0)
123 return ret;
124
125 ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_HIGH,
126 upper_16_bits(pbus_data_new));
127 if (ret < 0)
128 return ret;
129
130 ret = __phy_write(phydev, AIR_BPBUS_WR_DATA_LOW,
131 lower_16_bits(pbus_data_new));
132 if (ret < 0)
133 return ret;
134
135 return 0;
136}
137
138int air_phy_buckpbus_reg_read(struct phy_device *phydev, u32 pbus_address,
139 u32 *pbus_data)
140{
141 int saved_page;
142 int ret = 0;
143
144 saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
145
146 if (saved_page >= 0) {
147 ret = __air_buckpbus_reg_read(phydev, pbus_address, pbus_data);
148 if (ret < 0)
149 phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
150 pbus_address, ret);
151 }
152
153 return phy_restore_page(phydev, saved_page, ret);
154}
155EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_read);
156
157int air_phy_buckpbus_reg_write(struct phy_device *phydev, u32 pbus_address,
158 u32 pbus_data)
159{
160 int saved_page;
161 int ret = 0;
162
163 saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
164
165 if (saved_page >= 0) {
166 ret = __air_buckpbus_reg_write(phydev, pbus_address,
167 pbus_data);
168 if (ret < 0)
169 phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
170 pbus_address, ret);
171 }
172
173 return phy_restore_page(phydev, saved_page, ret);
174}
175EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_write);
176
177int air_phy_buckpbus_reg_modify(struct phy_device *phydev, u32 pbus_address,
178 u32 mask, u32 set)
179{
180 int saved_page;
181 int ret = 0;
182
183 saved_page = phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
184
185 if (saved_page >= 0) {
186 ret = __air_buckpbus_reg_modify(phydev, pbus_address, mask,
187 set);
188 if (ret < 0)
189 phydev_err(phydev, "%s 0x%08x failed: %d\n", __func__,
190 pbus_address, ret);
191 }
192
193 return phy_restore_page(phydev, saved_page, ret);
194}
195EXPORT_SYMBOL_GPL(air_phy_buckpbus_reg_modify);
196
197int air_phy_read_page(struct phy_device *phydev)
198{
199 return __phy_read(phydev, AIR_EXT_PAGE_ACCESS);
200}
201EXPORT_SYMBOL_GPL(air_phy_read_page);
202
203int air_phy_write_page(struct phy_device *phydev, int page)
204{
205 return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
206}
207EXPORT_SYMBOL_GPL(air_phy_write_page);
208
209MODULE_DESCRIPTION("Airoha PHY Library");
210MODULE_LICENSE("GPL");
211MODULE_AUTHOR("Louis-Alexis Eyraud");