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 27 struct 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 */ 40 static inline void bb_set(u32 __iomem *p, u32 m) 41 { 42 out_be32(p, in_be32(p) | m); 43 } 44 45 static inline void bb_clr(u32 __iomem *p, u32 m) 46 { 47 out_be32(p, in_be32(p) & ~m); 48 } 49 50 static inline int bb_read(u32 __iomem *p, u32 m) 51 { 52 return (in_be32(p) & m) != 0; 53 } 54 55 static 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 68 static 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 74 static 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 87 static 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 100 static 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 108 static 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 149 static 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 182 out_unmap_regs: 183 iounmap(bitbang->dir); 184 out_free_bus: 185 free_mdio_bitbang(new_bus); 186 out_free_priv: 187 kfree(bitbang); 188 out: 189 return ret; 190 } 191 192 static 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 203 static const struct of_device_id fs_enet_mdio_bb_match[] = { 204 { 205 .compatible = "fsl,cpm2-mdio-bitbang", 206 }, 207 {}, 208 }; 209 MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match); 210 211 static 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 220 module_platform_driver(fs_enet_bb_mdio_driver); 221 MODULE_LICENSE("GPL"); 222