1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2020 NovaTech LLC |
4 | * George McCollister <george.mccollister@gmail.com> |
5 | */ |
6 | |
7 | #include <linux/bitfield.h> |
8 | #include <linux/bits.h> |
9 | #include <linux/mdio.h> |
10 | #include <linux/module.h> |
11 | #include <linux/phy.h> |
12 | #include <linux/if_vlan.h> |
13 | #include <linux/of.h> |
14 | #include "xrs700x.h" |
15 | #include "xrs700x_reg.h" |
16 | |
17 | #define XRS_MDIO_IBA0 0x10 |
18 | #define XRS_MDIO_IBA1 0x11 |
19 | #define XRS_MDIO_IBD 0x14 |
20 | |
21 | #define XRS_IB_READ 0x0 |
22 | #define XRS_IB_WRITE 0x1 |
23 | |
24 | static int xrs700x_mdio_reg_read(void *context, unsigned int reg, |
25 | unsigned int *val) |
26 | { |
27 | struct mdio_device *mdiodev = context; |
28 | struct device *dev = &mdiodev->dev; |
29 | u16 uval; |
30 | int ret; |
31 | |
32 | uval = (u16)FIELD_GET(GENMASK(31, 16), reg); |
33 | |
34 | ret = mdiodev_write(mdiodev, XRS_MDIO_IBA1, val: uval); |
35 | if (ret < 0) { |
36 | dev_err(dev, "xrs mdiobus_write returned %d\n" , ret); |
37 | return ret; |
38 | } |
39 | |
40 | uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_READ); |
41 | |
42 | ret = mdiodev_write(mdiodev, XRS_MDIO_IBA0, val: uval); |
43 | if (ret < 0) { |
44 | dev_err(dev, "xrs mdiobus_write returned %d\n" , ret); |
45 | return ret; |
46 | } |
47 | |
48 | ret = mdiodev_read(mdiodev, XRS_MDIO_IBD); |
49 | if (ret < 0) { |
50 | dev_err(dev, "xrs mdiobus_read returned %d\n" , ret); |
51 | return ret; |
52 | } |
53 | |
54 | *val = (unsigned int)ret; |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static int xrs700x_mdio_reg_write(void *context, unsigned int reg, |
60 | unsigned int val) |
61 | { |
62 | struct mdio_device *mdiodev = context; |
63 | struct device *dev = &mdiodev->dev; |
64 | u16 uval; |
65 | int ret; |
66 | |
67 | ret = mdiodev_write(mdiodev, XRS_MDIO_IBD, val: (u16)val); |
68 | if (ret < 0) { |
69 | dev_err(dev, "xrs mdiobus_write returned %d\n" , ret); |
70 | return ret; |
71 | } |
72 | |
73 | uval = (u16)FIELD_GET(GENMASK(31, 16), reg); |
74 | |
75 | ret = mdiodev_write(mdiodev, XRS_MDIO_IBA1, val: uval); |
76 | if (ret < 0) { |
77 | dev_err(dev, "xrs mdiobus_write returned %d\n" , ret); |
78 | return ret; |
79 | } |
80 | |
81 | uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_WRITE); |
82 | |
83 | ret = mdiodev_write(mdiodev, XRS_MDIO_IBA0, val: uval); |
84 | if (ret < 0) { |
85 | dev_err(dev, "xrs mdiobus_write returned %d\n" , ret); |
86 | return ret; |
87 | } |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | static const struct regmap_config xrs700x_mdio_regmap_config = { |
93 | .val_bits = 16, |
94 | .reg_stride = 2, |
95 | .reg_bits = 32, |
96 | .pad_bits = 0, |
97 | .write_flag_mask = 0, |
98 | .read_flag_mask = 0, |
99 | .reg_read = xrs700x_mdio_reg_read, |
100 | .reg_write = xrs700x_mdio_reg_write, |
101 | .max_register = XRS_VLAN(VLAN_N_VID - 1), |
102 | .cache_type = REGCACHE_NONE, |
103 | .reg_format_endian = REGMAP_ENDIAN_BIG, |
104 | .val_format_endian = REGMAP_ENDIAN_BIG |
105 | }; |
106 | |
107 | static int xrs700x_mdio_probe(struct mdio_device *mdiodev) |
108 | { |
109 | struct xrs700x *priv; |
110 | int ret; |
111 | |
112 | priv = xrs700x_switch_alloc(base: &mdiodev->dev, devpriv: mdiodev); |
113 | if (!priv) |
114 | return -ENOMEM; |
115 | |
116 | priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev, |
117 | &xrs700x_mdio_regmap_config); |
118 | if (IS_ERR(ptr: priv->regmap)) { |
119 | ret = PTR_ERR(ptr: priv->regmap); |
120 | dev_err(&mdiodev->dev, "Failed to initialize regmap: %d\n" , ret); |
121 | return ret; |
122 | } |
123 | |
124 | dev_set_drvdata(dev: &mdiodev->dev, data: priv); |
125 | |
126 | ret = xrs700x_switch_register(priv); |
127 | |
128 | /* Main DSA driver may not be started yet. */ |
129 | if (ret) |
130 | return ret; |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static void xrs700x_mdio_remove(struct mdio_device *mdiodev) |
136 | { |
137 | struct xrs700x *priv = dev_get_drvdata(dev: &mdiodev->dev); |
138 | |
139 | if (!priv) |
140 | return; |
141 | |
142 | xrs700x_switch_remove(priv); |
143 | } |
144 | |
145 | static void xrs700x_mdio_shutdown(struct mdio_device *mdiodev) |
146 | { |
147 | struct xrs700x *priv = dev_get_drvdata(dev: &mdiodev->dev); |
148 | |
149 | if (!priv) |
150 | return; |
151 | |
152 | xrs700x_switch_shutdown(priv); |
153 | |
154 | dev_set_drvdata(dev: &mdiodev->dev, NULL); |
155 | } |
156 | |
157 | static const struct of_device_id __maybe_unused xrs700x_mdio_dt_ids[] = { |
158 | { .compatible = "arrow,xrs7003e" , .data = &xrs7003e_info }, |
159 | { .compatible = "arrow,xrs7003f" , .data = &xrs7003f_info }, |
160 | { .compatible = "arrow,xrs7004e" , .data = &xrs7004e_info }, |
161 | { .compatible = "arrow,xrs7004f" , .data = &xrs7004f_info }, |
162 | {}, |
163 | }; |
164 | MODULE_DEVICE_TABLE(of, xrs700x_mdio_dt_ids); |
165 | |
166 | static struct mdio_driver xrs700x_mdio_driver = { |
167 | .mdiodrv.driver = { |
168 | .name = "xrs700x-mdio" , |
169 | .of_match_table = of_match_ptr(xrs700x_mdio_dt_ids), |
170 | }, |
171 | .probe = xrs700x_mdio_probe, |
172 | .remove = xrs700x_mdio_remove, |
173 | .shutdown = xrs700x_mdio_shutdown, |
174 | }; |
175 | |
176 | mdio_module_driver(xrs700x_mdio_driver); |
177 | |
178 | MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>" ); |
179 | MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA MDIO driver" ); |
180 | MODULE_LICENSE("GPL v2" ); |
181 | |