On Fri, Oct 07, 2016 at 06:18:37PM +0300, Pantelis Antoniou wrote: > From: Georgi Vlaev <gvlaev@xxxxxxxxxxx> > > Add driver for the MDIO IP block present in Juniper's > SAM FPGA. > > This driver supports only Clause 45 of the 802.3 spec. > > Note that due to the fact that there are no drivers for > Broadcom/Avago retimers on 10/40Ge path that are controlled > from the MDIO interface there is a method to have direct > access to registers via a debugfs interface. This seems to be the wrong solution. Why not write those drivers? Controlling stuff from user space is generally frowned up. So i expect DaveM will NACK this patch. Please remove all the debugfs stuff. > +static int mdio_sam_stat_wait(struct mii_bus *bus, u32 wait_mask) > +{ > + struct mdio_sam_data *data = bus->priv; > + unsigned long timeout; > + u32 stat; > + > + timeout = jiffies + msecs_to_jiffies(MDIO_RDY_TMO); > + do { > + stat = ioread32(data->base + MDIO_STATUS); > + if (stat & wait_mask) > + return 0; > + > + usleep_range(50, 100); > + } while (time_before(jiffies, timeout)); > + > + return -EBUSY; I've recently had to fix a loop like this in another driver. usleep_range(50, 100) can sleep for a lot longer. If it sleeps for MDIO_RDY_TMO you exit out with -EBUSY after a single iteration, which is not what you want. It is better to make a fixed number of iterations rather than a timeout. > +} > + > +static int mdio_sam_read(struct mii_bus *bus, int phy_id, int regnum) > +{ > + struct mdio_sam_data *data = bus->priv; > + u32 command, res; > + int ret; > + > + /* mdiobus_read holds the bus->mdio_lock mutex */ > + > + if (!(regnum & MII_ADDR_C45)) > + return -ENXIO; > + > + ret = mdio_sam_stat_wait(bus, STAT_REG_RDY); > + if (ret < 0) > + return ret; > + > + command = regnum & 0x1fffff; /* regnum = (dev_id << 16) | reg */ > + command |= ((phy_id & 0x1f) << 21); > + > + iowrite32(command, data->base + MDIO_CMD1); > + ioread32(data->base + MDIO_CMD1); > + iowrite32(CMD2_READ | CMD2_ENABLE, data->base + MDIO_CMD2); > + ioread32(data->base + MDIO_CMD2); Why do you need to read the values back? Hardware bug? > + iowrite32(TBL_CMD_REG_GO, data->base + MDIO_TBL_CMD); > + ioread32(data->base + MDIO_TBL_CMD); Although not wrong, most drivers use writel(). > + > + usleep_range(50, 100); > + > + ret = mdio_sam_stat_wait(bus, (STAT_REG_DONE | STAT_REG_ERR)); Do you really need a wait before calling mdio_sam_stat_wait()? Isn't that what it is supposed to do, wait... > + if (ret < 0) > + return ret; > + > + res = ioread32(data->base + MDIO_RESULT); > + > + if (res & RES_ERROR || !(res & RES_SUCCESS)) > + return -EIO; > + > + return (res & 0xffff); > +} > + > +static int mdio_sam_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) > +{ > + struct mdio_sam_data *data = bus->priv; > + u32 command; > + int ret; > + > + /* mdiobus_write holds the bus->mdio_lock mutex */ > + > + if (!(regnum & MII_ADDR_C45)) > + return -ENXIO; > + > + ret = mdio_sam_stat_wait(bus, STAT_REG_RDY); > + if (ret < 0) > + return ret; > + > + command = regnum & 0x1fffff; /* regnum = (dev_id << 16) | reg */ > + command |= ((phy_id & 0x1f) << 21); > + > + iowrite32(command, data->base + MDIO_CMD1); > + ioread32(data->base + MDIO_CMD1); > + iowrite32(CMD2_ENABLE | val, data->base + MDIO_CMD2); > + ioread32(data->base + MDIO_CMD2); > + iowrite32(TBL_CMD_REG_GO, data->base + MDIO_TBL_CMD); > + ioread32(data->base + MDIO_TBL_CMD); > + > + usleep_range(50, 100); > + > + ret = mdio_sam_stat_wait(bus, (STAT_REG_DONE | STAT_REG_ERR)); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static int mdio_sam_reset(struct mii_bus *bus) > +{ > + struct mdio_sam_data *data = bus->priv; > + > + iowrite32(TBL_CMD_SOFT_RESET, data->base + MDIO_TBL_CMD); > + ioread32(data->base + MDIO_TBL_CMD); > + mdelay(10); > + iowrite32(0, data->base + MDIO_TBL_CMD); > + ioread32(data->base + MDIO_TBL_CMD); > + > + /* zero tables */ > + memset_io(data->base + MDIO_CMD1, 0, 0x1000); > + memset_io(data->base + MDIO_PRI_CMD1, 0, 0x1000); What tables? > + > + return 0; > +} > + > +static int mdio_sam_of_register_bus(struct platform_device *pdev, > + struct device_node *np, void __iomem *base) > +{ > + struct mii_bus *bus; > + struct mdio_sam_data *data; > + u32 reg; > + int ret; > + > + bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*data)); > + if (!bus) > + return -ENOMEM; > + > + /* bus offset */ > + ret = of_property_read_u32(np, "reg", ®); > + if (ret) > + return -ENODEV; > + > + data = bus->priv; > + data->base = base + reg; > + > + bus->parent = &pdev->dev; > + bus->name = "mdio-sam"; > + bus->read = mdio_sam_read; > + bus->write = mdio_sam_write; > + bus->reset = mdio_sam_reset; > + snprintf(bus->id, MII_BUS_ID_SIZE, "mdiosam-%x-%x", pdev->id, reg); > + > + ret = of_mdiobus_register(bus, np); > + if (ret < 0) > + return ret; > +#ifdef CONFIG_DEBUG_FS > + ret = mdio_sam_debugfs_init(bus); > + if (ret < 0) > + goto err_unregister; > +#endif > + ret = device_create_bin_file(&bus->dev, &bin_attr_raw_io); > + if (ret) > + goto err_debugfs; > + > + return 0; > + > +err_debugfs: > +#ifdef CONFIG_DEBUG_FS > + mdio_sam_debugfs_remove(bus); > +#endif > +err_unregister: > + mdiobus_unregister(bus); > + > + return ret; > +} > + > +static int mdio_sam_of_unregister_bus(struct device_node *np) > +{ > + struct mii_bus *bus; > + > + bus = of_mdio_find_bus(np); > + if (bus) { > + device_remove_bin_file(&bus->dev, &bin_attr_raw_io); > +#ifdef CONFIG_DEBUG_FS > + mdio_sam_debugfs_remove(bus); > +#endif > + mdiobus_unregister(bus); > + } > + return 0; > +} > + > +static int mdio_sam_probe(struct platform_device *pdev) > +{ > + struct device_node *np; > + struct resource *res; > + void __iomem *base; > + int ret; > + > + if (!pdev->dev.of_node) > + return -ENODEV; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + base = devm_ioremap_nocache(&pdev->dev, res->start, > + resource_size(res)); Why nocache? > + if (IS_ERR(base)) > + return PTR_ERR(base); > + > + for_each_available_child_of_node(pdev->dev.of_node, np) { > + ret = mdio_sam_of_register_bus(pdev, np, base); > + if (ret) > + goto err; > + } This is odd. There does not seem to be any shared resources. So you should really have one MDIO bus as one device in the device tree. Andrew > + > + return 0; > +err: > + /* roll back everything */ > + for_each_available_child_of_node(pdev->dev.of_node, np) > + mdio_sam_of_unregister_bus(np); > + > + return ret; > +} > + > +static int mdio_sam_remove(struct platform_device *pdev) > +{ > + struct device_node *np; > + > + for_each_available_child_of_node(pdev->dev.of_node, np) > + mdio_sam_of_unregister_bus(np); > + > + return 0; > +} > + > +static const struct of_device_id mdio_sam_of_match[] = { > + { .compatible = "jnx,mdio-sam" }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, mdio_sam_of_match); > + > +static struct platform_driver mdio_sam_driver = { > + .probe = mdio_sam_probe, > + .remove = mdio_sam_remove, > + .driver = { > + .name = "mdio-sam", > + .owner = THIS_MODULE, > + .of_match_table = mdio_sam_of_match, > + }, > +}; > + > +module_platform_driver(mdio_sam_driver); > + > +MODULE_ALIAS("platform:mdio-sam"); > +MODULE_AUTHOR("Georgi Vlaev <gvlaev@xxxxxxxxxxx>"); > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Juniper Networks SAM MDIO bus driver"); > -- > 1.9.1 > -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html