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
24static 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, 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, 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
59static 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, (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, 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, 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
92static 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
107static 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
135static 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
145static void xrs700x_mdio_shutdown(struct mdio_device *mdiodev)
146{
147	struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev);
148
149	if (!priv)
150		return;
151
152	xrs700x_switch_shutdown(priv);
153
154	dev_set_drvdata(&mdiodev->dev, NULL);
155}
156
157static 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};
164MODULE_DEVICE_TABLE(of, xrs700x_mdio_dt_ids);
165
166static 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
176mdio_module_driver(xrs700x_mdio_driver);
177
178MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>");
179MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA MDIO driver");
180MODULE_LICENSE("GPL v2");
181