18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Combined Ethernet driver for Motorola MPC8xx and MPC82xx. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2003 Intracom S.A. 58c2ecf20Sopenharmony_ci * by Pantelis Antoniou <panto@intracom.gr> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 2005 (c) MontaVista Software, Inc. 88c2ecf20Sopenharmony_ci * Vitaly Bordug <vbordug@ru.mvista.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License 118c2ecf20Sopenharmony_ci * version 2. This program is licensed "as is" without any warranty of any 128c2ecf20Sopenharmony_ci * kind, whether express or implied. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/ioport.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 208c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 218c2ecf20Sopenharmony_ci#include <linux/mii.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 238c2ecf20Sopenharmony_ci#include <linux/mdio-bitbang.h> 248c2ecf20Sopenharmony_ci#include <linux/of_address.h> 258c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 268c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "fs_enet.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct bb_info { 318c2ecf20Sopenharmony_ci struct mdiobb_ctrl ctrl; 328c2ecf20Sopenharmony_ci __be32 __iomem *dir; 338c2ecf20Sopenharmony_ci __be32 __iomem *dat; 348c2ecf20Sopenharmony_ci u32 mdio_msk; 358c2ecf20Sopenharmony_ci u32 mdc_msk; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* FIXME: If any other users of GPIO crop up, then these will have to 398c2ecf20Sopenharmony_ci * have some sort of global synchronization to avoid races with other 408c2ecf20Sopenharmony_ci * pins on the same port. The ideal solution would probably be to 418c2ecf20Sopenharmony_ci * bind the ports to a GPIO driver, and have this be a client of it. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistatic inline void bb_set(u32 __iomem *p, u32 m) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci out_be32(p, in_be32(p) | m); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic inline void bb_clr(u32 __iomem *p, u32 m) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci out_be32(p, in_be32(p) & ~m); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic inline int bb_read(u32 __iomem *p, u32 m) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci return (in_be32(p) & m) != 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic inline void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (dir) 638c2ecf20Sopenharmony_ci bb_set(bitbang->dir, bitbang->mdio_msk); 648c2ecf20Sopenharmony_ci else 658c2ecf20Sopenharmony_ci bb_clr(bitbang->dir, bitbang->mdio_msk); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* Read back to flush the write. */ 688c2ecf20Sopenharmony_ci in_be32(bitbang->dir); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline int mdio_read(struct mdiobb_ctrl *ctrl) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); 748c2ecf20Sopenharmony_ci return bb_read(bitbang->dat, bitbang->mdio_msk); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline void mdio(struct mdiobb_ctrl *ctrl, int what) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (what) 828c2ecf20Sopenharmony_ci bb_set(bitbang->dat, bitbang->mdio_msk); 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci bb_clr(bitbang->dat, bitbang->mdio_msk); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Read back to flush the write. */ 878c2ecf20Sopenharmony_ci in_be32(bitbang->dat); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic inline void mdc(struct mdiobb_ctrl *ctrl, int what) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (what) 958c2ecf20Sopenharmony_ci bb_set(bitbang->dat, bitbang->mdc_msk); 968c2ecf20Sopenharmony_ci else 978c2ecf20Sopenharmony_ci bb_clr(bitbang->dat, bitbang->mdc_msk); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* Read back to flush the write. */ 1008c2ecf20Sopenharmony_ci in_be32(bitbang->dat); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic const struct mdiobb_ops bb_ops = { 1048c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1058c2ecf20Sopenharmony_ci .set_mdc = mdc, 1068c2ecf20Sopenharmony_ci .set_mdio_dir = mdio_dir, 1078c2ecf20Sopenharmony_ci .set_mdio_data = mdio, 1088c2ecf20Sopenharmony_ci .get_mdio_data = mdio_read, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct resource res; 1148c2ecf20Sopenharmony_ci const u32 *data; 1158c2ecf20Sopenharmony_ci int mdio_pin, mdc_pin, len; 1168c2ecf20Sopenharmony_ci struct bb_info *bitbang = bus->priv; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci int ret = of_address_to_resource(np, 0, &res); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (resource_size(&res) <= 13) 1238c2ecf20Sopenharmony_ci return -ENODEV; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* This should really encode the pin number as well, but all 1268c2ecf20Sopenharmony_ci * we get is an int, and the odds of multiple bitbang mdio buses 1278c2ecf20Sopenharmony_ci * is low enough that it's not worth going too crazy. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci data = of_get_property(np, "fsl,mdio-pin", &len); 1328c2ecf20Sopenharmony_ci if (!data || len != 4) 1338c2ecf20Sopenharmony_ci return -ENODEV; 1348c2ecf20Sopenharmony_ci mdio_pin = *data; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci data = of_get_property(np, "fsl,mdc-pin", &len); 1378c2ecf20Sopenharmony_ci if (!data || len != 4) 1388c2ecf20Sopenharmony_ci return -ENODEV; 1398c2ecf20Sopenharmony_ci mdc_pin = *data; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci bitbang->dir = ioremap(res.start, resource_size(&res)); 1428c2ecf20Sopenharmony_ci if (!bitbang->dir) 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci bitbang->dat = bitbang->dir + 4; 1468c2ecf20Sopenharmony_ci bitbang->mdio_msk = 1 << (31 - mdio_pin); 1478c2ecf20Sopenharmony_ci bitbang->mdc_msk = 1 << (31 - mdc_pin); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int fs_enet_mdio_probe(struct platform_device *ofdev) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct mii_bus *new_bus; 1558c2ecf20Sopenharmony_ci struct bb_info *bitbang; 1568c2ecf20Sopenharmony_ci int ret = -ENOMEM; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); 1598c2ecf20Sopenharmony_ci if (!bitbang) 1608c2ecf20Sopenharmony_ci goto out; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci bitbang->ctrl.ops = &bb_ops; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci new_bus = alloc_mdio_bitbang(&bitbang->ctrl); 1658c2ecf20Sopenharmony_ci if (!new_bus) 1668c2ecf20Sopenharmony_ci goto out_free_priv; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci new_bus->name = "CPM2 Bitbanged MII", 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = fs_mii_bitbang_init(new_bus, ofdev->dev.of_node); 1718c2ecf20Sopenharmony_ci if (ret) 1728c2ecf20Sopenharmony_ci goto out_free_bus; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci new_bus->phy_mask = ~0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci new_bus->parent = &ofdev->dev; 1778c2ecf20Sopenharmony_ci platform_set_drvdata(ofdev, new_bus); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); 1808c2ecf20Sopenharmony_ci if (ret) 1818c2ecf20Sopenharmony_ci goto out_unmap_regs; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciout_unmap_regs: 1868c2ecf20Sopenharmony_ci iounmap(bitbang->dir); 1878c2ecf20Sopenharmony_ciout_free_bus: 1888c2ecf20Sopenharmony_ci free_mdio_bitbang(new_bus); 1898c2ecf20Sopenharmony_ciout_free_priv: 1908c2ecf20Sopenharmony_ci kfree(bitbang); 1918c2ecf20Sopenharmony_ciout: 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int fs_enet_mdio_remove(struct platform_device *ofdev) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct mii_bus *bus = platform_get_drvdata(ofdev); 1988c2ecf20Sopenharmony_ci struct bb_info *bitbang = bus->priv; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci mdiobus_unregister(bus); 2018c2ecf20Sopenharmony_ci free_mdio_bitbang(bus); 2028c2ecf20Sopenharmony_ci iounmap(bitbang->dir); 2038c2ecf20Sopenharmony_ci kfree(bitbang); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic const struct of_device_id fs_enet_mdio_bb_match[] = { 2098c2ecf20Sopenharmony_ci { 2108c2ecf20Sopenharmony_ci .compatible = "fsl,cpm2-mdio-bitbang", 2118c2ecf20Sopenharmony_ci }, 2128c2ecf20Sopenharmony_ci {}, 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic struct platform_driver fs_enet_bb_mdio_driver = { 2178c2ecf20Sopenharmony_ci .driver = { 2188c2ecf20Sopenharmony_ci .name = "fsl-bb-mdio", 2198c2ecf20Sopenharmony_ci .of_match_table = fs_enet_mdio_bb_match, 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci .probe = fs_enet_mdio_probe, 2228c2ecf20Sopenharmony_ci .remove = fs_enet_mdio_remove, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cimodule_platform_driver(fs_enet_bb_mdio_driver); 2268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 227