Message ID | 1461230323-27891-2-git-send-email-pramod.kumar@broadcom.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
From: Pramod Kumar <pramod.kumar@broadcom.com> Date: Thu, 21 Apr 2016 14:48:38 +0530 > +struct shared_mdio_master *shared_mdio_alloc_master(struct device *parent, > + struct device_node *node) > +{ > + int ret = 0; > + struct shared_mdio_master *master; Always order local variable declarations in reverse christmas tree (longest to shortest line) order. > +static int shared_mdio_driver_probe(struct device *dev) > +{ > + int rc; > + struct shared_mdio_master *master = to_shared_mdio_master(dev); > + struct shared_mdio_driver *drv = to_shared_mdio_driver(dev->driver); Likewise. Please audit your entire submission for this issue.
Hi David, Thanks for providing input over the patch. Will address the comment as described below. > -----Original Message----- > From: David Miller [mailto:davem@davemloft.net] > Sent: 24 April 2016 23:48 > To: pramod.kumar@broadcom.com > Cc: robh+dt@kernel.org; catalin.marinas@arm.com; will.deacon@arm.com; > yamada.masahiro@socionext.com; wens@csie.org; bcm-kernel-feedback- > list@broadcom.com; pawel.moll@arm.com; mark.rutland@arm.com; > arnd@arndb.de; suzuki.poulose@arm.com; punit.agrawal@arm.com; > devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux- > kernel@vger.kernel.org; netdev@vger.kernel.org; anup.patel@broadcom.com > Subject: Re: [PATCH 1/6] bus: Add shared MDIO bus framework > > From: Pramod Kumar <pramod.kumar@broadcom.com> > Date: Thu, 21 Apr 2016 14:48:38 +0530 > > > +struct shared_mdio_master *shared_mdio_alloc_master(struct device > *parent, > > + struct device_node *node) > > +{ > > + int ret = 0; > > + struct shared_mdio_master *master; > > Always order local variable declarations in reverse christmas tree (longest to > shortest line) order. > Sure. Next patch will address this. > > +static int shared_mdio_driver_probe(struct device *dev) { > > + int rc; > > + struct shared_mdio_master *master = to_shared_mdio_master(dev); > > + struct shared_mdio_driver *drv = to_shared_mdio_driver(dev->driver); > > Likewise. Sure. > Please audit your entire submission for this issue. Sure. I'll audit the entire patch set for above issue. Regards, Pramod
Hi Pramod I took a closer look. I don't see why the current MDIO code should not be used, rather than adding a new framework. What you need for your Non Ethernet PHYs is that they are somehow probed. The current MDIO code will do that, based on the compatible string. An mdio device gets passed a struct mdio_device * to its probe function, giving you the bus and address on the bus for the device. Your PHY driver can then register itself using devm_of_phy_provider_register(). The user of the PHY then needs to use devm_phy_get() to get a handle on the phy, and can then use phy_power_on()/phy_power_off(). There is a very simple example here for an MDIO device driver: http://thread.gmane.org/gmane.linux.network/393532 The muxing of the MDIO busses looks a little tricky. At the moment you have: writel(cmd, base + MDIO_PARAM_OFFSET); which mixes together the muxing parameters and the write value. Can this register be accessed as two 16 bit registers? If it can be, you can cleanly separate out the muxing. Take a look at mdio-mux-gpio.c and mdio-mux-mmioreg.c for examples of MDIO muxes. Andrew
Hi Andrew, Thanks for reviewing. I really appreciate your effort it. I am already aware of MDIO mux framework but did not see it fit for our use case due to below limitations: 1. Current MDIO mux framework is Ethernet centric and it is only meant to mux multiple MII buses using same MDIO controller. This means it is only meant for MII-compliant PHY devices (i.e. PHY devices having registers as-per MII specs). This is not the case with Broadcom SATA PHYs, PCIe PHYs, and USB PHYs even if we are sharing same MDIO controller for accessing these PHYs. 2. The MDIO mux framework registers each child bus as MII bus. The Linux Ethernet MDIO framework will scan for all attached PHY devices on given MII bus and try to read MII PHY_ID register which is not present in all Broadcom non-ethernet PHYs. 3. Let's say we ignore point1 and point2 above and go ahead and use MDIO mux framework then we will still have to emulated MII PHY_ID read for non-ethernet PHYs. Let's say we also emulate MII PHY_ID read in non-ethernet PHYs then next thing is non-ethernet PHYs don't follow MII state machine so all other MII PHY register read/write will not work. 4. Apart from these, by using MDIO mux framework we are making our non-ethernet PHYs dependent on Linux network drivers which is not acceptable. What if some product line does not need network subsystem at all? As you can see from above points, trying to re-use Linux Ethernet MDIO mux framework for non-Ethernet PHYs is not the right way. The sole reason being Linux MDIO mux framework is not generic enough to be shared across IO subsystems. Due to this reason we had to come-up with "Shared MDIO framework" which is very simple, generic and independent of I/O subsystems. I'll add PCIe PHYs driver based on Shared MDIO framework in next patch revision to get a feel of its need. Regards, Pramod > -----Original Message----- > From: Andrew Lunn [mailto:andrew@lunn.ch] > Sent: 26 April 2016 02:27 > To: Pramod Kumar > Cc: Rob Herring; Catalin Marinas; Will Deacon; Masahiro Yamada; Chen-Yu Tsai; > Mark Rutland; devicetree@vger.kernel.org; Pawel Moll; Arnd Bergmann; Suzuki > K Poulose; netdev@vger.kernel.org; Punit Agrawal; linux- > kernel@vger.kernel.org; BCM Kernel Feedback; linux-arm- > kernel@lists.infradead.org; Anup Patel > Subject: Re: [PATCH 1/6] bus: Add shared MDIO bus framework > > Hi Pramod > > I took a closer look. I don't see why the current MDIO code should not be used, > rather than adding a new framework. > > What you need for your Non Ethernet PHYs is that they are somehow probed. > The current MDIO code will do that, based on the compatible string. An mdio > device gets passed a struct mdio_device * to its probe function, giving you the > bus and address on the bus for the device. Your PHY driver can then register > itself using devm_of_phy_provider_register(). The user of the PHY then needs to > use > devm_phy_get() to get a handle on the phy, and can then use > phy_power_on()/phy_power_off(). > > There is a very simple example here for an MDIO device driver: > > http://thread.gmane.org/gmane.linux.network/393532 > > The muxing of the MDIO busses looks a little tricky. At the moment you have: > > writel(cmd, base + MDIO_PARAM_OFFSET); > > which mixes together the muxing parameters and the write value. Can this > register be accessed as two 16 bit registers? If it can be, you can cleanly > separate out the muxing. > > Take a look at mdio-mux-gpio.c and mdio-mux-mmioreg.c for examples of > MDIO muxes. > > Andrew
On Tue, Apr 26, 2016 at 02:03:27PM +0530, Pramod Kumar wrote: > Hi Andrew, > > Thanks for reviewing. I really appreciate your effort it. > > I am already aware of MDIO mux framework but did not see it fit for our > use case due to below limitations: > > 1. Current MDIO mux framework is Ethernet centric and it is only meant to > mux multiple MII buses using same MDIO controller. This means it is only > meant for MII-compliant PHY devices (i.e. PHY devices having registers > as-per MII specs). Nope, this is wrong. You can have a mixture of PHYs and other MDIO devices on the MII bus. The framework does not care. A PHY is a special case of an MDIO device. > 2. The MDIO mux framework registers each child bus as MII bus. The Linux > Ethernet MDIO framework will scan for all attached PHY devices on given > MII bus and try to read MII PHY_ID register which is not present in all > Broadcom non-ethernet PHYs. It only performs a scan if you don't list the devices in the device tree. If you do list devices, and include the address on the bus, it never scans. A good example of this is Ethernet switches. They occupy multiple addresses on the MDIO bus, and also do not implement the PHY_ID register at each address. Yet the MDIO layer is happy with this. > 3. Let's say we ignore point1 and point2 above and go ahead and use MDIO > mux framework then we will still have to emulated MII PHY_ID read for > non-ethernet PHYs. Nope. Not at all. You have an MDIO device on the bus, not an Ethernet PHY. Hence the device is not liked to the PHY state machine, etc. The key concept to get here is that there are MDIO devices, and a subset of MDIO devices are Ethernet PHYs..... > 4. Apart from these, by using MDIO mux framework we are making our > non-ethernet PHYs dependent on Linux network drivers which is not > acceptable. What if some product line does not need network subsystem at > all? This is your only valid point. However, does Broadcom have a product line which does not include networking? Is not Broadcom a network SoC vendor? > > As you can see from above points, trying to re-use Linux Ethernet MDIO mux > framework for non-Ethernet PHYs is not the right way. And as i pointed out, all your arguments are wrong, bar one. And i doubt that one argument is sufficient to duplicate a lot of code which already exists and does 95% of what you need. > I'll add PCIe PHYs driver based on Shared MDIO framework in next patch > revision to get a feel of its need. Great. Lets then see what is needed to turn it into an MDIO device. Andrew
From: Andrew Lunn <andrew@lunn.ch> Date: Tue, 26 Apr 2016 14:13:35 +0200 > On Tue, Apr 26, 2016 at 02:03:27PM +0530, Pramod Kumar wrote: >> As you can see from above points, trying to re-use Linux Ethernet MDIO mux >> framework for non-Ethernet PHYs is not the right way. > > And as i pointed out, all your arguments are wrong, bar one. And i > doubt that one argument is sufficient to duplicate a lot of code which > already exists and does 95% of what you need. +1
On 26/04/16 05:13, Andrew Lunn wrote: >> 4. Apart from these, by using MDIO mux framework we are making our >> non-ethernet PHYs dependent on Linux network drivers which is not >> acceptable. What if some product line does not need network subsystem at >> all? > > This is your only valid point. However, does Broadcom have a product > line which does not include networking? Is not Broadcom a network SoC > vendor? But even with that, there is no reason why we could not decouple the PHYLIB MDIO framework from PHYLIB and make it available as a more standalone subsystem which can be utilized when you have a mix of MDIO devices like here. I am not clear on how common a shared MDIO bus is on other SoCs, but the other Broadcom SoCs I am familiar with have dedicated MDIO buses instances per type of PHY (PCIe, BUSB, Ethernet), thus making the split a ton easier.
> I am not clear on how common a shared MDIO bus is on other SoCs, but the > other Broadcom SoCs I am familiar with have dedicated MDIO buses > instances per type of PHY (PCIe, BUSB, Ethernet), thus making the split > a ton easier. I don't actually see this shared bus being an issue, once the mux it implemented. With the mux, you see N MDIO busses, each acting like a normal MDIO bus. I've used the GPIO variety and had no issues: http://marc.info/?l=linux-arm-kernel&m=145910090401796&w=2 Andrew
On Tuesday 26 April 2016 10:23:02 Florian Fainelli wrote: > On 26/04/16 05:13, Andrew Lunn wrote: > >> 4. Apart from these, by using MDIO mux framework we are making our > >> non-ethernet PHYs dependent on Linux network drivers which is not > >> acceptable. What if some product line does not need network subsystem at > >> all? > > > > This is your only valid point. However, does Broadcom have a product > > line which does not include networking? Is not Broadcom a network SoC > > vendor? > > But even with that, there is no reason why we could not decouple the > PHYLIB MDIO framework from PHYLIB and make it available as a more > standalone subsystem which can be utilized when you have a mix of MDIO > devices like here. [adding Kishon Vijay Abraham] We should also consider how this fits in with drivers/phy/, which is the generic framework for all PHY devices that are not for ethernet. The most straightforward way that you mention would be to allow generic PHY devices to be probed on an MIO bus or mux. This should just work using mdio_module_driver(), as Andrew already explained. A more complex problem would be having a PHY driver for a device that can be either an ethernet phy or some other phy. With today's frameworks that would require two separate drivers, one in drivers/phy and one in drivers/net/phy/. If that turns out to be a common problem, we might want to come up with a way to nest one on top of the other, or merge two two device structures (struct phy_device and struct phy). > I am not clear on how common a shared MDIO bus is on other SoCs, but the > other Broadcom SoCs I am familiar with have dedicated MDIO buses > instances per type of PHY (PCIe, BUSB, Ethernet), thus making the split > a ton easier. I think most commonly, the other PHYs are not on MDIO at all, but are integrated inside of the SoC as an MMIO based device. Arnd
> A more complex problem would be having a PHY driver for a device > that can be either an ethernet phy or some other phy. I doubt that ever happens. You can have up to 32 different devices on an MDIO bus. Since an Ethernet PHY and a "some other sort of PHY" are completely different things, why would a hardware engineer place them on the same address? It is like saying your ATA controller and VGA controller share the same slot on the PCI bus... Andrew
On Tuesday 26 April 2016 20:23:35 Andrew Lunn wrote: > > A more complex problem would be having a PHY driver for a device > > that can be either an ethernet phy or some other phy. > > I doubt that ever happens. You can have up to 32 different devices on > an MDIO bus. Since an Ethernet PHY and a "some other sort of PHY" are > completely different things, why would a hardware engineer place them > on the same address? It is like saying your ATA controller and VGA > controller share the same slot on the PCI bus... To clarify: what I meant is a device that is designed as a PHY for similar hardware (e.g. SATA, USB3 and PCIe) and that has a common register set and a single driver, but that driver can operate in multiple modes. You typically have multiple instances of such hardware, with each instance linked to exactly one host device, but one driver for all of them. See Documentation/devicetree/bindings/phy/apm-xgene-phy.txt and drivers/phy/phy-xgene.c for one such example. Arnd
On Tue, Apr 26, 2016 at 09:24:34PM +0200, Arnd Bergmann wrote: > On Tuesday 26 April 2016 20:23:35 Andrew Lunn wrote: > > > A more complex problem would be having a PHY driver for a device > > > that can be either an ethernet phy or some other phy. > > > > I doubt that ever happens. You can have up to 32 different devices on > > an MDIO bus. Since an Ethernet PHY and a "some other sort of PHY" are > > completely different things, why would a hardware engineer place them > > on the same address? It is like saying your ATA controller and VGA > > controller share the same slot on the PCI bus... > > To clarify: what I meant is a device that is designed as a PHY for > similar hardware (e.g. SATA, USB3 and PCIe) and that has a common > register set and a single driver, but that driver can operate > in multiple modes. You typically have multiple instances of > such hardware, with each instance linked to exactly one host > device, but one driver for all of them. > > See Documentation/devicetree/bindings/phy/apm-xgene-phy.txt > and drivers/phy/phy-xgene.c for one such example. Interesting. Also, that this lists SGMII. I assume this is a phy in the MAC in order to talk to the Ethernet PHY. I still don't see it being a big problem if a phy driver implements an Ethernet PHY. It just needs to call phy_device_create() and phy_device_register(). Andrew
On Wed, Apr 27, 2016 at 1:11 AM, Andrew Lunn <andrew@lunn.ch> wrote: > On Tue, Apr 26, 2016 at 09:24:34PM +0200, Arnd Bergmann wrote: >> On Tuesday 26 April 2016 20:23:35 Andrew Lunn wrote: >> > > A more complex problem would be having a PHY driver for a device >> > > that can be either an ethernet phy or some other phy. >> > >> > I doubt that ever happens. You can have up to 32 different devices on >> > an MDIO bus. Since an Ethernet PHY and a "some other sort of PHY" are >> > completely different things, why would a hardware engineer place them >> > on the same address? It is like saying your ATA controller and VGA >> > controller share the same slot on the PCI bus... >> >> To clarify: what I meant is a device that is designed as a PHY for >> similar hardware (e.g. SATA, USB3 and PCIe) and that has a common >> register set and a single driver, but that driver can operate >> in multiple modes. You typically have multiple instances of >> such hardware, with each instance linked to exactly one host >> device, but one driver for all of them. >> >> See Documentation/devicetree/bindings/phy/apm-xgene-phy.txt >> and drivers/phy/phy-xgene.c for one such example. > > Interesting. Also, that this lists SGMII. I assume this is a phy in > the MAC in order to talk to the Ethernet PHY. > > I still don't see it being a big problem if a phy driver implements an > Ethernet PHY. It just needs to call phy_device_create() and > phy_device_register(). > > Andrew It is really interesting to see the evolution of MDIO bus: 1. Traditionally, MDIO controller used to be part of each ethernet controller itself so each ethernet controller used to have it's own 2 wire MDIO bus 2. Next, we saw SoC with multiple ethernet controllers sharing same MDIO bus. In other words, we saw multiple MDIO bus being muxed over single MDIO bus with additional bus select lines (I think this is when drivers/net/phy/mdio-mux.c APIs were implemented but at this point all PHYs on muxed MDIO bus were ethernet PHYs). 3. Then, we saw SoC with ethernet switch devices also accessible over shared MDIO bus (or Muxed MDIO bus) along with ethernet PHYs (I guess this is why we have drivers/net/phy/mdio_device.c which adds "mdio_device" for non-ethernet-PHY devices on MDIO bus). 4. Now, we have SoC with SATA PHYs, PCIe PHYs, USB PHYs, and Ethernet PHYs all accessible over same shared MDIO bus (or Muxed MDIO bus). The SATA PHYs and PCIe PHYs are registered to "Generic PHY framework". For USB PHYs, we can either register to "Generic PHY framework" or "USB PHY framework". For Ethernet PHYs, we register MDIO bus instance to "Ethernet MDIO framework". The devices on ARM64 SoC has to be within first 4GB and RAM has to start from first 4GB to be ARM compliant because ARM64 CPUs have 32bit mode and all devices and RAM should be available to 32bit mode with MMU disabled. This means that we only have 4GB to fit all devices registers and some portion of RAM. Some of these non-Ethernet PHYs have tons of registers so as number of PHYs increase in a SoC they will eat-up lot of first 4GB address space. Using same MDIO bus for all types of PHYs (SATA, PCIe, USB, and Ethernet) is actually a good approach because it actually saves lot of first 4GB address space. In future, more devices will be moved to a shared MDIO bus which are less frequently accessed. For Broadcom iProc SoCs, the design choice has already been made to use shared MDIO bus for all PHYs. In fact, Broadcom iProc NS2 SoC already has a shared MDIO bus for SATA PHYs, USB PHYs, PCIe PHYs, and Ethernet PHYs and more Broadcom iProc SoCs are on their way. Of course, there are few exceptions in iProc SoCs such as SATA PHYs where we also have memory mapped registers to access PHYs but other PHYs don't have such memory mapped registers. Clearly from above, the traditional 2 wire MDIO bus is now a shared MDIO bus with 2-wire plus additional select lines. Also, now we have SoCs (such as Broadcom iProc SoCs) which has such shared MDIO bus and I think in-future we will have SoCs with a shared MDIO bus for variety of devices. For long term, we really need a clean solution to fit shared MDIO based PHY drivers in Linux kernel. Also, shared MDIO based PHY drivers should not be dependent on any particular IO subsystem (such as Linux Ethernet) because there are lot of use-cases where people want strip down kernel image by not-compiling IO subsystems which are not required. IMHO, we have several options in front of us: 1. Use some light-weight framework (such as shared_mdio.c implemented by this patchset) under drivers/bus 2. Extend "Generic PHY framework" to allow something like shared MDIO bus (as-per Arnd's suggestion) 3. Move-out "MDIO-mux APIs" from drivers/net/phy to something like drivers/mdio-mux and make it independent of "Linux Ethernet subsystem". (... may be more options ...) Regards, Anup
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index d4a3a31..1b51b1a 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -107,6 +107,17 @@ config OMAP_OCP2SCP OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via OCP2SCP. +config SHARED_MDIO + bool "Shared MDIO bus" + depends on OF + + help + Shared MDIO bus implementation for SoCs having common MDIO bus + for different IO subsystems. All masters should be registered to + this bus via a platform driver using shared framework API. + Respective drivers would be called matching a compatible string + provided in master node. + config SIMPLE_PM_BUS bool "Simple Power-Managed Bus Driver" depends on OF && PM diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index ccff007..2b6d6e9 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o +obj-$(CONFIG_SHARED_MDIO) += shared_mdio.o diff --git a/drivers/bus/shared_mdio.c b/drivers/bus/shared_mdio.c new file mode 100644 index 0000000..909af73 --- /dev/null +++ b/drivers/bus/shared_mdio.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Shared MDIO Bus + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/errno.h> +#include <linux/idr.h> +#include <linux/of_device.h> +#include <linux/stddef.h> +#include <linux/module.h> +#include <linux/shared_mdio.h> +#include <linux/slab.h> + +static DEFINE_IDA(shared_mdio_ida); + +static void shared_mdio_release_master(struct device *dev) +{ + struct shared_mdio_master *master = to_shared_mdio_master(dev); + + ida_simple_remove(&shared_mdio_ida, master->dev_num); + kfree(master); +} + +struct shared_mdio_master *shared_mdio_alloc_master(struct device *parent, + struct device_node *node) +{ + int ret = 0; + struct shared_mdio_master *master; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) { + ret = -ENOMEM; + goto fail1; + } + + master->dev.parent = parent; + master->dev.bus = &shared_mdio_bus; + master->dev.release = shared_mdio_release_master; + + device_initialize(&master->dev); + + ret = ida_simple_get(&shared_mdio_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto fail2; + master->dev_num = ret; + + dev_set_name(&master->dev, "shared-mdio-master%d", master->dev_num); + + of_node_get(node); + master->dev.of_node = node; + + return master; + +fail2: + kfree(master); +fail1: + return ERR_PTR(ret); +} +EXPORT_SYMBOL(shared_mdio_alloc_master); + +int shared_mdio_add_master(struct shared_mdio_master *master) +{ + if (!master) + return -EINVAL; + + return device_add(&master->dev); +} +EXPORT_SYMBOL(shared_mdio_add_master); + +static int shared_mdio_driver_probe(struct device *dev) +{ + int rc; + struct shared_mdio_master *master = to_shared_mdio_master(dev); + struct shared_mdio_driver *drv = to_shared_mdio_driver(dev->driver); + + if (!drv->probe) + return -ENODEV; + + rc = drv->probe(master); + if (rc) + return rc; + + return 0; +} + +static int shared_mdio_driver_remove(struct device *dev) +{ + struct shared_mdio_driver *drv = to_shared_mdio_driver(dev->driver); + + if (drv->remove) + return drv->remove(to_shared_mdio_master(dev)); + + return 0; +} + +static void shared_mdio_driver_shutdown(struct device *dev) +{ + struct shared_mdio_driver *drv = to_shared_mdio_driver(dev->driver); + + if (drv->shutdown) + drv->shutdown(to_shared_mdio_master(dev)); +} + +int __shared_mdio_register_driver(struct shared_mdio_driver *drv, + struct module *owner) +{ + int rc; + + drv->driver.bus = &shared_mdio_bus; + drv->driver.owner = owner; + drv->driver.probe = shared_mdio_driver_probe; + drv->driver.remove = shared_mdio_driver_remove; + drv->driver.shutdown = shared_mdio_driver_shutdown; + + rc = driver_register(&drv->driver); + if (rc) { + pr_err("driver_register() failed for %s, error: %d\n", + drv->driver.name, rc); + return rc; + } + + return 0; +} +EXPORT_SYMBOL(__shared_mdio_register_driver); + +static int shared_mdio_bus_match(struct device *dev, struct device_driver *drv) +{ + /* Attempt an OF style match */ + if (of_driver_match_device(dev, drv)) + return 1; + + return 0; +} + +struct bus_type shared_mdio_bus = { + .name = "shared_mdio", + .match = shared_mdio_bus_match, +}; +EXPORT_SYMBOL(shared_mdio_bus); + +static int __init shared_mdio_init(void) +{ + int rc; + + rc = bus_register(&shared_mdio_bus); + if (rc) { + pr_err("Failed to register shared_mdio bus, error: %d\n", rc); + return rc; + } + + return 0; +} + +static void __exit shared_mdio_exit(void) +{ + bus_unregister(&shared_mdio_bus); + + ida_destroy(&shared_mdio_ida); +} + +subsys_initcall(shared_mdio_init); +module_exit(shared_mdio_exit); + +MODULE_DESCRIPTION("Shared MDIO Bus"); +MODULE_AUTHOR("Anup Patel <anup.patel@broadcom.com>"); +MODULE_AUTHOR("Pramod Kumar <pramod.kumar@broadcom.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/shared_mdio.h b/include/linux/shared_mdio.h new file mode 100644 index 0000000..db6e442 --- /dev/null +++ b/include/linux/shared_mdio.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SHARED_MDIO_H__ +#define __SHARED_MDIO_H__ + +#include <linux/device.h> +#include <linux/types.h> + +extern struct bus_type shared_mdio_bus; + +/* + * This structure represets the mdio master that control the MDIO bus + * to access the phy attached on it. + * @dev Underlying device for mdio master + * @dev_num Unique device number of mdio master + * @mdio_write Write callback of mdio master + * @mdio_read Read callback for mdio master + */ +struct shared_mdio_master { + struct device dev; + int dev_num; + void *priv; + + int (*mdio_write)(struct shared_mdio_master *master, + u16 phyid, u16 reg, u16 data); + int (*mdio_read)(struct shared_mdio_master *master, u16 phyid, u16 reg); +}; +#define to_shared_mdio_master(d) \ + container_of(d, struct shared_mdio_master, dev) + +struct shared_mdio_driver { + int (*probe)(struct shared_mdio_master *master); + int (*remove)(struct shared_mdio_master *master); + void (*shutdown)(struct shared_mdio_master *master); + + struct device_driver driver; +}; +#define to_shared_mdio_driver(d) \ + container_of(d, struct shared_mdio_driver, driver) + +struct shared_mdio_master *shared_mdio_alloc_master(struct device *parent, + struct device_node *node); + +int shared_mdio_add_master(struct shared_mdio_master *master); + +static inline void shared_mdio_remove_master(struct shared_mdio_master *master) +{ + device_unregister(&master->dev); +} + +int __shared_mdio_register_driver(struct shared_mdio_driver *drv, + struct module *owner); + +/* use a define to avoid include chaining to get THIS_MODULE & friends */ +#define shared_mdio_register_driver(drv) \ + __shared_mdio_register_driver(drv, THIS_MODULE) + +static inline void shared_mdio_unregister_driver( + struct shared_mdio_driver *drv) +{ + driver_unregister(&drv->driver); +} + +/** + * module_shared_mdio_driver() - Helper macro for registering a shared_mdio + * driver + * @__shared_mdio_driver: shared_mdio_driver struct + * + * Helper macro for shared mdio drivers which do not do anything special in + * module init/exit. This eliminates a lot of boilerplate. Each module + * may only use this macro once, and calling it replaces module_init() + * and module_exit(). + */ +#define module_shared_mdio_driver(__shared_mdio_driver) \ + module_driver(__shared_mdio_driver, shared_mdio_register_driver, \ + shared_mdio_unregister_driver) + + +static inline int shared_mdio_write(struct shared_mdio_master *master, + u16 phy, u16 reg, u16 data) +{ + if (master->mdio_write) + return master->mdio_write(master, phy, reg, data); + + return -ENOTSUPP; +} + +static inline int shared_mdio_read(struct shared_mdio_master *master, + u16 phy_id, u16 reg) +{ + if (master->mdio_read) + return master->mdio_read(master, phy_id, reg); + + return -ENOTSUPP; +} + +/* + * Use the following functions to manipulate shared_mdio's per-master + * driver-specific data. + */ +static inline void *shared_mdio_get_drvdata(struct shared_mdio_master *master) +{ + return dev_get_drvdata(&master->dev); +} + +static inline void shared_mdio_set_drvdata(struct shared_mdio_master *master, + void *data) +{ + dev_set_drvdata(&master->dev, data); +} + +#endif