LCOV - code coverage report
Current view: top level - drivers/net/phy - fixed_phy.c (source / functions) Hit Total Coverage
Test: Real Lines: 16 131 12.2 %
Date: 2020-10-17 15:46:16 Functions: 0 15 0.0 %
Legend: Neither, QEMU, Real, Both Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0+
       2                 :            : /*
       3                 :            :  * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
       4                 :            :  *
       5                 :            :  * Author: Vitaly Bordug <vbordug@ru.mvista.com>
       6                 :            :  *         Anton Vorontsov <avorontsov@ru.mvista.com>
       7                 :            :  *
       8                 :            :  * Copyright (c) 2006-2007 MontaVista Software, Inc.
       9                 :            :  */
      10                 :            : 
      11                 :            : #include <linux/kernel.h>
      12                 :            : #include <linux/module.h>
      13                 :            : #include <linux/platform_device.h>
      14                 :            : #include <linux/list.h>
      15                 :            : #include <linux/mii.h>
      16                 :            : #include <linux/phy.h>
      17                 :            : #include <linux/phy_fixed.h>
      18                 :            : #include <linux/err.h>
      19                 :            : #include <linux/slab.h>
      20                 :            : #include <linux/of.h>
      21                 :            : #include <linux/gpio/consumer.h>
      22                 :            : #include <linux/seqlock.h>
      23                 :            : #include <linux/idr.h>
      24                 :            : #include <linux/netdevice.h>
      25                 :            : #include <linux/linkmode.h>
      26                 :            : 
      27                 :            : #include "swphy.h"
      28                 :            : 
      29                 :            : struct fixed_mdio_bus {
      30                 :            :         struct mii_bus *mii_bus;
      31                 :            :         struct list_head phys;
      32                 :            : };
      33                 :            : 
      34                 :            : struct fixed_phy {
      35                 :            :         int addr;
      36                 :            :         struct phy_device *phydev;
      37                 :            :         seqcount_t seqcount;
      38                 :            :         struct fixed_phy_status status;
      39                 :            :         bool no_carrier;
      40                 :            :         int (*link_update)(struct net_device *, struct fixed_phy_status *);
      41                 :            :         struct list_head node;
      42                 :            :         struct gpio_desc *link_gpiod;
      43                 :            : };
      44                 :            : 
      45                 :            : static struct platform_device *pdev;
      46                 :            : static struct fixed_mdio_bus platform_fmb = {
      47                 :            :         .phys = LIST_HEAD_INIT(platform_fmb.phys),
      48                 :            : };
      49                 :            : 
      50                 :          0 : int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier)
      51                 :            : {
      52                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
      53                 :          0 :         struct phy_device *phydev = dev->phydev;
      54                 :            :         struct fixed_phy *fp;
      55                 :            : 
      56                 :          0 :         if (!phydev || !phydev->mdio.bus)
      57                 :            :                 return -EINVAL;
      58                 :            : 
      59                 :          0 :         list_for_each_entry(fp, &fmb->phys, node) {
      60                 :          0 :                 if (fp->addr == phydev->mdio.addr) {
      61                 :          0 :                         fp->no_carrier = !new_carrier;
      62                 :          0 :                         return 0;
      63                 :            :                 }
      64                 :            :         }
      65                 :            :         return -EINVAL;
      66                 :            : }
      67                 :            : EXPORT_SYMBOL_GPL(fixed_phy_change_carrier);
      68                 :            : 
      69                 :          0 : static void fixed_phy_update(struct fixed_phy *fp)
      70                 :            : {
      71                 :          0 :         if (!fp->no_carrier && fp->link_gpiod)
      72                 :          0 :                 fp->status.link = !!gpiod_get_value_cansleep(fp->link_gpiod);
      73                 :          0 : }
      74                 :            : 
      75                 :          3 : static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
      76                 :            : {
      77                 :          3 :         struct fixed_mdio_bus *fmb = bus->priv;
      78                 :            :         struct fixed_phy *fp;
      79                 :            : 
      80                 :          3 :         list_for_each_entry(fp, &fmb->phys, node) {
      81                 :          0 :                 if (fp->addr == phy_addr) {
      82                 :            :                         struct fixed_phy_status state;
      83                 :            :                         int s;
      84                 :            : 
      85                 :            :                         do {
      86                 :            :                                 s = read_seqcount_begin(&fp->seqcount);
      87                 :          0 :                                 fp->status.link = !fp->no_carrier;
      88                 :            :                                 /* Issue callback if user registered it. */
      89                 :          0 :                                 if (fp->link_update)
      90                 :          0 :                                         fp->link_update(fp->phydev->attached_dev,
      91                 :            :                                                         &fp->status);
      92                 :            :                                 /* Check the GPIO for change in status */
      93                 :          0 :                                 fixed_phy_update(fp);
      94                 :          0 :                                 state = fp->status;
      95                 :          0 :                         } while (read_seqcount_retry(&fp->seqcount, s));
      96                 :            : 
      97                 :          0 :                         return swphy_read_reg(reg_num, &state);
      98                 :            :                 }
      99                 :            :         }
     100                 :            : 
     101                 :            :         return 0xFFFF;
     102                 :            : }
     103                 :            : 
     104                 :          0 : static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
     105                 :            :                             u16 val)
     106                 :            : {
     107                 :          0 :         return 0;
     108                 :            : }
     109                 :            : 
     110                 :            : /*
     111                 :            :  * If something weird is required to be done with link/speed,
     112                 :            :  * network driver is able to assign a function to implement this.
     113                 :            :  * May be useful for PHY's that need to be software-driven.
     114                 :            :  */
     115                 :          0 : int fixed_phy_set_link_update(struct phy_device *phydev,
     116                 :            :                               int (*link_update)(struct net_device *,
     117                 :            :                                                  struct fixed_phy_status *))
     118                 :            : {
     119                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
     120                 :            :         struct fixed_phy *fp;
     121                 :            : 
     122                 :          0 :         if (!phydev || !phydev->mdio.bus)
     123                 :            :                 return -EINVAL;
     124                 :            : 
     125                 :          0 :         list_for_each_entry(fp, &fmb->phys, node) {
     126                 :          0 :                 if (fp->addr == phydev->mdio.addr) {
     127                 :          0 :                         fp->link_update = link_update;
     128                 :          0 :                         fp->phydev = phydev;
     129                 :          0 :                         return 0;
     130                 :            :                 }
     131                 :            :         }
     132                 :            : 
     133                 :            :         return -ENOENT;
     134                 :            : }
     135                 :            : EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
     136                 :            : 
     137                 :          0 : static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr,
     138                 :            :                                struct fixed_phy_status *status,
     139                 :            :                                struct gpio_desc *gpiod)
     140                 :            : {
     141                 :            :         int ret;
     142                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
     143                 :            :         struct fixed_phy *fp;
     144                 :            : 
     145                 :          0 :         ret = swphy_validate_state(status);
     146                 :          0 :         if (ret < 0)
     147                 :            :                 return ret;
     148                 :            : 
     149                 :          0 :         fp = kzalloc(sizeof(*fp), GFP_KERNEL);
     150                 :          0 :         if (!fp)
     151                 :            :                 return -ENOMEM;
     152                 :            : 
     153                 :            :         seqcount_init(&fp->seqcount);
     154                 :            : 
     155                 :          0 :         if (irq != PHY_POLL)
     156                 :          0 :                 fmb->mii_bus->irq[phy_addr] = irq;
     157                 :            : 
     158                 :          0 :         fp->addr = phy_addr;
     159                 :          0 :         fp->status = *status;
     160                 :          0 :         fp->link_gpiod = gpiod;
     161                 :            : 
     162                 :          0 :         fixed_phy_update(fp);
     163                 :            : 
     164                 :          0 :         list_add_tail(&fp->node, &fmb->phys);
     165                 :            : 
     166                 :          0 :         return 0;
     167                 :            : }
     168                 :            : 
     169                 :          0 : int fixed_phy_add(unsigned int irq, int phy_addr,
     170                 :            :                   struct fixed_phy_status *status) {
     171                 :            : 
     172                 :          0 :         return fixed_phy_add_gpiod(irq, phy_addr, status, NULL);
     173                 :            : }
     174                 :            : EXPORT_SYMBOL_GPL(fixed_phy_add);
     175                 :            : 
     176                 :            : static DEFINE_IDA(phy_fixed_ida);
     177                 :            : 
     178                 :          0 : static void fixed_phy_del(int phy_addr)
     179                 :            : {
     180                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
     181                 :            :         struct fixed_phy *fp, *tmp;
     182                 :            : 
     183                 :          0 :         list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
     184                 :          0 :                 if (fp->addr == phy_addr) {
     185                 :            :                         list_del(&fp->node);
     186                 :          0 :                         if (fp->link_gpiod)
     187                 :          0 :                                 gpiod_put(fp->link_gpiod);
     188                 :          0 :                         kfree(fp);
     189                 :          0 :                         ida_simple_remove(&phy_fixed_ida, phy_addr);
     190                 :          0 :                         return;
     191                 :            :                 }
     192                 :            :         }
     193                 :            : }
     194                 :            : 
     195                 :            : #ifdef CONFIG_OF_GPIO
     196                 :          0 : static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
     197                 :            : {
     198                 :            :         struct device_node *fixed_link_node;
     199                 :            :         struct gpio_desc *gpiod;
     200                 :            : 
     201                 :          0 :         if (!np)
     202                 :            :                 return NULL;
     203                 :            : 
     204                 :          0 :         fixed_link_node = of_get_child_by_name(np, "fixed-link");
     205                 :          0 :         if (!fixed_link_node)
     206                 :            :                 return NULL;
     207                 :            : 
     208                 :            :         /*
     209                 :            :          * As the fixed link is just a device tree node without any
     210                 :            :          * Linux device associated with it, we simply have obtain
     211                 :            :          * the GPIO descriptor from the device tree like this.
     212                 :            :          */
     213                 :          0 :         gpiod = gpiod_get_from_of_node(fixed_link_node, "link-gpios", 0,
     214                 :            :                                        GPIOD_IN, "mdio");
     215                 :          0 :         if (IS_ERR(gpiod) && PTR_ERR(gpiod) != -EPROBE_DEFER) {
     216                 :          0 :                 if (PTR_ERR(gpiod) != -ENOENT)
     217                 :          0 :                         pr_err("error getting GPIO for fixed link %pOF, proceed without\n",
     218                 :            :                                fixed_link_node);
     219                 :            :                 gpiod = NULL;
     220                 :            :         }
     221                 :          0 :         of_node_put(fixed_link_node);
     222                 :            : 
     223                 :          0 :         return gpiod;
     224                 :            : }
     225                 :            : #else
     226                 :            : static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np)
     227                 :            : {
     228                 :            :         return NULL;
     229                 :            : }
     230                 :            : #endif
     231                 :            : 
     232                 :          0 : static struct phy_device *__fixed_phy_register(unsigned int irq,
     233                 :            :                                                struct fixed_phy_status *status,
     234                 :            :                                                struct device_node *np,
     235                 :            :                                                struct gpio_desc *gpiod)
     236                 :            : {
     237                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
     238                 :            :         struct phy_device *phy;
     239                 :            :         int phy_addr;
     240                 :            :         int ret;
     241                 :            : 
     242                 :          0 :         if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED)
     243                 :            :                 return ERR_PTR(-EPROBE_DEFER);
     244                 :            : 
     245                 :            :         /* Check if we have a GPIO associated with this fixed phy */
     246                 :          0 :         if (!gpiod) {
     247                 :          0 :                 gpiod = fixed_phy_get_gpiod(np);
     248                 :          0 :                 if (IS_ERR(gpiod))
     249                 :            :                         return ERR_CAST(gpiod);
     250                 :            :         }
     251                 :            : 
     252                 :            :         /* Get the next available PHY address, up to PHY_MAX_ADDR */
     253                 :          0 :         phy_addr = ida_simple_get(&phy_fixed_ida, 0, PHY_MAX_ADDR, GFP_KERNEL);
     254                 :          0 :         if (phy_addr < 0)
     255                 :          0 :                 return ERR_PTR(phy_addr);
     256                 :            : 
     257                 :          0 :         ret = fixed_phy_add_gpiod(irq, phy_addr, status, gpiod);
     258                 :          0 :         if (ret < 0) {
     259                 :          0 :                 ida_simple_remove(&phy_fixed_ida, phy_addr);
     260                 :          0 :                 return ERR_PTR(ret);
     261                 :            :         }
     262                 :            : 
     263                 :          0 :         phy = get_phy_device(fmb->mii_bus, phy_addr, false);
     264                 :          0 :         if (IS_ERR(phy)) {
     265                 :          0 :                 fixed_phy_del(phy_addr);
     266                 :          0 :                 return ERR_PTR(-EINVAL);
     267                 :            :         }
     268                 :            : 
     269                 :            :         /* propagate the fixed link values to struct phy_device */
     270                 :          0 :         phy->link = status->link;
     271                 :          0 :         if (status->link) {
     272                 :          0 :                 phy->speed = status->speed;
     273                 :          0 :                 phy->duplex = status->duplex;
     274                 :          0 :                 phy->pause = status->pause;
     275                 :          0 :                 phy->asym_pause = status->asym_pause;
     276                 :            :         }
     277                 :            : 
     278                 :          0 :         of_node_get(np);
     279                 :          0 :         phy->mdio.dev.of_node = np;
     280                 :          0 :         phy->is_pseudo_fixed_link = true;
     281                 :            : 
     282                 :          0 :         switch (status->speed) {
     283                 :            :         case SPEED_1000:
     284                 :            :                 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
     285                 :            :                                  phy->supported);
     286                 :            :                 linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
     287                 :            :                                  phy->supported);
     288                 :            :                 /* fall through */
     289                 :            :         case SPEED_100:
     290                 :            :                 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
     291                 :            :                                  phy->supported);
     292                 :            :                 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
     293                 :            :                                  phy->supported);
     294                 :            :                 /* fall through */
     295                 :            :         case SPEED_10:
     296                 :            :         default:
     297                 :            :                 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
     298                 :            :                                  phy->supported);
     299                 :            :                 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
     300                 :            :                                  phy->supported);
     301                 :            :         }
     302                 :            : 
     303                 :          0 :         phy_advertise_supported(phy);
     304                 :            : 
     305                 :          0 :         ret = phy_device_register(phy);
     306                 :          0 :         if (ret) {
     307                 :          0 :                 phy_device_free(phy);
     308                 :          0 :                 of_node_put(np);
     309                 :          0 :                 fixed_phy_del(phy_addr);
     310                 :          0 :                 return ERR_PTR(ret);
     311                 :            :         }
     312                 :            : 
     313                 :            :         return phy;
     314                 :            : }
     315                 :            : 
     316                 :          0 : struct phy_device *fixed_phy_register(unsigned int irq,
     317                 :            :                                       struct fixed_phy_status *status,
     318                 :            :                                       struct device_node *np)
     319                 :            : {
     320                 :          0 :         return __fixed_phy_register(irq, status, np, NULL);
     321                 :            : }
     322                 :            : EXPORT_SYMBOL_GPL(fixed_phy_register);
     323                 :            : 
     324                 :            : struct phy_device *
     325                 :          0 : fixed_phy_register_with_gpiod(unsigned int irq,
     326                 :            :                               struct fixed_phy_status *status,
     327                 :            :                               struct gpio_desc *gpiod)
     328                 :            : {
     329                 :          0 :         return __fixed_phy_register(irq, status, NULL, gpiod);
     330                 :            : }
     331                 :            : EXPORT_SYMBOL_GPL(fixed_phy_register_with_gpiod);
     332                 :            : 
     333                 :          0 : void fixed_phy_unregister(struct phy_device *phy)
     334                 :            : {
     335                 :          0 :         phy_device_remove(phy);
     336                 :          0 :         of_node_put(phy->mdio.dev.of_node);
     337                 :          0 :         fixed_phy_del(phy->mdio.addr);
     338                 :          0 : }
     339                 :            : EXPORT_SYMBOL_GPL(fixed_phy_unregister);
     340                 :            : 
     341                 :          3 : static int __init fixed_mdio_bus_init(void)
     342                 :            : {
     343                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
     344                 :            :         int ret;
     345                 :            : 
     346                 :          3 :         pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
     347                 :          3 :         if (IS_ERR(pdev))
     348                 :          0 :                 return PTR_ERR(pdev);
     349                 :            : 
     350                 :          3 :         fmb->mii_bus = mdiobus_alloc();
     351                 :          3 :         if (fmb->mii_bus == NULL) {
     352                 :            :                 ret = -ENOMEM;
     353                 :            :                 goto err_mdiobus_reg;
     354                 :            :         }
     355                 :            : 
     356                 :          3 :         snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
     357                 :          3 :         fmb->mii_bus->name = "Fixed MDIO Bus";
     358                 :          3 :         fmb->mii_bus->priv = fmb;
     359                 :          3 :         fmb->mii_bus->parent = &pdev->dev;
     360                 :          3 :         fmb->mii_bus->read = &fixed_mdio_read;
     361                 :          3 :         fmb->mii_bus->write = &fixed_mdio_write;
     362                 :            : 
     363                 :          3 :         ret = mdiobus_register(fmb->mii_bus);
     364                 :          3 :         if (ret)
     365                 :            :                 goto err_mdiobus_alloc;
     366                 :            : 
     367                 :            :         return 0;
     368                 :            : 
     369                 :            : err_mdiobus_alloc:
     370                 :          0 :         mdiobus_free(fmb->mii_bus);
     371                 :            : err_mdiobus_reg:
     372                 :          0 :         platform_device_unregister(pdev);
     373                 :          0 :         return ret;
     374                 :            : }
     375                 :            : module_init(fixed_mdio_bus_init);
     376                 :            : 
     377                 :          0 : static void __exit fixed_mdio_bus_exit(void)
     378                 :            : {
     379                 :            :         struct fixed_mdio_bus *fmb = &platform_fmb;
     380                 :            :         struct fixed_phy *fp, *tmp;
     381                 :            : 
     382                 :          0 :         mdiobus_unregister(fmb->mii_bus);
     383                 :          0 :         mdiobus_free(fmb->mii_bus);
     384                 :          0 :         platform_device_unregister(pdev);
     385                 :            : 
     386                 :          0 :         list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
     387                 :            :                 list_del(&fp->node);
     388                 :          0 :                 kfree(fp);
     389                 :            :         }
     390                 :          0 :         ida_destroy(&phy_fixed_ida);
     391                 :          0 : }
     392                 :            : module_exit(fixed_mdio_bus_exit);
     393                 :            : 
     394                 :            : MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
     395                 :            : MODULE_AUTHOR("Vitaly Bordug");
     396                 :            : MODULE_LICENSE("GPL");
    

Generated by: LCOV version 1.14