File Index
Files and directories
Sign in
FILE

mii-bitbang.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
  4 *
  5 * Copyright (c) 2003 Intracom S.A.
  6 *  by Pantelis Antoniou <panto@intracom.gr>
  7 *
  8 * 2005 (c) MontaVista Software, Inc.
  9 * Vitaly Bordug <vbordug@ru.mvista.com>
 10 */
 11
 12#include <linux/module.h>
 13#include <linux/ioport.h>
 14#include <linux/slab.h>
 15#include <linux/interrupt.h>
 16#include <linux/netdevice.h>
 17#include <linux/etherdevice.h>
 18#include <linux/mii.h>
 19#include <linux/platform_device.h>
 20#include <linux/mdio-bitbang.h>
 21#include <linux/of_address.h>
 22#include <linux/of_mdio.h>
 23#include <linux/of_platform.h>
 24
 25#include "fs_enet.h"
 26
 27struct bb_info {
 28	struct mdiobb_ctrl ctrl;
 29	u32 __iomem *dir;
 30	u32 __iomem *dat;
 31	u32 mdio_msk;
 32	u32 mdc_msk;
 33};
 34
 35/* FIXME: If any other users of GPIO crop up, then these will have to
 36 * have some sort of global synchronization to avoid races with other
 37 * pins on the same port.  The ideal solution would probably be to
 38 * bind the ports to a GPIO driver, and have this be a client of it.
 39 */
 40static inline void bb_set(u32 __iomem *p, u32 m)
 41{
 42	out_be32(p, in_be32(p) | m);
 43}
 44
 45static inline void bb_clr(u32 __iomem *p, u32 m)
 46{
 47	out_be32(p, in_be32(p) & ~m);
 48}
 49
 50static inline int bb_read(u32 __iomem *p, u32 m)
 51{
 52	return (in_be32(p) & m) != 0;
 53}
 54
 55static inline void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
 56{
 57	struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
 58
 59	if (dir)
 60		bb_set(bitbang->dir, bitbang->mdio_msk);
 61	else
 62		bb_clr(bitbang->dir, bitbang->mdio_msk);
 63
 64	/* Read back to flush the write. */
 65	in_be32(bitbang->dir);
 66}
 67
 68static inline int mdio_read(struct mdiobb_ctrl *ctrl)
 69{
 70	struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
 71	return bb_read(bitbang->dat, bitbang->mdio_msk);
 72}
 73
 74static inline void mdio(struct mdiobb_ctrl *ctrl, int what)
 75{
 76	struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
 77
 78	if (what)
 79		bb_set(bitbang->dat, bitbang->mdio_msk);
 80	else
 81		bb_clr(bitbang->dat, bitbang->mdio_msk);
 82
 83	/* Read back to flush the write. */
 84	in_be32(bitbang->dat);
 85}
 86
 87static inline void mdc(struct mdiobb_ctrl *ctrl, int what)
 88{
 89	struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
 90
 91	if (what)
 92		bb_set(bitbang->dat, bitbang->mdc_msk);
 93	else
 94		bb_clr(bitbang->dat, bitbang->mdc_msk);
 95
 96	/* Read back to flush the write. */
 97	in_be32(bitbang->dat);
 98}
 99
100static const struct mdiobb_ops bb_ops = {
101	.owner = THIS_MODULE,
102	.set_mdc = mdc,
103	.set_mdio_dir = mdio_dir,
104	.set_mdio_data = mdio,
105	.get_mdio_data = mdio_read,
106};
107
108static int fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np)
109{
110	struct resource res;
111	const u32 *data;
112	int mdio_pin, mdc_pin, len;
113	struct bb_info *bitbang = bus->priv;
114
115	int ret = of_address_to_resource(np, 0, &res);
116	if (ret)
117		return ret;
118
119	if (resource_size(&res) <= 13)
120		return -ENODEV;
121
122	/* This should really encode the pin number as well, but all
123	 * we get is an int, and the odds of multiple bitbang mdio buses
124	 * is low enough that it's not worth going too crazy.
125	 */
126	snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res.start);
127
128	data = of_get_property(np, "fsl,mdio-pin", &len);
129	if (!data || len != 4)
130		return -ENODEV;
131	mdio_pin = *data;
132
133	data = of_get_property(np, "fsl,mdc-pin", &len);
134	if (!data || len != 4)
135		return -ENODEV;
136	mdc_pin = *data;
137
138	bitbang->dir = ioremap(res.start, resource_size(&res));
139	if (!bitbang->dir)
140		return -ENOMEM;
141
142	bitbang->dat = bitbang->dir + 4;
143	bitbang->mdio_msk = 1 << (31 - mdio_pin);
144	bitbang->mdc_msk = 1 << (31 - mdc_pin);
145
146	return 0;
147}
148
149static int fs_enet_mdio_probe(struct platform_device *ofdev)
150{
151	struct mii_bus *new_bus;
152	struct bb_info *bitbang;
153	int ret = -ENOMEM;
154
155	bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);
156	if (!bitbang)
157		goto out;
158
159	bitbang->ctrl.ops = &bb_ops;
160
161	new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
162	if (!new_bus)
163		goto out_free_priv;
164
165	new_bus->name = "CPM2 Bitbanged MII",
166
167	ret = fs_mii_bitbang_init(new_bus, ofdev->dev.of_node);
168	if (ret)
169		goto out_free_bus;
170
171	new_bus->phy_mask = ~0;
172
173	new_bus->parent = &ofdev->dev;
174	platform_set_drvdata(ofdev, new_bus);
175
176	ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
177	if (ret)
178		goto out_unmap_regs;
179
180	return 0;
181
182out_unmap_regs:
183	iounmap(bitbang->dir);
184out_free_bus:
185	free_mdio_bitbang(new_bus);
186out_free_priv:
187	kfree(bitbang);
188out:
189	return ret;
190}
191
192static void fs_enet_mdio_remove(struct platform_device *ofdev)
193{
194	struct mii_bus *bus = platform_get_drvdata(ofdev);
195	struct bb_info *bitbang = bus->priv;
196
197	mdiobus_unregister(bus);
198	free_mdio_bitbang(bus);
199	iounmap(bitbang->dir);
200	kfree(bitbang);
201}
202
203static const struct of_device_id fs_enet_mdio_bb_match[] = {
204	{
205		.compatible = "fsl,cpm2-mdio-bitbang",
206	},
207	{},
208};
209MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match);
210
211static struct platform_driver fs_enet_bb_mdio_driver = {
212	.driver = {
213		.name = "fsl-bb-mdio",
214		.of_match_table = fs_enet_mdio_bb_match,
215	},
216	.probe = fs_enet_mdio_probe,
217	.remove = fs_enet_mdio_remove,
218};
219
220module_platform_driver(fs_enet_bb_mdio_driver);
221MODULE_LICENSE("GPL");
~/ Read-only file preview.