brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · 2e210a0 Raw
222 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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 to36 * have some sort of global synchronization to avoid races with other37 * pins on the same port.  The ideal solution would probably be to38 * 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	else62		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	else81		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	else94		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 all123	 * we get is an int, and the odds of multiple bitbang mdio buses124	 * is low enough that it's not worth going too crazy.125	 */126	snprintf(bus->id, MII_BUS_ID_SIZE, "%x", 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_new = fs_enet_mdio_remove,218};219 220module_platform_driver(fs_enet_bb_mdio_driver);221MODULE_LICENSE("GPL");222