Merge tag 'drm-misc-next-2021-10-14' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / drivers / net / dsa / xrs700x / xrs700x_mdio.c
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 = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA1, 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 = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA0, uval);
43         if (ret < 0) {
44                 dev_err(dev, "xrs mdiobus_write returned %d\n", ret);
45                 return ret;
46         }
47
48         ret = mdiobus_read(mdiodev->bus, mdiodev->addr, 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 = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBD, (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 = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA1, 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 = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA0, 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(&mdiodev->dev, 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(priv->regmap)) {
119                 ret = PTR_ERR(priv->regmap);
120                 dev_err(&mdiodev->dev, "Failed to initialize regmap: %d\n", ret);
121                 return ret;
122         }
123
124         dev_set_drvdata(&mdiodev->dev, 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(&mdiodev->dev);
138
139         if (!priv)
140                 return;
141
142         xrs700x_switch_remove(priv);
143
144         dev_set_drvdata(&mdiodev->dev, NULL);
145 }
146
147 static void xrs700x_mdio_shutdown(struct mdio_device *mdiodev)
148 {
149         struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev);
150
151         if (!priv)
152                 return;
153
154         xrs700x_switch_shutdown(priv);
155
156         dev_set_drvdata(&mdiodev->dev, NULL);
157 }
158
159 static const struct of_device_id __maybe_unused xrs700x_mdio_dt_ids[] = {
160         { .compatible = "arrow,xrs7003e", .data = &xrs7003e_info },
161         { .compatible = "arrow,xrs7003f", .data = &xrs7003f_info },
162         { .compatible = "arrow,xrs7004e", .data = &xrs7004e_info },
163         { .compatible = "arrow,xrs7004f", .data = &xrs7004f_info },
164         {},
165 };
166 MODULE_DEVICE_TABLE(of, xrs700x_mdio_dt_ids);
167
168 static struct mdio_driver xrs700x_mdio_driver = {
169         .mdiodrv.driver = {
170                 .name   = "xrs700x-mdio",
171                 .of_match_table = of_match_ptr(xrs700x_mdio_dt_ids),
172         },
173         .probe  = xrs700x_mdio_probe,
174         .remove = xrs700x_mdio_remove,
175         .shutdown = xrs700x_mdio_shutdown,
176 };
177
178 mdio_module_driver(xrs700x_mdio_driver);
179
180 MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>");
181 MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA MDIO driver");
182 MODULE_LICENSE("GPL v2");