mediatek: add Airoha AN8855 gigabit switch driver
[openwrt/openwrt.git] / target / linux / mediatek / files-6.6 / drivers / mfd / airoha-an8855.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * MFD driver for Airoha AN8855 Switch
4 */
5
6 #include <linux/mfd/airoha-an8855-mfd.h>
7 #include <linux/mfd/core.h>
8 #include <linux/mdio.h>
9 #include <linux/module.h>
10 #include <linux/phy.h>
11 #include <linux/regmap.h>
12
13 static const struct mfd_cell an8855_mfd_devs[] = {
14 {
15 .name = "an8855-efuse",
16 .of_compatible = "airoha,an8855-efuse",
17 }, {
18 .name = "an8855-switch",
19 .of_compatible = "airoha,an8855-switch",
20 }, {
21 .name = "an8855-mdio",
22 .of_compatible = "airoha,an8855-mdio",
23 }
24 };
25
26 int an8855_mii_set_page(struct an8855_mfd_priv *priv, u8 phy_id,
27 u8 page) __must_hold(&priv->bus->mdio_lock)
28 {
29 struct mii_bus *bus = priv->bus;
30 int ret;
31
32 ret = __mdiobus_write(bus, phy_id, AN8855_PHY_SELECT_PAGE, page);
33 if (ret < 0)
34 dev_err_ratelimited(&bus->dev,
35 "failed to set an8855 mii page\n");
36
37 /* Cache current page if next mii read/write is for switch */
38 priv->current_page = page;
39 return ret < 0 ? ret : 0;
40 }
41 EXPORT_SYMBOL_GPL(an8855_mii_set_page);
42
43 static int an8855_mii_read32(struct mii_bus *bus, u8 phy_id, u32 reg,
44 u32 *val) __must_hold(&bus->mdio_lock)
45 {
46 int lo, hi, ret;
47
48 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_MODE,
49 AN8855_PBUS_MODE_ADDR_FIXED);
50 if (ret < 0)
51 goto err;
52
53 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_RD_ADDR_HIGH,
54 upper_16_bits(reg));
55 if (ret < 0)
56 goto err;
57 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_RD_ADDR_LOW,
58 lower_16_bits(reg));
59 if (ret < 0)
60 goto err;
61
62 hi = __mdiobus_read(bus, phy_id, AN8855_PBUS_RD_DATA_HIGH);
63 if (hi < 0) {
64 ret = hi;
65 goto err;
66 }
67 lo = __mdiobus_read(bus, phy_id, AN8855_PBUS_RD_DATA_LOW);
68 if (lo < 0) {
69 ret = lo;
70 goto err;
71 }
72
73 *val = ((u16)hi << 16) | ((u16)lo & 0xffff);
74
75 return 0;
76 err:
77 dev_err_ratelimited(&bus->dev,
78 "failed to read an8855 register\n");
79 return ret;
80 }
81
82 static int an8855_regmap_read(void *ctx, uint32_t reg, uint32_t *val)
83 {
84 struct an8855_mfd_priv *priv = ctx;
85 struct mii_bus *bus = priv->bus;
86 u16 addr = priv->switch_addr;
87 int ret;
88
89 mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
90 ret = an8855_mii_set_page(priv, addr, AN8855_PHY_PAGE_EXTENDED_4);
91 if (ret < 0)
92 goto exit;
93
94 ret = an8855_mii_read32(bus, addr, reg, val);
95
96 exit:
97 mutex_unlock(&bus->mdio_lock);
98
99 return ret < 0 ? ret : 0;
100 }
101
102 static int an8855_mii_write32(struct mii_bus *bus, u8 phy_id, u32 reg,
103 u32 val) __must_hold(&bus->mdio_lock)
104 {
105 int ret;
106
107 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_MODE,
108 AN8855_PBUS_MODE_ADDR_FIXED);
109 if (ret < 0)
110 goto err;
111
112 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_ADDR_HIGH,
113 upper_16_bits(reg));
114 if (ret < 0)
115 goto err;
116 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_ADDR_LOW,
117 lower_16_bits(reg));
118 if (ret < 0)
119 goto err;
120
121 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_DATA_HIGH,
122 upper_16_bits(val));
123 if (ret < 0)
124 goto err;
125 ret = __mdiobus_write(bus, phy_id, AN8855_PBUS_WR_DATA_LOW,
126 lower_16_bits(val));
127 if (ret < 0)
128 goto err;
129
130 return 0;
131 err:
132 dev_err_ratelimited(&bus->dev,
133 "failed to write an8855 register\n");
134 return ret;
135 }
136
137 static int
138 an8855_regmap_write(void *ctx, uint32_t reg, uint32_t val)
139 {
140 struct an8855_mfd_priv *priv = ctx;
141 struct mii_bus *bus = priv->bus;
142 u16 addr = priv->switch_addr;
143 int ret;
144
145 mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
146 ret = an8855_mii_set_page(priv, addr, AN8855_PHY_PAGE_EXTENDED_4);
147 if (ret < 0)
148 goto exit;
149
150 ret = an8855_mii_write32(bus, addr, reg, val);
151
152 exit:
153 mutex_unlock(&bus->mdio_lock);
154
155 return ret < 0 ? ret : 0;
156 }
157
158 static int an8855_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask,
159 uint32_t write_val)
160 {
161 struct an8855_mfd_priv *priv = ctx;
162 struct mii_bus *bus = priv->bus;
163 u16 addr = priv->switch_addr;
164 u32 val;
165 int ret;
166
167 mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
168 ret = an8855_mii_set_page(priv, addr, AN8855_PHY_PAGE_EXTENDED_4);
169 if (ret < 0)
170 goto exit;
171
172 ret = an8855_mii_read32(bus, addr, reg, &val);
173 if (ret < 0)
174 goto exit;
175
176 val &= ~mask;
177 val |= write_val;
178 ret = an8855_mii_write32(bus, addr, reg, val);
179
180 exit:
181 mutex_unlock(&bus->mdio_lock);
182
183 return ret < 0 ? ret : 0;
184 }
185
186 static const struct regmap_range an8855_readable_ranges[] = {
187 regmap_reg_range(0x10000000, 0x10000fff), /* SCU */
188 regmap_reg_range(0x10001000, 0x10001fff), /* RBUS */
189 regmap_reg_range(0x10002000, 0x10002fff), /* MCU */
190 regmap_reg_range(0x10005000, 0x10005fff), /* SYS SCU */
191 regmap_reg_range(0x10007000, 0x10007fff), /* I2C Slave */
192 regmap_reg_range(0x10008000, 0x10008fff), /* I2C Master */
193 regmap_reg_range(0x10009000, 0x10009fff), /* PDMA */
194 regmap_reg_range(0x1000a100, 0x1000a2ff), /* General Purpose Timer */
195 regmap_reg_range(0x1000a200, 0x1000a2ff), /* GPU timer */
196 regmap_reg_range(0x1000a300, 0x1000a3ff), /* GPIO */
197 regmap_reg_range(0x1000a400, 0x1000a5ff), /* EFUSE */
198 regmap_reg_range(0x1000c000, 0x1000cfff), /* GDMP CSR */
199 regmap_reg_range(0x10010000, 0x1001ffff), /* GDMP SRAM */
200 regmap_reg_range(0x10200000, 0x10203fff), /* Switch - ARL Global */
201 regmap_reg_range(0x10204000, 0x10207fff), /* Switch - BMU */
202 regmap_reg_range(0x10208000, 0x1020bfff), /* Switch - ARL Port */
203 regmap_reg_range(0x1020c000, 0x1020cfff), /* Switch - SCH */
204 regmap_reg_range(0x10210000, 0x10213fff), /* Switch - MAC */
205 regmap_reg_range(0x10214000, 0x10217fff), /* Switch - MIB */
206 regmap_reg_range(0x10218000, 0x1021bfff), /* Switch - Port Control */
207 regmap_reg_range(0x1021c000, 0x1021ffff), /* Switch - TOP */
208 regmap_reg_range(0x10220000, 0x1022ffff), /* SerDes */
209 regmap_reg_range(0x10286000, 0x10286fff), /* RG Batcher */
210 regmap_reg_range(0x1028c000, 0x1028ffff), /* ETHER_SYS */
211 regmap_reg_range(0x30000000, 0x37ffffff), /* I2C EEPROM */
212 regmap_reg_range(0x38000000, 0x3fffffff), /* BOOT_ROM */
213 regmap_reg_range(0xa0000000, 0xbfffffff), /* GPHY */
214 };
215
216 static const struct regmap_access_table an8855_readable_table = {
217 .yes_ranges = an8855_readable_ranges,
218 .n_yes_ranges = ARRAY_SIZE(an8855_readable_ranges),
219 };
220
221 static const struct regmap_config an8855_regmap_config = {
222 .reg_bits = 32,
223 .val_bits = 32,
224 .reg_stride = 4,
225 .max_register = 0xbfffffff,
226 .reg_read = an8855_regmap_read,
227 .reg_write = an8855_regmap_write,
228 .reg_update_bits = an8855_regmap_update_bits,
229 .disable_locking = true,
230 .rd_table = &an8855_readable_table,
231 };
232
233 static int an8855_mfd_probe(struct mdio_device *mdiodev)
234 {
235 struct an8855_mfd_priv *priv;
236 struct regmap *regmap;
237
238 priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
239 if (!priv)
240 return -ENOMEM;
241
242 priv->bus = mdiodev->bus;
243 priv->dev = &mdiodev->dev;
244 priv->switch_addr = mdiodev->addr;
245 /* no DMA for mdiobus, mute warning for DMA mask not set */
246 priv->dev->dma_mask = &priv->dev->coherent_dma_mask;
247
248 regmap = devm_regmap_init(priv->dev, NULL, priv,
249 &an8855_regmap_config);
250 if (IS_ERR(regmap))
251 dev_err_probe(priv->dev, PTR_ERR(priv->dev),
252 "regmap initialization failed\n");
253
254 dev_set_drvdata(&mdiodev->dev, priv);
255
256 return devm_mfd_add_devices(priv->dev, PLATFORM_DEVID_AUTO, an8855_mfd_devs,
257 ARRAY_SIZE(an8855_mfd_devs), NULL, 0,
258 NULL);
259 }
260
261 static const struct of_device_id an8855_mfd_of_match[] = {
262 { .compatible = "airoha,an8855-mfd" },
263 { /* sentinel */ }
264 };
265 MODULE_DEVICE_TABLE(of, an8855_mfd_of_match);
266
267 static struct mdio_driver an8855_mfd_driver = {
268 .probe = an8855_mfd_probe,
269 .mdiodrv.driver = {
270 .name = "an8855",
271 .of_match_table = an8855_mfd_of_match,
272 },
273 };
274 mdio_module_driver(an8855_mfd_driver);
275
276 MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
277 MODULE_DESCRIPTION("Driver for Airoha AN8855 MFD");
278 MODULE_LICENSE("GPL");