// SPDX-License-Identifier: GPL-2.0-or-later /* * Freescale PowerQUICC Ethernet Driver -- MIIM bus implementation * Provides Bus interface for MIIM regs * * Author: Andy Fleming <[email protected]> * Modifier: Sandeep Gopalpet <[email protected]> * * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc. * * Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips) */ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/mii.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_mdio.h> #include <linux/property.h> #include <asm/io.h> #if IS_ENABLED(CONFIG_UCC_GETH) #include <soc/fsl/qe/ucc.h> #endif #include "gianfar.h" #define MIIMIND_BUSY … #define MIIMIND_NOTVALID … #define MIIMCFG_INIT_VALUE … #define MIIMCFG_RESET … #define MII_READ_COMMAND … struct fsl_pq_mii { … }; struct fsl_pq_mdio { … } __packed; /* Number of microseconds to wait for an MII register to respond */ #define MII_TIMEOUT … struct fsl_pq_mdio_priv { … }; /* * Per-device-type data. Each type of device tree node that we support gets * one of these. * * @mii_offset: the offset of the MII registers within the memory map of the * node. Some nodes define only the MII registers, and some define the whole * MAC (which includes the MII registers). * * @get_tbipa: determines the address of the TBIPA register * * @ucc_configure: a special function for extra QE configuration */ struct fsl_pq_mdio_data { … }; /* * Write value to the PHY at mii_id at register regnum, on the bus attached * to the local interface, which may be different from the generic mdio bus * (tied to a single interface), waiting until the write is done before * returning. This is helpful in programming interfaces like the TBI which * control interfaces like onchip SERDES and are always tied to the local * mdio pins, which may not be the same as system mdio bus, used for * controlling the external PHYs, for example. */ static int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) { … } /* * Read the bus for PHY at addr mii_id, register regnum, and return the value. * Clears miimcom first. * * All PHY operation done on the bus attached to the local interface, which * may be different from the generic mdio bus. This is helpful in programming * interfaces like the TBI which, in turn, control interfaces like on-chip * SERDES and are always tied to the local mdio pins, which may not be the * same as system mdio bus, used for controlling the external PHYs, for eg. */ static int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { … } /* Reset the MIIM registers, and wait for the bus to free */ static int fsl_pq_mdio_reset(struct mii_bus *bus) { … } #if IS_ENABLED(CONFIG_GIANFAR) /* * Return the TBIPA address, starting from the address * of the mapped GFAR MDIO registers (struct gfar) * This is mildly evil, but so is our hardware for doing this. * Also, we have to cast back to struct gfar because of * definition weirdness done in gianfar.h. */ static uint32_t __iomem *get_gfar_tbipa_from_mdio(void __iomem *p) { … } /* * Return the TBIPA address, starting from the address * of the mapped GFAR MII registers (gfar_mii_regs[] within struct gfar) */ static uint32_t __iomem *get_gfar_tbipa_from_mii(void __iomem *p) { … } /* * Return the TBIPAR address for an eTSEC2 node */ static uint32_t __iomem *get_etsec_tbipa(void __iomem *p) { … } #endif #if IS_ENABLED(CONFIG_UCC_GETH) /* * Return the TBIPAR address for a QE MDIO node, starting from the address * of the mapped MII registers (struct fsl_pq_mii) */ static uint32_t __iomem *get_ucc_tbipa(void __iomem *p) { struct fsl_pq_mdio __iomem *mdio = container_of(p, struct fsl_pq_mdio, mii); return &mdio->utbipar; } /* * Find the UCC node that controls the given MDIO node * * For some reason, the QE MDIO nodes are not children of the UCC devices * that control them. Therefore, we need to scan all UCC nodes looking for * the one that encompases the given MDIO node. We do this by comparing * physical addresses. The 'start' and 'end' addresses of the MDIO node are * passed, and the correct UCC node will cover the entire address range. * * This assumes that there is only one QE MDIO node in the entire device tree. */ static void ucc_configure(phys_addr_t start, phys_addr_t end) { static bool found_mii_master; struct device_node *np = NULL; if (found_mii_master) return; for_each_compatible_node(np, NULL, "ucc_geth") { struct resource res; const uint32_t *iprop; uint32_t id; int ret; ret = of_address_to_resource(np, 0, &res); if (ret < 0) { pr_debug("fsl-pq-mdio: no address range in node %pOF\n", np); continue; } /* if our mdio regs fall within this UCC regs range */ if ((start < res.start) || (end > res.end)) continue; iprop = of_get_property(np, "cell-index", NULL); if (!iprop) { iprop = of_get_property(np, "device-id", NULL); if (!iprop) { pr_debug("fsl-pq-mdio: no UCC ID in node %pOF\n", np); continue; } } id = be32_to_cpup(iprop); /* * cell-index and device-id for QE nodes are * numbered from 1, not 0. */ if (ucc_set_qe_mux_mii_mng(id - 1) < 0) { pr_debug("fsl-pq-mdio: invalid UCC ID in node %pOF\n", np); continue; } pr_debug("fsl-pq-mdio: setting node UCC%u to MII master\n", id); found_mii_master = true; } } #endif static const struct of_device_id fsl_pq_mdio_match[] = …; MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match); static void set_tbipa(const u32 tbipa_val, struct platform_device *pdev, uint32_t __iomem * (*get_tbipa)(void __iomem *), void __iomem *reg_map, struct resource *reg_res) { … } static int fsl_pq_mdio_probe(struct platform_device *pdev) { … } static void fsl_pq_mdio_remove(struct platform_device *pdev) { … } static struct platform_driver fsl_pq_mdio_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;