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